如何使用 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

https://feedback.azure.com/forums/263030-azure-cosmos-db/suggestions/33632779-support-gremlin-bytecode-to-enable-the-fluent-api

在那之前你必须继续使用字符串,但是......因为你正在使用 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