The same everyday tasks in the three graph query languages ArcadeDB speaks, side by side. They all work on the same data, so you can use whichever reads best for the job. Every snippet was tested against a running ArcadeDB server.
The examples use a tiny social graph: Person vertices with name and city, connected by Knows edges.
Run them from the Query panel of ArcadeDB Studio, from the console, or over HTTP with the language field set to
sql, sqlscript, cypher or gremlin. Moving an existing Cypher codebase over?
The Neo4j migration guide covers Bolt drivers and compatibility.
SQL declares schema up front. Cypher and Gremlin create a type the first time you use a label, so you can start schemaless and add constraints later.
CREATE VERTEX TYPE Person;
CREATE EDGE TYPE Knows;
CREATE PROPERTY Person.name STRING;// No DDL needed: the Person type is
// created on first use of the label
CREATE (:Person {name: 'Alice'})// No DDL needed: the Person type is
// created on first use of the label
g.addV('Person').property('name', 'Alice')Each statement creates one Person vertex with two properties.
CREATE VERTEX Person SET name = 'Alice', city = 'Rome'
-- INSERT works too
INSERT INTO Person SET name = 'Bob', city = 'Rome'CREATE (p:Person {name: 'Dave', city: 'Paris'})
RETURN pg.addV('Person')
.property('name', 'Frank')
.property('city', 'Rome')Connect two existing vertices with a Knows edge that carries a property.
CREATE EDGE Knows
FROM (SELECT FROM Person WHERE name = 'Alice')
TO (SELECT FROM Person WHERE name = 'Bob')
SET since = 2020MATCH (a:Person {name: 'Alice'}),
(b:Person {name: 'Bob'})
CREATE (a)-[:Knows {since: 2020}]->(b)g.V().has('Person', 'name', 'Alice')
.addE('Knows')
.to(__.V().has('Person', 'name', 'Bob'))
.property('since', 2020)Who does Alice know? One hop along outgoing Knows edges.
MATCH {type: Person, as: a, where: (name = 'Alice')}
-Knows->{as: b}
RETURN a.name, b.nameMATCH (a:Person {name: 'Alice'})-[:Knows]->(b)
RETURN b.nameg.V().has('Person', 'name', 'Alice')
.out('Knows')
.values('name')Everyone Alice reaches in one, two or three Knows hops, without duplicates.
MATCH {type: Person, as: a, where: (name = 'Alice')}
-Knows->{as: b, while: ($depth < 3),
where: ($depth > 0)}
RETURN DISTINCT b.name AS nameMATCH (a:Person {name: 'Alice'})-[:Knows*1..3]->(b)
RETURN DISTINCT b.nameg.V().has('Person', 'name', 'Alice')
.repeat(out('Knows')).emit().times(3)
.dedup()
.values('name')The fewest hops between two people, ignoring edge direction.
SELECT shortestPath($a, $b, 'BOTH', 'Knows') AS path
LET $a = (SELECT FROM Person WHERE name = 'Alice'),
$b = (SELECT FROM Person WHERE name = 'Dave')MATCH p = shortestPath(
(a:Person {name: 'Alice'})-[:Knows*]-(b:Person {name: 'Dave'}))
RETURN [n IN nodes(p) | n.name] AS namesg.V().has('Person', 'name', 'Alice')
.repeat(both('Knows').simplePath())
.until(has('name', 'Dave'))
.path().by('name')
.limit(1)Count people per city, largest group first.
SELECT city, count(*) AS people
FROM Person
GROUP BY city
ORDER BY people DESCMATCH (p:Person)
RETURN p.city AS city, count(*) AS people
ORDER BY people DESCg.V().hasLabel('Person')
.groupCount()
.by('city')A unique index doubles as a uniqueness constraint. Gremlin has no index DDL: create indexes with SQL or Cypher, from any client.
CREATE INDEX ON Person (name) UNIQUE;
CREATE INDEX ON Person (city) NOTUNIQUE;
-- idempotent form
CREATE INDEX IF NOT EXISTS ON Person (city) NOTUNIQUE;CREATE INDEX FOR (p:Person) ON (p.city)
CREATE CONSTRAINT FOR (p:Person)
REQUIRE p.email IS UNIQUENo index DDL in Gremlin. Create the index with SQL or Cypher; Gremlin traversals run on the same data.
A FULL_TEXT index is backed by Apache Lucene and ranks results with BM25. Queries use Lucene syntax.
CREATE INDEX ON Article (content) FULL_TEXT;
SELECT title, $score
FROM Article
WHERE SEARCH_INDEX('Article[content]', '+graph +database')
ORDER BY $score DESC// index created with the SQL on the left
CALL db.index.fulltext.queryNodes(
'Article[content]', 'graph AND database')
YIELD node, score
RETURN node.title, scoreNot available as a Gremlin step. Use the SQL or Cypher form from the same database.
Approximate nearest-neighbor search on an LSM_VECTOR index. Pass the query embedding as a parameter; its length must match the index dimensions.
CREATE PROPERTY Doc.embedding ARRAY_OF_FLOATS;
CREATE INDEX ON Doc (embedding) LSM_VECTOR
METADATA {dimensions: 384, similarity: 'COSINE'};
-- 10 nearest neighbors, with distance
SELECT expand(vector.neighbors('Doc[embedding]', :queryVector, 10))// index created with the SQL on the left
CALL db.index.vector.queryNodes(
'Doc[embedding]', 10, $queryVector)
YIELD node, score
RETURN node.title AS title, scoreNot available as a Gremlin step. Use the SQL or Cypher form from the same database.
Every single command is already atomic. To group several, use a SQL script, an HTTP session, or your driver's transaction API.
-- language: sqlscript
BEGIN;
LET a = CREATE VERTEX Person SET name = 'Gina';
LET b = CREATE VERTEX Person SET name = 'Hal';
CREATE EDGE Knows FROM $a TO $b;
COMMIT RETRY 3;# HTTP: begin returns an arcadedb-session-id header
curl -i -X POST -u root:pwd \
http://localhost:2480/api/v1/begin/mydb
# send it with each command, then commit
curl -X POST -u root:pwd \
-H 'arcadedb-session-id: <id>' \
-d '{"language":"cypher","command":"CREATE (:Person {name: \"Ivy\"})"}' \
http://localhost:2480/api/v1/command/mydb
curl -X POST -u root:pwd \
-H 'arcadedb-session-id: <id>' \
http://localhost:2480/api/v1/commit/mydb// Java, remote Gremlin driver
Transaction tx = g.tx();
GraphTraversalSource gtx = tx.begin();
try {
gtx.addV("Person").property("name", "Alice").iterate();
tx.commit();
} catch (Exception e) {
tx.rollback();
}Yes. All three languages read and write the same records. A type created in SQL is a label in Cypher and Gremlin, and a vertex inserted with Gremlin is returned by a SQL SELECT. Pick the language per query.
Use SQL for schema, indexes, bulk updates and reporting, Cypher for readable graph patterns and for code moving from Neo4j, and Gremlin when you already have TinkerPop traversals. Full-text and vector search are available from SQL and Cypher.
No. Cypher and Gremlin create a vertex or edge type the first time you use a label. SQL can declare types, properties and indexes explicitly when you want constraints.