通过JNI和RESTful API实现的Graph Planner接口¶
GraphPlanner 是 GOpt 查询优化与物理计划生成的主要入口点。最初,它与前端服务紧密集成,负责优化通过 Bolt 协议接收的 Cypher 查询,并为不同后端引擎生成执行计划。
为了增强其灵活性和集成便利性,GraphPlanner现已作为独立模块提供,无需依赖其他前端模块。它同时支持JNI和RESTful API接口,能够轻量级且直接地集成到各类系统中。无论您是在开发原生应用还是基于Web的服务,GraphPlanner都能无缝融入您的架构,为广泛的使用场景提供高效的查询优化和物理计划生成能力。
JNI API¶
接口概览¶
我们为JNI接口提供了一个C++封装实现GraphPlannerWrapper。以下是该c++类提供的逻辑接口的简要说明。
构造函数:
/**
* @brief Constructs a new GraphPlannerWrapper object
* @param java_path Java class path
* @param jna_path JNA library path
* @param graph_schema_yaml Path to the graph schema file in YAML format (optional)
* @param graph_statistic_json Path to the graph statistics file in JSON format (optional)
*/
GraphPlannerWrapper(const std::string &java_path,
const std::string &jna_path,
const std::string &graph_schema_yaml = "",
const std::string &graph_statistic_json = "");
方法:
/**
* @brief Compile a cypher query to a physical plan by JNI invocation.
* @param compiler_config_path The path of compiler config file.
* @param cypher_query_string The cypher query string.
* @param graph_schema_yaml Content of the graph schema in YAML format
* @param graph_statistic_json Content of the graph statistics in JSON format
* @return The physical plan in bytes and result schema in yaml.
*/
Plan GraphPlannerWrapper::CompilePlan(const std::string &compiler_config_path,
const std::string &cypher_query_string,
const std::string &graph_schema_yaml,
const std::string &graph_statistic_json)
快速入门¶
按照以下步骤开始使用Graph Planner接口进行C++调用。
步骤1:构建项目¶
导航到项目目录并使用Maven构建包:
cd interactive_engine
mvn clean package -DskipTests -Pgraph-planner-jni
步骤2:定位并提取软件包¶
构建完成后,一个名为graph-planner-jni.tar.gz的压缩包将出现在assembly/target目录中。解压该压缩包的内容:
cd assembly/target
tar xvzf graph-planner-jni.tar.gz
cd graph-planner-jni
步骤3:运行示例二进制文件¶
为了演示JNI接口的用法,提供了一个示例二进制文件test_graph_planner。使用以下命令执行它:
# bin/test_graph_planner <java class path> <jna lib path> <graph schema path> <graph statistics path> <query> <config path>
bin/test_graph_planner libs native ./conf/graph.yaml ./conf/modern_statistics.json "MATCH (n) RETURN n, COUNT(n);" ./conf/gs_interactive_hiactor.yaml
输出包含物理计划(以字节格式)和结果模式(以YAML格式)。该物理计划遵循protobuf中定义的规范。
以下是一个结果模式的示例:
schema:
name: default
description: default desc
mode: READ
extension: .so
library: libdefault.so
params: []
returns:
- name: n
type: {primitive_type: DT_UNKNOWN}
- name: $f1
type: {primitive_type: DT_SIGNED_INT64}
type: UNKNOWN
query: MATCH (n) RETURN n, COUNT(n);
returns字段定义了后端引擎返回数据的结构。returns字段中的每个嵌套条目包含三个组成部分:
列名,用于指定结果列的名称;
条目的序号位置,决定了列ID;
类型,用于对列强制执行数据类型约束。
Restful API¶
我们提供另一种方法将接口作为RESTful API公开。按照以下步骤通过REST访问接口。
快速入门¶
步骤1:构建项目¶
要构建项目,请运行以下命令:
cd interactive_engine
# Use '-Dskip.native=true' to skip compiling C++ native code
mvn clean package -DskipTests -Pgraph-planner-jni -Dskip.native=true
步骤2:定位并提取软件包¶
构建完成后,一个名为graph-planner-jni.tar.gz的压缩包将出现在assembly/target目录中。按以下方式解压内容:
cd assembly/target
tar xvzf graph-planner-jni.tar.gz
cd graph-planner-jni
步骤3:启动Graph Planner RESTful服务¶
要启动服务,请运行以下命令:
java -cp ".:./libs/*" com.alibaba.graphscope.sdk.restful.GraphPlannerService --spring.config.location=./conf/application.yaml
步骤4:通过Curl¶访问RESTful API
要向RESTful API发送请求,请使用以下curl命令:
curl -X POST http://localhost:8080/api/compilePlan \
-H "Content-Type: application/json" \
-d "{
\"configPath\": \"$configPath\",
\"query\": \"$query\",
\"schemaYaml\": \"$schemaYaml\",
\"statsJson\": \"$statsJson\"
}"
将 $configPath、$query、$schemaYaml 和 $statsJson 替换为适当的值。
响应将以JSON格式返回,类似如下:
{
"graphPlan": {
"physicalBytes": "",
"resultSchemaYaml": ""
}
}
响应包含两个字段:
physicalBytes: 一个Base64编码的字符串,表示物理计划的字节数据。
resultSchemaYaml: 表示YAML模式的字符串。
您可以将这些值解码为所需的结构。
步骤4:通过Java SDK访问RESTful API¶
另外,如果您是Java端用户,我们提供了一个Java SDK示例,指导您如何访问RESTful API并解码响应:
public static void main(String[] args) throws Exception {
if (args.length < 4) {
System.out.println("Usage: <configPath> <query> <schemaPath> <statsPath>");
System.exit(1);
}
// set request body in json format
String jsonPayLoad = createParameters(args[0], args[1], args[2], args[3]).toString();
HttpClient client = HttpClient.newBuilder().build();
// create http request, set header and body content
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8080/api/compilePlan"))
.setHeader("Content-Type", "application/json")
.POST(
HttpRequest.BodyPublishers.ofString(
jsonPayLoad, StandardCharsets.UTF_8))
.build();
// send request and get response
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
// parse response body as json
JsonNode planNode = (new ObjectMapper()).readTree(body).get("graphPlan");
// print result
System.out.println(getPhysicalPlan(planNode));
System.out.println(getResultSchemaYaml(planNode));
}
private static JsonNode createParameters(
String configPath, String query, String schemaPath, String statsPath) throws Exception {
Map<String, String> params =
ImmutableMap.of(
"configPath",
configPath,
"query",
query,
"schemaYaml",
FileUtils.readFileToString(new File(schemaPath), StandardCharsets.UTF_8),
"statsJson",
FileUtils.readFileToString(new File(statsPath), StandardCharsets.UTF_8));
return (new ObjectMapper()).valueToTree(params);
}
// get base64 string from json, convert it to physical bytes , then parse it to PhysicalPlan
private static GraphAlgebraPhysical.PhysicalPlan getPhysicalPlan(JsonNode planNode)
throws Exception {
String base64Str = planNode.get("physicalBytes").asText();
byte[] bytes = java.util.Base64.getDecoder().decode(base64Str);
return GraphAlgebraPhysical.PhysicalPlan.parseFrom(bytes);
}
// get result schema yaml from json
private static String getResultSchemaYaml(JsonNode planNode) {
return planNode.get("resultSchemaYaml").asText();
}
使用以下命令运行Java SDK示例:
java -cp ".:./libs/*" com.alibaba.graphscope.sdk.examples.TestGraphPlanner ./conf/gs_interactive_hiactor.yaml "Match (n) Return n;" ./conf/graph.yaml ./conf/modern_statistics.json