如何使用 Gremlin 和 Java 查询远程 Apache Tinkerpop 图形数据库?
How can I query a remote Apache Tinkerpop Graph Database with Gremlin and Java?
我一直无法找到使用 Gremlin 和 Java 连接并查询远程 Apache Tinkerpop 图形数据库的综合示例。而且我不能完全让它工作。有没有做过类似的可以给点建议吗?
我已经在 Graph-DB 模式下设置了一个 Azure Cosmos 数据库,它需要 Gremlin 查询来修改和访问它的数据。我有数据库主机名、端口、用户名和密码,并且我能够执行查询,但前提是我传递了一个丑陋的大查询字符串。我希望能够利用 org.apache.tinkerpop.gremlin.structure.Graph 遍历方法,但我不能完全让它工作。
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//More imports...
@Service
public class SearchService {
private final static Logger log = LoggerFactory.getLogger(SearchService.class);
@Autowired
private GraphDbConnection graphDbConnection;
@Autowired
private Graph graph;
public Object workingQuery() {
try {
String query = "g.V('1234').outE('related').inV().both().as('v').project('vertex').by(select('v')).by(bothE().fold())";
log.info("Submitting this Gremlin query: {}", query);
ResultSet results = graphDbConnection.executeQuery(query);
CompletableFuture<List<Result>> completableFutureResults = results.all();
List<Result> resultList = completableFutureResults.get();
Result result = resultList.get(0);
log.info("Query result: {}", result.toString());
return result.toString();
} catch (Exception e) {
log.error("Error fetching data.", e);
}
return null;
}
public Object failingQuery() {
return graph.traversal().V(1234).outE("related").inV()
.both().as("v").project("vertex").by("v").bothE().fold()
.next();
/* I get an Exception:
"org.apache.tinkerpop.gremlin.process.remote.RemoteConnectionException:
java.lang.RuntimeException: java.lang.RuntimeException:
java.util.concurrent.TimeoutException: Timed out while waiting for an
available host - check the client configuration and connectivity to the
server if this message persists" */
}
}
这是我的配置class:
import java.util.HashMap;
import java.util.Map;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GraphDbConfig {
private final static Logger log = LoggerFactory.getLogger(GraphDbConfig.class);
@Value("${item.graph.hostName}")
private String hostName;
@Value("${item.graph.port}")
private int port;
@Value("${item.graph.username}")
private String username;
@Value("${item.graph.password}")
private String password;
@Value("${item.graph.enableSsl}")
private boolean enableSsl;
@Bean
public Graph graph() {
Map<String, String> graphConfig = new HashMap<>();
graphConfig.put("gremlin.graph",
"org.apache.tinkerpop.gremlin.process.remote.RemoteGraph");
graphConfig.put("gremlin.remoteGraph.remoteConnectionClass",
"org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection");
Graph g = GraphFactory.open(graphConfig);
g.traversal().withRemote(DriverRemoteConnection.using(cluster()));
return g;
}
@Bean
public Cluster cluster() {
Cluster cluster = null;
try {
MessageSerializer serializer = new GraphSONMessageSerializerGremlinV2d0();
Cluster.Builder clusterBuilder = Cluster.build().addContactPoint(hostName)
.serializer(serializer)
.port(port).enableSsl(enableSsl)
.credentials(username, password);
cluster = clusterBuilder.create();
} catch (Exception e) {
log.error("Error in connecting to host address.", e);
}
return cluster;
}
}
我现在必须定义这个连接组件才能向数据库发送查询:
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class GraphDbConnection {
private final static Logger log = LoggerFactory.getLogger(GraphDbConnection.class);
@Autowired
private Cluster cluster;
public ResultSet executeQuery(String query) {
Client client = connect();
ResultSet results = client.submit(query);
closeConnection(client);
return results;
}
private Client connect() {
Client client = null;
try {
client = cluster.connect();
} catch (Exception e) {
log.error("Error in connecting to host address.", e);
}
return client;
}
private void closeConnection(Client client) {
client.close();
}
}
您还不能将远程 API 与 CosmosDB 结合使用。它还不支持 Gremlin 字节码。
https://github.com/Azure/azure-documentdb-dotnet/issues/439
在那之前你必须继续使用字符串,但是......因为你正在使用 Java 你可以尝试一些未公开的功能:GroovyTranslator
gremlin> g = EmptyGraph.instance().traversal()
==>graphtraversalsource[emptygraph[empty], standard]
gremlin> translator = GroovyTranslator.of('g')
==>translator[g:gremlin-groovy]
gremlin> translator.translate(g.V().out('knows').has('person','name','marko').asAdmin().getBytecode())
==>g.V().out("knows").has("person","name","marko")
如您所见,它需要 Gremlin Bytecode
并将其转换为您可以提交给 CosmosDB 的 Gremlin String
。稍后,当 CosmosDB 支持字节码时,您可以删除 GroovyTranslator
并从 EmptyGraph
构造更改 GraphTraversalSource
并且一切都应该开始工作。为了使这真正无缝,您可以执行额外的步骤并编写一个 TraversalStrategy
来执行类似于 TinkerPop 的 RemoteStrategy
的操作。而不是像该策略那样提交 Bytecode
,您只需使用 GroovyTranslator
并提交 Gremlin 的字符串。当 CosmosDB 支持 Bytecode
时,这种方法将使切换变得更加容易,因为那时您所要做的就是删除自定义 TraversalStrategy
并以标准方式重新配置远程 GraphTraversalSource
。
我一直无法找到使用 Gremlin 和 Java 连接并查询远程 Apache Tinkerpop 图形数据库的综合示例。而且我不能完全让它工作。有没有做过类似的可以给点建议吗?
我已经在 Graph-DB 模式下设置了一个 Azure Cosmos 数据库,它需要 Gremlin 查询来修改和访问它的数据。我有数据库主机名、端口、用户名和密码,并且我能够执行查询,但前提是我传递了一个丑陋的大查询字符串。我希望能够利用 org.apache.tinkerpop.gremlin.structure.Graph 遍历方法,但我不能完全让它工作。
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//More imports...
@Service
public class SearchService {
private final static Logger log = LoggerFactory.getLogger(SearchService.class);
@Autowired
private GraphDbConnection graphDbConnection;
@Autowired
private Graph graph;
public Object workingQuery() {
try {
String query = "g.V('1234').outE('related').inV().both().as('v').project('vertex').by(select('v')).by(bothE().fold())";
log.info("Submitting this Gremlin query: {}", query);
ResultSet results = graphDbConnection.executeQuery(query);
CompletableFuture<List<Result>> completableFutureResults = results.all();
List<Result> resultList = completableFutureResults.get();
Result result = resultList.get(0);
log.info("Query result: {}", result.toString());
return result.toString();
} catch (Exception e) {
log.error("Error fetching data.", e);
}
return null;
}
public Object failingQuery() {
return graph.traversal().V(1234).outE("related").inV()
.both().as("v").project("vertex").by("v").bothE().fold()
.next();
/* I get an Exception:
"org.apache.tinkerpop.gremlin.process.remote.RemoteConnectionException:
java.lang.RuntimeException: java.lang.RuntimeException:
java.util.concurrent.TimeoutException: Timed out while waiting for an
available host - check the client configuration and connectivity to the
server if this message persists" */
}
}
这是我的配置class:
import java.util.HashMap;
import java.util.Map;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GraphDbConfig {
private final static Logger log = LoggerFactory.getLogger(GraphDbConfig.class);
@Value("${item.graph.hostName}")
private String hostName;
@Value("${item.graph.port}")
private int port;
@Value("${item.graph.username}")
private String username;
@Value("${item.graph.password}")
private String password;
@Value("${item.graph.enableSsl}")
private boolean enableSsl;
@Bean
public Graph graph() {
Map<String, String> graphConfig = new HashMap<>();
graphConfig.put("gremlin.graph",
"org.apache.tinkerpop.gremlin.process.remote.RemoteGraph");
graphConfig.put("gremlin.remoteGraph.remoteConnectionClass",
"org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection");
Graph g = GraphFactory.open(graphConfig);
g.traversal().withRemote(DriverRemoteConnection.using(cluster()));
return g;
}
@Bean
public Cluster cluster() {
Cluster cluster = null;
try {
MessageSerializer serializer = new GraphSONMessageSerializerGremlinV2d0();
Cluster.Builder clusterBuilder = Cluster.build().addContactPoint(hostName)
.serializer(serializer)
.port(port).enableSsl(enableSsl)
.credentials(username, password);
cluster = clusterBuilder.create();
} catch (Exception e) {
log.error("Error in connecting to host address.", e);
}
return cluster;
}
}
我现在必须定义这个连接组件才能向数据库发送查询:
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class GraphDbConnection {
private final static Logger log = LoggerFactory.getLogger(GraphDbConnection.class);
@Autowired
private Cluster cluster;
public ResultSet executeQuery(String query) {
Client client = connect();
ResultSet results = client.submit(query);
closeConnection(client);
return results;
}
private Client connect() {
Client client = null;
try {
client = cluster.connect();
} catch (Exception e) {
log.error("Error in connecting to host address.", e);
}
return client;
}
private void closeConnection(Client client) {
client.close();
}
}
您还不能将远程 API 与 CosmosDB 结合使用。它还不支持 Gremlin 字节码。
https://github.com/Azure/azure-documentdb-dotnet/issues/439
在那之前你必须继续使用字符串,但是......因为你正在使用 Java 你可以尝试一些未公开的功能:GroovyTranslator
gremlin> g = EmptyGraph.instance().traversal()
==>graphtraversalsource[emptygraph[empty], standard]
gremlin> translator = GroovyTranslator.of('g')
==>translator[g:gremlin-groovy]
gremlin> translator.translate(g.V().out('knows').has('person','name','marko').asAdmin().getBytecode())
==>g.V().out("knows").has("person","name","marko")
如您所见,它需要 Gremlin Bytecode
并将其转换为您可以提交给 CosmosDB 的 Gremlin String
。稍后,当 CosmosDB 支持字节码时,您可以删除 GroovyTranslator
并从 EmptyGraph
构造更改 GraphTraversalSource
并且一切都应该开始工作。为了使这真正无缝,您可以执行额外的步骤并编写一个 TraversalStrategy
来执行类似于 TinkerPop 的 RemoteStrategy
的操作。而不是像该策略那样提交 Bytecode
,您只需使用 GroovyTranslator
并提交 Gremlin 的字符串。当 CosmosDB 支持 Bytecode
时,这种方法将使切换变得更加容易,因为那时您所要做的就是删除自定义 TraversalStrategy
并以标准方式重新配置远程 GraphTraversalSource
。