Differences in Cypher implementations

Differences in Cypher implementations

Memgraph implements the openCypher (opens in a new tab) query language and aims to be as close as possible to the most commonly used openCypher implementations. Still, there are some differences in Memgraph Cypher implementation that enhance the user experience.

Difference from Neo4j's Cypher implementation

The openCypher initiative stems from Neo4j's Cypher query language. Following is a list of the most important differences between Neo4j's and Memgraph's Cypher implementation is for users who are already familiar with Neo4j.

Indexes and constraints

In Memgraph, indexes are not created in advance and creating constraints does not imply index creation. Memgraph supports label-property and label node indexes, node property existence and uniqueness constraints.

By default, Neo4j will create a range index for the CREATE ... INDEX ... command. Here is an example of such a query:

CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname);

Memgraph does not support the same syntax. To create such an index in Memgraph, run:

CREATE INDEX ON :Person(surname);

To create only label index in Memgraph, run the following query:

CREATE INDEX ON :Person;

Memgraph does not support composite indexes. -> Track progress on GitHub (opens in a new tab) and add a comment if you require such a feature.

You can instruct the planner to use specific index(es) in Memgraph by using the syntax below:

USING INDEX :Label1, :Label2 ...;
USING INDEX :Label(property) ...;

Besides index hinting, the ANALYZE GRAPH feature can also be applied to optimize performance.

To create a node property existence constraint in Neo4j, you would run the following query:

CREATE CONSTRAINT author_name
FOR (author:Author) REQUIRE author.name IS NOT NULL;

To achieve the same in Memgraph, change the syntax to:

CREATE CONSTRAINT ON (author:Author) ASSERT EXISTS (author.name);

To drop a node property existence constraint in Neo4j, you would run the following query:

DROP CONSTRAINT author_name;

To achieve the same in Memgraph, change the syntax to:

DROP CONSTRAINT ON (author:Author) ASSERT EXISTS (author.name);

To create a node property uniqueness constraint in Neo4j, you would run the following query:

CREATE CONSTRAINT book_isbn
FOR (book:Book) REQUIRE book.isbn IS UNIQUE

To achieve the same in Memgraph, change the syntax to:

CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE;

To drop a node property uniqueness constraint in Neo4j, you would run the following query:

DROP CONSTRAINT book_isbn;

To achieve the same in Memgraph, change the syntax to:

DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE;

Shortest path

In Neo4j, to find the shortest possible path between two nodes, you would use the shortestPath algorithm:

MATCH p=shortestPath(
(:Person {name:"Keanu Reeves"})-[*]-(:Person {name:"Tom Hanks"})
)
RETURN p

Memgraph offers fast deep path traversals as built-in graph algorithms, including BFS, DFS, WSP and ASP algorithms. That is a bit different from the shortestPath and allShortestPaths functions you might be used to, but with such algorithms being built in, Memgraph offers fast traversals. Here is an example of how you would rewrite the above query to work in Memgraph:

MATCH p=(:Person {name:"Keanu Reeves"})-[*BFS]-(:Person {name:"Tom Hanks"})
RETURN p

NOT label expression

In Neo4j, you can use the NOT label expression (!):

MATCH (:Person {name:'Tom Hanks'})-[r:!ACTED_IN]->(m:Movie)
Return type(r) AS type, m.title AS movies

In Memgraph, such a construct is not supported, but there is still a workaround:

MATCH (p:Person {name:'Tom Hanks'})-[r]->(m:Movie)
WHERE type(r) != "ACTED_IN"
RETURN type(r) AS type, m.title AS movies;

Search for patterns of a fixed length

In Neo4j, to search for patterns of a fixed length, you would use the following construct:

MATCH (tom:Person {name:'Tom Hanks'})--{2}(colleagues:Person)
RETURN DISTINCT colleagues.name AS name, colleagues.born AS bornIn
ORDER BY bornIn
LIMIT 5

Memgraph does not support such a construct, but since it has built-in traversals, you can achieve the same with the depth-first search (DFS) algorithm:

MATCH (tom:Person {name:'Tom Hanks'})-[*2]-(colleagues:Person)
RETURN DISTINCT colleagues.name AS name, colleagues.born AS bornIn
ORDER BY bornIn
LIMIT 5

