Gremlin Plugin

gremlin-logo.png

Gremlin is a Groovy based Graph Traversal Language. It provides a very expressive way of explicitly scripting traversals through a Neo4j graph.

The Neo4j Gremlin Plugin provides an endpoint to send Gremlin scripts to the Neo4j Server. The scripts are executed on the server database and the results are returned as Neo4j Node and Relationship representations. This keeps the types throughout the REST API consistent. The results are quite verbose when returning Neo4j Node, Relationship or Graph representations. On the other hand, just return properties like in the [rest-api-send-a-gremlin-script---json-encoded-with-table-results] example for responses tailored to specific needs.

Warning
The Gremlin plugin lets you execute arbitrary Groovy code under the hood. In hosted and open environments, this can constitute a security risk. In these case, consider using declarative approaches like [cypher-query-lang] or write your own server side plugin executing the interesting Gremlin or Java routines, see [server-plugins] or secure your server, see [security-server].
Tip
When returning results from pipes like g.v(0).in(), make sure to iterate through the results in order not to return the pipe object but its content, like g.v(0).in().iterate(). For more caveats, see Gremlin Troubleshooting

Send a Gremlin Script - URL encoded

Scripts can be sent as URL-encoded In this example, the graph has been autoindexed by Neo4j, so we can look up the name property on nodes.

Raw script source

g.getIndex('node_auto_index', Vertex.class).get('name','I')_().out()
Final-Graph-Send-a-Gremlin-Script---URL-encoded.svg
Figure 1. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/x-www-form-urlencoded

script=g.getIndex%28%27node_auto_index%27%2C+Vertex.class%29.get%28%27name%27%2C%27I%27%29_%28%29.out%28%29

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/0/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/0/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/0/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/0/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/0",
  "properties" : "http://localhost:7474/db/data/node/0/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/0/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/0/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/0/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/0/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/0/relationships/in/{-list|&|types}"
} ]

Load a sample graph

Import a graph form a GraphML file can be achieved through the Gremlin GraphMLReader. The following script imports a small GraphML file from an URL into Neo4j, resulting in the depicted graph. The underlying database is auto-indexed, see [auto-indexing] so the script can return the imported node by index lookup.

Raw script source

g.loadGraphML('https://raw.github.com/neo4j-contrib/gremlin-plugin/master/src/data/graphml1.xml')
g.autoStartTransaction(false)
g.getIndex('node_auto_index', Vertex.class).get('name','you').toList()
Final-Graph-Load-a-sample-graph.svg
Figure 2. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.loadGraphML('https://raw.github.com/neo4j-contrib/gremlin-plugin/master/src/data/graphml1.xml');g.autoStartTransaction(false);g.getIndex('node_auto_index', Vertex.class).get('name','you').toList();"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/9/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/9/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/9/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/9/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/9/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/9",
  "properties" : "http://localhost:7474/db/data/node/9/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/9/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/9/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/9/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/9/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/9/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/9/relationships/in/{-list|&|types}"
} ]

Sort a result using raw Groovy operations

The following script returns a sorted list of all nodes connected via outgoing relationships to node 1, sorted by their name-property.

Raw script source

g.getIndex('node_auto_index', Vertex.class).get('name','I')._().out.sort{it.name}
Final-Graph-Sort-a-result-using-raw-Groovy-operations.svg
Figure 3. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.getIndex('node_auto_index', Vertex.class).get('name','I')._().out.sort{it.name}"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/15/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/15/relationships/out",
  "data" : {
    "name" : "him"
  },
  "traverse" : "http://localhost:7474/db/data/node/15/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/15/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/15/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/15",
  "properties" : "http://localhost:7474/db/data/node/15/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/15/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/15/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/15/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/15/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/15/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/15/relationships/in/{-list|&|types}"
}, {
  "labels" : "http://localhost:7474/db/data/node/14/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/14/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/14/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/14/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/14/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/14",
  "properties" : "http://localhost:7474/db/data/node/14/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/14/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/14/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/14/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/14/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/14/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/14/relationships/in/{-list|&|types}"
} ]

