Skip to content

Cypher Reference

Purple8 Graph implements a subset of the openCypher specification. The engine covers ~80% of everyday Cypher use cases. The sections below document exactly what is and is not supported.

Supported clauses

ClauseSinceNotes
CREATEv0.1Node and relationship creation
MATCHv0.1Pattern matching with optional predicates
OPTIONAL MATCHv0.5Returns null for non-matching patterns
MERGEv0.4Create if not exists
SETv0.1Update node/edge properties
DELETEv0.2Remove nodes and edges
DETACH DELETEv0.3Delete node + all its relationships
RETURNv0.1Project results
WITHv0.5Pipeline between query steps
WHEREv0.1Filter predicates
ORDER BYv0.3Sort results
LIMIT / SKIPv0.3Pagination
UNWINDv0.6Expand a list into rows
CALLv0.8Procedure calls (see below)

Pattern syntax

cypher
-- Node pattern
MATCH (n)
MATCH (n:Person)
MATCH (n:Person {name: "Alice"})

-- Relationship pattern
MATCH (a)-[:KNOWS]->(b)
MATCH (a)-[r:KNOWS {since: 2020}]->(b)

-- Undirected
MATCH (a)-[:KNOWS]-(b)

-- Variable-length path (v0.22+)
MATCH (a)-[:KNOWS*1..4]->(b)

-- Any relationship type
MATCH (a)-[r]->(b)

WHERE predicates

cypher
-- Comparison
WHERE n.age > 30
WHERE n.name = "Alice"
WHERE n.name <> "Bob"

-- Null checks
WHERE n.email IS NOT NULL
WHERE n.deleted_at IS NULL

-- String operators
WHERE n.name STARTS WITH "Al"
WHERE n.email ENDS WITH "@acme.com"
WHERE n.bio CONTAINS "engineer"

-- Boolean operators
WHERE n.age > 30 AND n.active = true
WHERE n.role = "admin" OR n.role = "owner"
WHERE NOT n.deleted = true

-- IN list
WHERE n.status IN ["active", "pending"]

-- Range (shorthand)
WHERE 18 <= n.age <= 65

Aggregation functions

cypher
MATCH (n:Document)
RETURN
  COUNT(n)                AS total,
  COUNT(DISTINCT n.author) AS unique_authors,
  AVG(n.word_count)       AS avg_words,
  SUM(n.word_count)       AS total_words,
  MIN(n.published_at)     AS earliest,
  MAX(n.published_at)     AS latest,
  COLLECT(n.title)        AS titles

WITH — multi-step pipelines

cypher
MATCH (p:Person)-[:AUTHORED]->(d:Document)
WHERE d.published_at > "2024-01-01"
WITH p, COUNT(d) AS doc_count
WHERE doc_count > 5
RETURN p.name, doc_count
ORDER BY doc_count DESC
LIMIT 10

MERGE — upsert pattern

cypher
-- Create the node if it doesn't exist, match if it does
MERGE (p:Person {email: "alice@acme.com"})
ON CREATE SET p.created_at = timestamp()
ON MATCH  SET p.last_seen  = timestamp()
RETURN p

Parameters

Always use parameters for user-supplied values — they prevent injection and enable query plan caching:

python
engine.query(
    "MATCH (p:Person {email: $email}) RETURN p",
    email="alice@acme.com",
)
cypher
-- In raw Cypher, parameters are referenced with $name
MATCH (p:Person {email: $email})
WHERE p.age > $min_age
RETURN p.name, p.age

CALL — built-in procedures

cypher
CALL db.vector.search('Label', $embedding, k)
YIELD node, score

-- Example: top-5 documents similar to a query vector
CALL db.vector.search('Document', $vec, 5)
YIELD node AS doc, score
RETURN doc.title, doc.author, score
ORDER BY score DESC
ArgumentTypeDescription
LabelSTRINGNode label to search within
$embeddingLIST<FLOAT>Query vector (must match index dimensionality)
kINTEGERNumber of nearest neighbours to return

With filters:

cypher
CALL db.vector.search('Document', $vec, 20)
YIELD node AS doc, score
WHERE doc.status = "published" AND doc.year >= 2023
RETURN doc.title, score
ORDER BY score DESC
LIMIT 5
cypher
CALL db.bm25.search('Document', 'transformer attention mechanism', 10)
YIELD node AS doc, score
RETURN doc.title, score
ORDER BY score DESC
cypher
CALL db.hybrid.search('Document', $vec, 'transformer attention', 10, {alpha: 0.6})
YIELD node AS doc, score
MATCH (doc)-[:AUTHORED_BY]->(author:Person)
RETURN doc.title, author.name, score
ORDER BY score DESC

alpha controls the vector/BM25 blend — 1.0 = pure vector, 0.0 = pure BM25.

Named embedding slots

When a node has multiple vector properties (e.g., title_vec + body_vec), specify the slot name:

cypher
CALL db.vector.search('Document', $vec, 10, {slot: 'body_vec'})
YIELD node AS doc, score
RETURN doc.title, score

Functions

FunctionDescription
id(n)Internal node ID
labels(n)List of node labels
type(r)Relationship type string
properties(n)All properties as a map
keys(n)List of property keys
size(list)Length of a list
toString(x)Cast to string
toInteger(x)Cast to integer
toFloat(x)Cast to float
toBoolean(x)Cast to boolean
toLower(s)Lowercase string
toUpper(s)Uppercase string
trim(s)Strip whitespace
split(s, delim)Split string to list
substring(s, start, len)Extract substring
timestamp()Current UNIX epoch (ms)
date()Current date string (YYYY-MM-DD)
coalesce(a, b, ...)First non-null value
head(list)First element
tail(list)All but first element
last(list)Last element
range(start, end)Integer range list

Not supported

The following openCypher features are not implemented in the current release:

FeatureWorkaround
FOREACHUNWIND + WITH
List comprehensions [x IN list WHERE ...]UNWIND + WHERE + COLLECT
Pattern comprehensions [(a)-->(b) | b.name]MATCH + COLLECT
CALL { ... } (subquery)Split into WITH steps
CALL { ... } IN TRANSACTIONSUse engine.batch_query()
EXISTS { ... } subqueryOPTIONAL MATCH + null check
APOC proceduresNo APOC support — use Python SDK
shortestPath() / allShortestPaths()Planned for v0.26
Full-text index synonymsUse BM25 + custom tokenizer

Cypher vs SQL at a glance

cypher
-- SQL:  SELECT p.name FROM people p JOIN authored a ON p.id=a.person_id
--       JOIN documents d ON d.id=a.doc_id WHERE d.year=2024
-- Cypher equivalent:
MATCH (p:Person)-[:AUTHORED]->(d:Document {year: 2024})
RETURN p.name

Purple8 Graph is proprietary software. All rights reserved.