Cypher for Gremlin

oCIM 4 Apache TinkerPop

Apache TinkerPop™ is a graph computing framework for both graph databases (OLTP) and graph analytic systems (OLAP).

Gremlin is a graph traversal language developed by Apache TinkerPop. Gremlin in the wild Gremlin in the wild

IBM Graph Gremlin in the wild

IBM Graph Gremlin in the wild

IBM Graph Azure Cosmos DB Gremlin in the wild

IBM Graph Azure Cosmos DB

Amazon Neptune Gremlin language

gremlin> g.V().count() ==>0 gremlin> g.addV('City').property('name', 'København') gremlin> g.V().count() ==>1 gremlin> g.V().hasLabel('City').values('name') ==>København Declarative vs imperative querying

// : what to get MATCH (b:Boat)-[:SAILS_TO]->(:Location {name: 'Denmark'}) RETURN b

// Gremlin: how to get it g.V().hasLabel('Boat').as('b') .out('SAILS_TO') .hasLabel('Location').has('name', 'Denmark') .select('b') Gremlin traversal g.V().hasLabel('Boat').as('b') .out('SAILS_TO') .hasLabel('Location').has('name', 'Denmark') .select('b') .profile()

Step Count

TinkerGraphStep(vertex,[~label.eq(Boat)])@[b] 100000

VertexStep(OUT,[SAILS_TO],vertex) 100000

HasStep([~label.eq(Location), name.eq(Denmark)]) 1

SelectOneStep(last,b) 1 Gremlin traversal (the right way) g.V().hasLabel('Location').has('name', 'Denmark') .in('SAILS_TO') .hasLabel('Boat') .profile()

Step Count

TinkerGraphStep(vertex,[~label.eq(Location), name.eq(Denmark)]) 1

VertexStep(IN,[SAILS_TO],vertex) 1

HasStep([~label.eq(Boat)]) 1 Cypher in Gremlin

// MATCH (b:Boat)-[:SAILS_TO]->(:Location {name: 'Denmark'}) // RETURN b.name g.V().hasLabel('Boat').as('b') .out('SAILS_TO') .hasLabel('Location').has('name', 'Denmark') .select('b').values('name')

==>"Havhingsten fra Glendalough" ==>"Roar Ege" Cypher in Gremlin: return columns

// MATCH (b:Boat)-[:SAILS_TO]->(:Location {name: 'Denmark'}) // RETURN b.name g.V().hasLabel('Boat').as('b') .out('SAILS_TO') .hasLabel('Location').has('name', 'Denmark') .select('b').project('b.name').by(values('name'))

==>{"b.name": "Havhingsten fra Glendalough"} ==>{"b.name": "Roar Ege"} Null handling in Gremlin

gremlin> g.addV('Boat').property('name', 'Roar Ege') ==>v[0] gremlin> g.addV('Boat') ==>v[1] gremlin> g.V().count() ==>2 gremlin> g.V().hasLabel('Boat').values('name') ==>Roar Ege gremlin> g.V().hasLabel('Boat').project('name').by(values('name')) ==>[name:Roar Ege] The provided traverser does not map to a value: v[5]->[PropertiesStep([name],value)] Cypher in Gremlin: nulls

// MATCH (b:Boat)-[:SAILS_TO]->(:Location {name: 'Denmark'}) // RETURN b.name g.V().hasLabel('Boat').as('b') .out('SAILS_TO') .hasLabel('Location').has('name', 'Denmark') .select('b').project('b.name').by( choose(neq('NULL'), coalesce(values('name'), constant('NULL')), constant('NULL'))) Cypher in Gremlin: traversal-based logic

// RETURN $a AND $b g.inject('START').project('$a AND $b').by( choose( and(constant(a).is(eq(true)), constant(b).is(eq(true))), constant(true), choose( or(constant(a).is(eq(false)), constant(b).is(eq(false))), constant(false), constant('NULL')))) Translation flow Cypher TCK progress

82.5% 731 scenarios

(with Server plugin) Major limitations

● Functionality exclusive to Gremlin Servers with Cypher plugin:

○ List access by non-constant index ○ Map access ○ Non-numeric plus operator ○ Path comprehensions ○ Functions with non-native implementation: length, nodes, percentileCont, percentileDisc, properties, relationships, size, toBoolean, toFloat, toInteger, toString ● Multiple labels and label modification are not supported

○ TinkerPop graph elements have a single, immutable string label ○ No plans for a workaround Cypher for Gremlin integration

Gremlin Server Gremlin Server

without Cypher plugin with Cypher plugin

Gremlin driver ❌ ✔ server-side translation

Cypher Gremlin client ✔ client-side translation ✔ server-side translation for Java

Gremlin Console ❌ ❌

Gremlin Console ✔ client-side translation ✔ server-side translation with Cypher plugin It’s on GitHub!

https://github.com/opencypher/cypher-for-gremlin It’s on GitHub!

● Cypher to Gremlin translation library for Java ● Cypher plugin for Gremlin Server ● Cypher plugin for Gremlin Console ● Cypher wrapper for Gremlin client ● driver API wrapper ● TCK implementation for Gremlin

https://github.com/opencypher/cypher-for-gremlin Java APIs: translation

String cypher = "MATCH (p:Person) WHERE p.age > 25 RETURN p.name"; CypherAstWrapper ast = CypherAstWrapper.parse(cypher);

Translator translator = Translator.builder().gremlinGroovy().build(); String gremlin = ast.buildTranslation(translator); Java APIs: Gremlin Server client

Cluster cluster = Cluster.open(configuration); Client gremlinClient = cluster.connect(); CypherGremlinClient cypherGremlinClient = CypherGremlinClient.translating(gremlinClient);

String cypher = "MATCH (p:person) WHERE p.age > 25 RETURN p.name"; CypherResultSet resultSet = cypherGremlinClient.submit(cypher); List> results = resultSet.all(); Java APIs: Neo4j driver

Config config = Config.build() .withTranslation() .toConfig(); Driver driver = GremlinDatabase.driver("//localhost:8182", config);

String cypher = "MATCH (p:person) WHERE p.age > 25 RETURN p.name"; try (Session session = driver.session()) { StatementResult result = session.run(cypher); List records = result.list(); } Java APIs: In-memory graphs

TinkerGraph graph = TinkerFactory.createModern(); GraphTraversalSource traversal = graph.traversal();

// Gremlin Server client CypherGremlinClient cypherGremlinClient = CypherGremlinClient.inMemory(traversal);

// Neo4j driver Driver driver = GremlinDatabase.driver(traversal); Java APIs: In-memory graphs

// SimpleGraph Excel ExcelSystem excelSystem = new ExcelSystem(); excelSystem.connect(); excelSystem.moveTo(new File("graph.xlsx").toURI().toString()); GraphTraversalSource traversal = excelSystem.g();

CypherGremlinClient cypherGremlinClient = CypherGremlinClient.inMemory(traversal); Quick start

// Add dependencies compile "org.apache.tinkerpop:tinkergraph-gremlin:3.3.2" compile "org.opencypher.gremlin:cypher-gremlin-neo4j-driver:0.9.6"

// Create an in-memory graph TinkerGraph graph = TinkerFactory.createModern(); GraphTraversalSource traversal = graph.traversal(); Driver driver = GremlinDatabase.driver(traversal);

// Query as usual: driver.session()... etc // (Except transactions)