Send a Gremlin Script - JSON encoded with table results

To send a Script JSON encoded, set the payload Content-Type Header. In this example, find all the things that my friends like, and return a table listing my friends by their name, and the names of the things they like in a table with two columns, ignoring the third named step variable I. Remember that everything in Gremlin is an iterator - in order to populate the result table t, iterate through the pipes with iterate().

Raw script source

t= new Table()
g.v(22).as('I').out('know').as('friend').out('like').as('likes').table(t,['friend','likes']){it.name}{it.name}.iterate()
t
Final-Graph-Send-a-Gremlin-Script---JSON-encoded-with-table-results.svg
Figure 4. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "t= new Table();g.v(22).as('I').out('know').as('friend').out('like').as('likes').table(t,['friend','likes']){it.name}{it.name}.iterate();t;"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

{
  "columns" : [ "friend", "likes" ],
  "data" : [ [ "Joe", "cats" ], [ "Joe", "dogs" ] ]
}

Returning nested pipes

Raw script source

g.v(26).as('I').out('know').as('friend').out('like').as('likes').table(new Table()){it.name}{it.name}.cap
Final-Graph-returning-nested-pipes.svg
Figure 5. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(26).as('I').out('know').as('friend').out('like').as('likes').table(new Table()){it.name}{it.name}.cap;"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "data" : [ [ "I", "Joe", "cats" ], [ "I", "Joe", "dogs" ] ],
  "columns" : [ "I", "friend", "likes" ]
} ]

Set script variables

To set variables in the bindings for the Gremlin Script Engine on the server, you can include a params parameter with a String representing a JSON map of variables to set to initial values. These can then be accessed as normal variables within the script.

Raw script source

meaning_of_life
Final-Graph-Set-script-variables.svg
Figure 6. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "meaning_of_life",
  "params" : {
    "meaning_of_life" : 42.0
  }
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

42.0

Send a Gremlin Script with variables in a JSON Map

Send a Gremlin Script, as JSON payload and additional parameters

Raw script source

g.v(me).out
Final-Graph-Send-a-Gremlin-Script-with-variables-in-a-JSON-Map.svg
Figure 7. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(me).out",
  "params" : {
    "me" : "5"
  }
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/4/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/4/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/4/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/4/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/4",
  "properties" : "http://localhost:7474/db/data/node/4/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/4/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/4/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/4/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/4/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/4/relationships/in/{-list|&|types}"
} ]

Return paths from a Gremlin script

The following script returns paths. Paths in Gremlin consist of the pipes that make up the path from the starting pipes. The server is returning JSON representations of their content as a nested list.

Raw script source

g.v(19).out.name.path
Final-Graph-Return-paths-from-a-Gremlin-script.svg
Figure 8. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(19).out.name.path"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ [ {
  "labels" : "http://localhost:7474/db/data/node/19/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/19/relationships/out",
  "data" : {
    "name" : "I"
  },
  "traverse" : "http://localhost:7474/db/data/node/19/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/19/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/19",
  "properties" : "http://localhost:7474/db/data/node/19/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/19/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/19/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/19/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/19/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/in/{-list|&|types}"
}, {
  "labels" : "http://localhost:7474/db/data/node/17/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/17/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/17/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/17/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/17/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/17",
  "properties" : "http://localhost:7474/db/data/node/17/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/17/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/17/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/17/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/17/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/17/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/17/relationships/in/{-list|&|types}"
}, "you" ], [ {
  "labels" : "http://localhost:7474/db/data/node/19/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/19/relationships/out",
  "data" : {
    "name" : "I"
  },
  "traverse" : "http://localhost:7474/db/data/node/19/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/19/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/19",
  "properties" : "http://localhost:7474/db/data/node/19/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/19/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/19/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/19/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/19/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/19/relationships/in/{-list|&|types}"
}, {
  "labels" : "http://localhost:7474/db/data/node/18/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/18/relationships/out",
  "data" : {
    "name" : "him"
  },
  "traverse" : "http://localhost:7474/db/data/node/18/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/18/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/18/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/18",
  "properties" : "http://localhost:7474/db/data/node/18/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/18/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/18/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/18/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/18/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/18/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/18/relationships/in/{-list|&|types}"
}, "him" ] ]

