Search code examples
javagremlinjanusgraphgremlin-server

How do I serialize a Tinkerpop Vertex/Edge list for a REST service?


Using an embedded JanusGraph, I'm creating a REST service that returns lists of Vertex and Edge, but I'd like to return JSON, like the gremlin server does.

I've attempted to use the MessageSerializer, but I'm not able to coerce the binary format into JSON.

@RequestMapping("/search")
public String search(@RequestBody SearchOptions options) throws JsonProcessingException, SerializationException {
    List<Object> data = new ArrayList<>();
    Builder responseBuilder = getResponseBuilder(data);

    List<Vertex> nodes;
    List<Object> edges;

    if (Strings.isNullOrEmpty(options.getText()) || Strings.isNullOrEmpty(options.getField())) {
        nodes = g.V().limit(options.getLimit()).toList();
        edges = g.V().limit(options.getLimit()).aggregate("node").outE().as("edge").inV().where(P.within("node"))
                .select("edge").toList();
    } else {
        nodes = g.V().has(options.getField(), options.getText()).toList();
        edges = g.V().has(options.getField(), options.getText()).aggregate("node").outE().as("edge").inV()
                .where(P.within("node")).select("edge").toList();
    }
    data.add(nodes);
    data.add(edges);

    ResponseMessage response = responseBuilder.create();
    ByteBufAllocator allocator = new PooledByteBufAllocator();
    ByteBuf byteBuffer = ser.serializeResponseAsBinary(response, allocator);
    byte[] bytes = byteBuffer.array();
    String str = new String(bytes);
    return str;
}   

@Bean
public MessageSerializer messageSerializer() {
    GryoMapper.Builder kryo = GryoMapper.build().addRegistry(JanusGraphIoRegistry.getInstance());
    MessageSerializer serializer = new GryoMessageSerializerV1d0(kryo);
    return serializer;
}

I tried using a jackson object mapper, but get this error -

[http-nio-8080-exec-10] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException: (was java.lang.IllegalStateException) (through reference chain: org.apache.tinkerpop.gremlin.driver.message.ResponseMessage["result"]->org.apache.tinkerpop.gremlin.driver.message.ResponseResult["data"]->java.util.ArrayList[0]->java.util.ArrayList[0]->org.janusgraph.graphdb.relations.RelationIdentifier["inVertexId"])
    at org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388)   

I define the ObjectMapper like this -

ObjectMapper mapper = GraphSONMapper.build().version(GraphSONVersion.V2_0).create().createMapper();

This was the right way to create the ObjectMapper.

private ObjectMapper mapper = GraphSONMapper.build()
        .addRegistry(JanusGraphIoRegistry.getInstance())
        .version(GraphSONVersion.V2_0).create().createMapper();

Solution

  • I think you should just create a Jackson ObjectMapper from a GraphSONMapper instance with whatever configurations you need and then pass your results into that:

        gremlin> mapper = GraphSONMapper.build().version(GraphSONVersion.V2_0).create().createMapper()
        ==>org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper@4ca8dbfa
        gremlin> mapper.class
        ==>class org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper
        gremlin> vertices = g.V().toList()
        ==>v[1]
        ==>v[2]
        ==>v[3]
        ==>v[4]
        ==>v[5]
        ==>v[6]
        gremlin> mapper.writeValueAsString(vertices)
        ==>[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":2},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":2},"value":"vadas","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":3},"value":{"@type":"g:Int32","@value":27},"label":"age"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":3},"label":"software","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":4},"value":"lop","label":"name"}}],"lang":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":5},"value":"java","label":"lang"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":4},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":6},"value":"josh","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":7},"value":{"@type":"g:Int32","@value":32},"label":"age"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":5},"label":"software","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":8},"value":"ripple","label":"name"}}],"lang":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":9},"value":"java","label":"lang"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":6},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":10},"value":"peter","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":11},"value":{"@type":"g:Int32","@value":35},"label":"age"}}]}}}]