Similarly, to match a graph for patterns of a variable length, you would run the following query in Neo4j:

MATCH (p:Person {name:'Tom Hanks'})--{1,4}(colleagues:Person)
RETURN DISTINCT colleagues.name AS name, colleagues.born AS bornIn
ORDER BY bornIn, name
LIMIT 5

In Memgraph, again use DFS:

MATCH (p:Person {name:'Tom Hanks'})-[*1..4]-(colleagues:Person)
RETURN DISTINCT colleagues.name AS name, colleagues.born AS bornIn
ORDER BY bornIn, name
LIMIT 5

Unsupported constructs

CALL subqueries in transactions

Such a query is not supported in Memgraph, but you can use the periodic module to execute a query periodically in batches.

EXISTS subqueries

Such clause is not supported in Memgraph, but you can use exists() pattern function with the WHERE clause to filter with pattern expressions.

The following constructs are not yet supported in Memgraph:

For all other unsupported constructs that you require, please open an issue on our GitHub repository (opens in a new tab). Learn more in our Community Call on Subqueries & Patterns in Filtering Clauses (opens in a new tab).

COUNT subqueries

Such a construct is not supported in Memgraph, but you can use count() aggregation function to count the number of non-null values returned by the expression.

COLLECT subqueries

Such a construct is not supported in Memgraph, but you can use collect() aggregation function to return a single aggregated list from provided values.

Patterns in expressions

Patterns in expressions are supported in Memgraph in particular functions, like exists(pattern). In other cases, Memgraph does not yet support patterns in functions, e.g. size((n)-->()). Most of the time, the same functionalities can be expressed differently in Memgraph using OPTIONAL expansions, function calls, etc.

List comprehension

The following type of query is not yet supported in Memgraph:

MATCH (keanu:Person {name:'Keanu Reeves'})
RETURN [x IN keanu.resume WHERE x contains 'The Matrix'] AS matrixList

Unsupported expressions

Cypher expressions

  • Numerical:
    • An octal INTEGER literal (starting with 0o): 0o1372, 0o5671
    • A FLOAT literal: Inf, Infinity, NaN
  • Boolean:
    • Label and relationship type expressions: (n:A|B), ()-[r:R1|R2]->() -> Track progress on GitHub (opens in a new tab) and add a comment if you require such a feature.

Conditional expressions (CASE)

  • More than one value after WHEN operator:
    MATCH (n:Person)
    RETURN
    CASE n.eyes
      WHEN 'blue'  THEN 1
      WHEN 'brown', 'hazel' THEN 2
      ELSE 3
    END AS result, n.eyes
    Here is a workaround in Memgraph:
    MATCH (n:Person)
    RETURN
    CASE
    WHEN n.eyes='blue' THEN 1
    WHEN n.eyes='brown' OR n.eyes='hazel' THEN 2
    ELSE 3
    END AS result, n.eyes;
    -> Track progress on GitHub (opens in a new tab) and add a comment if you require such a feature.

Type predicate expressions

The following expression is not supported in Memgraph:

UNWIND [42, true, 'abc'] AS val
RETURN val, val IS :: INTEGER AS isInteger

Still, you can check the value type with the valueType() scalar function, which returns the value type of the object in textual format:

UNWIND [42, true, 'abc'] AS val
RETURN val, valueType(val) = "INTEGER"

Unsupported functions

Functions for converting data values:

  • toBooleanList()
  • toBooleanOrNull()
  • toFloatList()
  • toFloatOrNull()
  • toIntegerList()
  • toIntegerOrNull()
  • toStringList()
  • toStringOrNull()

Predicate functions:

  • exists(n.property) - can be expressed using n.property IS NOT NULL
  • isEmpty()

Scalar functions:

  • elementId() - id() can be used instead
  • length() - size() can be used instead
  • nullIf()

Aggregating functions:

  • percentileCont(), percentileDisc()
  • stDev(), stDevP()

Mathematical functions:

  • isNan()
  • cot()
  • degrees()
  • haversin()
  • radians()

String functions:

  • normalize()

Datetime functions:

  • time()

Memgraph's Cypher extension

Besides implementing openCypher, Memgraph created various language extensions to provide an enhanced user experience. Here are some of the improvements:

For all other unsupported constructs that you require, please open an issue on our GitHub repository (opens in a new tab).