Send an arbitrary Groovy script - Lucene sorting

This example demonstrates that you via the Groovy runtime embedded with the server have full access to all of the servers Java APIs. The below example creates Nodes in the database both via the Blueprints and the Neo4j API indexes the nodes via the native Neo4j Indexing API constructs a custom Lucene sorting and searching returns a Neo4j IndexHits result iterator.

Raw script source

'******** Additional imports *********'
import org.neo4j.graphdb.index.*
import org.neo4j.graphdb.*
import org.neo4j.index.lucene.*
import org.apache.lucene.search.*

'**** Blueprints API methods on the injected Neo4j2Graph at variable g ****'
meVertex = g.addVertex([name:'me'])
meNode = meVertex.getRawVertex()

'*** get the Neo4j raw instance ***'
neo4j = g.getRawGraph()


'******** Neo4j API methods: *********'
tx = neo4j.beginTx()
 youNode = neo4j.createNode()
 youNode.setProperty('name','you')
 youNode.createRelationshipTo(meNode,DynamicRelationshipType.withName('knows'))

'*** index using Neo4j APIs ***'
 idxManager = neo4j.index()
 personIndex = idxManager.forNodes('persons')
 personIndex.add(meNode,'name',meNode.getProperty('name'))
 personIndex.add(youNode,'name',youNode.getProperty('name'))
tx.success()
tx.finish()

'*** Prepare a custom Lucene query context with Neo4j API ***'
query = new QueryContext( 'name:*' ).sort( new Sort(new SortField( 'name',SortField.STRING, true ) ) )
results = personIndex.query( query ).toList()
Final-Graph-Send-an-arbitrary-Groovy-script---Lucene-sorting.svg
Figure 9. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "'******** Additional imports *********';import org.neo4j.graphdb.index.*;import org.neo4j.graphdb.*;import org.neo4j.index.lucene.*;import org.apache.lucene.search.*;;'**** Blueprints API methods on the injected Neo4j2Graph at variable g ****';meVertex = g.addVertex([name:'me']);meNode = meVertex.getRawVertex();;'*** get the Neo4j raw instance ***';neo4j = g.getRawGraph();;;'******** Neo4j API methods: *********';tx = neo4j.beginTx(); youNode = neo4j.createNode(); youNode.setProperty('name','you'); youNode.createRelationshipTo(meNode,DynamicRelationshipType.withName('knows'));;'*** index using Neo4j APIs ***'; idxManager = neo4j.index(); personIndex = idxManager.forNodes('persons'); personIndex.add(meNode,'name',meNode.getProperty('name')); personIndex.add(youNode,'name',youNode.getProperty('name'));tx.success();tx.finish();;'*** Prepare a custom Lucene query context with Neo4j API ***';query = new QueryContext( 'name:*' ).sort( new Sort(new SortField( 'name',SortField.STRING, true ) ) );results = personIndex.query( query ).toList();"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/29/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/29/relationships/out",
  "data" : {
    "name" : "you"
  },
  "traverse" : "http://localhost:7474/db/data/node/29/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/29/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/29/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/29",
  "properties" : "http://localhost:7474/db/data/node/29/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/29/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/29/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/29/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/29/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/29/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/29/relationships/in/{-list|&|types}"
}, {
  "labels" : "http://localhost:7474/db/data/node/28/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/28/relationships/out",
  "data" : {
    "name" : "me"
  },
  "traverse" : "http://localhost:7474/db/data/node/28/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/28/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/28/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/28",
  "properties" : "http://localhost:7474/db/data/node/28/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/28/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/28/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/28/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/28/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/28/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/28/relationships/in/{-list|&|types}"
} ]

Emit a sample graph

Exporting a graph can be done by simple emitting the appropriate String.

Raw script source

writer = new GraphMLWriter(g)
out = new java.io.ByteArrayOutputStream()
writer.outputGraph(out)
result = out.toString()
Final-Graph-Emit-a-sample-graph.svg
Figure 10. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "writer = new GraphMLWriter(g);out = new java.io.ByteArrayOutputStream();writer.outputGraph(out);result = out.toString();"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

"<?xml version=\"1.0\" ?><graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.1/graphml.xsd\"><key id=\"name\" for=\"node\" attr.name=\"name\" attr.type=\"string\"></key><graph id=\"G\" edgedefault=\"directed\"><node id=\"11\"><data key=\"name\">you</data></node><node id=\"12\"><data key=\"name\">him</data></node><node id=\"13\"><data key=\"name\">I</data></node><edge id=\"6\" source=\"13\" target=\"11\" label=\"know\"></edge><edge id=\"7\" source=\"13\" target=\"12\" label=\"know\"></edge></graph></graphml>"

HyperEdges - find user roles in groups

Imagine a user being part of different groups. A group can have different roles, and a user can be part of different groups. He also can have different roles in different groups apart from the membership. The association of a User, a Group and a Role can be referred to as a HyperEdge. However, it can be easily modeled in a property graph as a node that captures this n-ary relationship, as depicted below in the U1G2R1 node.

To find out in what roles a user is for a particular groups (here Group2), the following script can traverse this HyperEdge node and provide answers.

Raw script source

g.v(36).out('hasRoleInGroup').as('hyperedge').out('hasGroup').filter{it.name=='Group2'}.back('hyperedge').out('hasRole').name
Final-Graph-HyperEdges---find-user-roles-in-groups.svg
Figure 11. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(36).out('hasRoleInGroup').as('hyperedge').out('hasGroup').filter{it.name=='Group2'}.back('hyperedge').out('hasRole').name"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ "Role1" ]

Group count

This example is showing a group count in Gremlin, for instance the counting of the different relationship types connected to some the start node. The result is collected into a variable that then is returned.

Raw script source

m = [:]
g.v(40).bothE().label.groupCount(m).iterate()
m
Final-Graph-group-count.svg
Figure 12. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "m = [:];g.v(40).bothE().label.groupCount(m).iterate();m"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

{
  "knows" : 2,
  "likes" : 1
}

Collect multiple traversal results

Multiple traversals can be combined into a single result, using splitting and merging pipes in a lazy fashion.

Raw script source

g.getIndex('node_auto_index', Vertex.class).get('name','Peter')._().copySplit(_().out('knows'), _().in('likes')).fairMerge.name
Final-Graph-collect-multiple-traversal-results.svg
Figure 13. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.getIndex('node_auto_index', Vertex.class).get('name','Peter')._().copySplit(_().out('knows'), _().in('likes')).fairMerge.name"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ "Ian", "Marie" ]

Collaborative filtering

This example demonstrates basic collaborative filtering - ordering a traversal after occurence counts and substracting objects that are not interesting in the final result.

Here, we are finding Friends-of-Friends that are not Joes friends already. The same can be applied to graphs of users that LIKE things and others.

Raw script source

x=[]
fof=[:]
g.v(62).out('knows').aggregate(x).out('knows').except(x).groupCount(fof).iterate()
fof.sort{a,b -> b.value <=> a.value}
Final-Graph-collaborative-filtering.svg
Figure 14. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "x=[];fof=[:];g.v(62).out('knows').aggregate(x).out('knows').except(x).groupCount(fof).iterate();fof.sort{a,b -> b.value <=> a.value}"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

{
  "v[60]" : 2,
  "v[59]" : 1,
  "v[61]" : 1
}

Chunking and offsetting in Gremlin

Raw script source

g.v(50).out('knows').filter{it.name == 'Sara'}[0..100]
Final-Graph-chunking-and-offsetting-in-Gremlin.svg
Figure 15. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(50).out('knows').filter{it.name == 'Sara'}[0..100]"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ {
  "labels" : "http://localhost:7474/db/data/node/48/labels",
  "outgoing_relationships" : "http://localhost:7474/db/data/node/48/relationships/out",
  "data" : {
    "name" : "Sara"
  },
  "traverse" : "http://localhost:7474/db/data/node/48/traverse/{returnType}",
  "all_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/all/{-list|&|types}",
  "property" : "http://localhost:7474/db/data/node/48/properties/{key}",
  "self" : "http://localhost:7474/db/data/node/48",
  "properties" : "http://localhost:7474/db/data/node/48/properties",
  "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/out/{-list|&|types}",
  "incoming_relationships" : "http://localhost:7474/db/data/node/48/relationships/in",
  "extensions" : {
  },
  "create_relationship" : "http://localhost:7474/db/data/node/48/relationships",
  "paged_traverse" : "http://localhost:7474/db/data/node/48/paged/traverse/{returnType}{?pageSize,leaseTime}",
  "all_relationships" : "http://localhost:7474/db/data/node/48/relationships/all",
  "incoming_typed_relationships" : "http://localhost:7474/db/data/node/48/relationships/in/{-list|&|types}"
} ]

Modify the graph while traversing

This example is showing a group count in Gremlin, for instance the counting of the different relationship types connected to some the start node. The result is collected into a variable that then is returned.

Starting-Graph-starting_graphmodify-the-graph-while-traversing.svg
Figure 16. Starting Graph

Raw script source

g.v(43).bothE.each{g.removeEdge(it)}
Final-Graph-modify-the-graph-while-traversing.svg
Figure 17. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.v(43).bothE.each{g.removeEdge(it)};"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

[ ]

Flow algorithms with Gremlin

This is a basic stub example for implementing flow algorithms in for instance Flow Networks with a pipes-based approach and scripting, here between source and sink using the capacity property on relationships as the base for the flow function and modifying the graph during calculation.

Starting-Graph-starting_graphflow-algorithms-with-Gremlin.svg
Figure 18. Starting Graph

Raw script source

source=g.v(71)
sink=g.v(72)
maxFlow = 0
source.outE.inV.loop(2){!it.object.equals(sink)}.path.each{flow = it.capacity.min()
 maxFlow += flow
it.findAll{it.capacity}.each{it.capacity -= flow}}
maxFlow
Final-Graph-flow-algorithms-with-Gremlin.svg
Figure 19. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "source=g.v(71);sink=g.v(72);maxFlow = 0;source.outE.inV.loop(2){!it.object.equals(sink)}.path.each{flow = it.capacity.min(); maxFlow += flow;it.findAll{it.capacity}.each{it.capacity -= flow}};maxFlow"
}

Example response

  • 200: OK

  • Content-Type: application/json; charset=UTF-8

4

Script execution errors

Script errors will result in an HTTP error response code.

Final-Graph-script-execution-errors.svg
Figure 20. Final Graph

Example request

  • POST http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

  • Accept: application/json; charset=UTF-8

  • Content-Type: application/json

{
  "script" : "g.blah"
}

Example response

  • 400: Bad Request

  • Content-Type: application/json; charset=UTF-8

{
  "message": "javax.script.ScriptException: groovy.lang.MissingPropertyException: No such property: blah for class: com.tinkerpop.blueprints.impls.neo4j2.Neo4j2Graph\nPossible solutions: class",
  "exception": "BadInputException",
  "fullname": "org.neo4j.server.rest.repr.BadInputException",
  "stacktrace": [
    "org.neo4j.server.plugin.gremlin.GremlinPlugin.executeScript(GremlinPlugin.java:93)",
    "java.lang.reflect.Method.invoke(Method.java:601)",
    "org.neo4j.server.plugins.PluginMethod.invoke(PluginMethod.java:61)",
    "org.neo4j.server.plugins.PluginManager.invoke(PluginManager.java:168)",
    "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:312)",
    "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:134)",
    "java.lang.reflect.Method.invoke(Method.java:601)",
    "org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)",
    "org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
    "java.lang.Thread.run(Thread.java:722)"
  ],
  "cause": {
    "message": "javax.script.ScriptException: groovy.lang.MissingPropertyException: No such property: blah for class: com.tinkerpop.blueprints.impls.neo4j2.Neo4j2Graph\nPossible solutions: class",
    "cause": {
      "message": "groovy.lang.MissingPropertyException: No such property: blah for class: com.tinkerpop.blueprints.impls.neo4j2.Neo4j2Graph\nPossible solutions: class",
      "cause": {
        "message": "No such property: blah for class: com.tinkerpop.blueprints.impls.neo4j2.Neo4j2Graph\nPossible solutions: class",
        "exception": "MissingPropertyException",
        "stacktrace": [
          "java.lang.reflect.Constructor.newInstance(Constructor.java:525)",
          "org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)",
          "org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)",
          "org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)",
          "org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)",
          "org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:194)",
          "com.tinkerpop.gremlin.groovy.loaders.GraphLoader$_load_closure1.doCall(GraphLoader.groovy:32)",
          "java.lang.reflect.Method.invoke(Method.java:601)",
          "org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)",
          "org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:80)",
          "groovy.lang.MetaClassImpl.invokeMissingProperty(MetaClassImpl.java:771)",
          "groovy.lang.MetaClassImpl$12.getProperty(MetaClassImpl.java:1786)",
          "org.codehaus.groovy.runtime.callsite.GetEffectivePojoPropertySite.getProperty(GetEffectivePojoPropertySite.java:61)",
          "org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:227)",
          "Script24.run(Script24.groovy:1)",
          "com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:219)",
          "com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:90)",
          "javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)",
          "org.neo4j.server.plugin.gremlin.GremlinPlugin.executeScript(GremlinPlugin.java:86)",
          "java.lang.reflect.Method.invoke(Method.java:601)",
          "org.neo4j.server.plugins.PluginMethod.invoke(PluginMethod.java:61)",
          "org.neo4j.server.plugins.PluginManager.invoke(PluginManager.java:168)",
          "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:312)",
          "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:134)",
          "java.lang.reflect.Method.invoke(Method.java:601)",
          "org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)",
          "org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
          "java.lang.Thread.run(Thread.java:722)"
        ],
        "fullname": "groovy.lang.MissingPropertyException"
      },
      "exception": "ScriptException",
      "stacktrace": [
        "com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:221)",
        "com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:90)",
        "javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)",
        "org.neo4j.server.plugin.gremlin.GremlinPlugin.executeScript(GremlinPlugin.java:86)",
        "java.lang.reflect.Method.invoke(Method.java:601)",
        "org.neo4j.server.plugins.PluginMethod.invoke(PluginMethod.java:61)",
        "org.neo4j.server.plugins.PluginManager.invoke(PluginManager.java:168)",
        "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:312)",
        "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:134)",
        "java.lang.reflect.Method.invoke(Method.java:601)",
        "org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)",
        "org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
        "java.lang.Thread.run(Thread.java:722)"
      ],
      "fullname": "javax.script.ScriptException"
    },
    "exception": "ScriptException",
    "stacktrace": [
      "com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine.eval(GremlinGroovyScriptEngine.java:94)",
      "javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)",
      "org.neo4j.server.plugin.gremlin.GremlinPlugin.executeScript(GremlinPlugin.java:86)",
      "java.lang.reflect.Method.invoke(Method.java:601)",
      "org.neo4j.server.plugins.PluginMethod.invoke(PluginMethod.java:61)",
      "org.neo4j.server.plugins.PluginManager.invoke(PluginManager.java:168)",
      "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:312)",
      "org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:134)",
      "java.lang.reflect.Method.invoke(Method.java:601)",
      "org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)",
      "org.neo4j.server.rest.security.SecurityFilter.doFilter(SecurityFilter.java:112)",
      "java.lang.Thread.run(Thread.java:722)"
    ],
    "fullname": "javax.script.ScriptException"
  }
}