Skip to main content
Everything you need to write a Cypher query in FalkorDB, on one page. Each section links to the full reference if you need the details.
FalkorDB implements a growing subset of OpenCypher (version 9) plus proprietary extensions. Before using syntax you know from another graph database, check Cypher coverage and Known limitations.
Statements separated by a blank line inside a single block are independent queries — send them one at a time rather than pasting the whole block.

Patterns

Patterns are the ASCII art at the heart of every query. Nodes use parentheses, relationships use brackets.
Variable-length and named paths:
Full reference: MATCH.

Reading clauses

A mandatory MATCH cannot follow an OPTIONAL MATCH.

Filtering with WHERE

Pattern predicates filter by structure instead of properties:
Label predicates work in WHERE too, though putting the label in the pattern is faster:
Full reference: WHERE.

Projecting and paging

WITH applies the same modifiers mid-query and controls which variables stay in scope:
Combine result sets with UNION (deduplicates) or UNION ALL (keeps every row). Column names and count must match:

Writing clauses

CREATE

Full reference: CREATE.

MERGE

MERGE matches an existing pattern or creates it. Merge one entity per clause to avoid accidental duplicates:
Conditional property updates:
MERGE on a whole path either matches the entire path or creates all of it. Merging (a {name: 'Alice'})-[:KNOWS]->(b {name: 'Bob'}) in one clause duplicates Alice and Bob when the relationship is missing.
Full reference: MERGE.

SET, REMOVE, DELETE

SET and REMOVE act on variables bound earlier in the query. The forms below are shown as clauses — prefix each with a MATCH such as MATCH (n:Person {name: 'Jim'}) to run it. The copy form is the exception: it needs both entities bound, as in MATCH (jim {name: 'Jim'}), (pam {name: 'Pam'}).
Put together, that is one complete statement:
Deleting a node always deletes its relationships — FalkorDB never leaves dangling edges. Full references: SET, REMOVE, DELETE.

FOREACH

Runs updating clauses (CREATE, MERGE, SET, REMOVE, DELETE, FOREACH) over a list without changing the current rows:
Full reference: FOREACH.

Subqueries (CALL {})

A subquery runs once per input row. Import outer variables with an opening WITH.
Non-returning subqueries perform side effects and keep the row count unchanged:
Full reference: CALL {}.

Functions

Full reference: Functions.

Aggregation

Non-aggregated expressions in RETURN become implicit grouping keys — there is no GROUP BY:

Comprehensions, CASE, and reduce

Indexing

Range index

Accelerates equality, range, geospatial, and array-membership filters.
The older CREATE INDEX ON :Person(age) form is still accepted and creates the same index. Range indexes are applied automatically. Confirm with GRAPH.EXPLAIN, which shows Index Scan instead of Node By Label Scan. Not-equal (<>) filters are not optimized. Full reference: Range index.

Full-text index

Pass OPTIONS instead to control stemming language and stopwords:
Query it with RediSearch syntax: term* for prefix matching, and %term%distance for fuzzy matching, where distance is the maximum Levenshtein distance (1–3, default 1).
Full reference: Full-text index.

Vector index

Full reference: Vector index.

Listing indexes

Store vectors with vecf32() and search them with the vector index procedures. The index, the stored vectors, and the query vector must all share the same dimension — the 3-dimensional vectors below are kept short so the example runs as-is.
The same works on relationship properties. This continues from the Doc nodes created above, so run that example first:
Order results by what the similarity function means. With cosine, a higher score is more similar, so sort DESC. With euclidean, the score is a distance, so sort ascending.
Compare two vectors directly without an index:
Use cosine for normalized embeddings (most text embedding models) and euclidean when magnitude is meaningful.

Constraints

Constraints are created with a Redis command rather than a Cypher clause. A unique constraint requires a supporting range index on exactly the same properties, so create that index first or the command fails.
Constraints are built asynchronously. The command replies PENDING and the constraint is enforced gradually while its status is UNDER CONSTRUCTION. It becomes OPERATIONAL once every governed node or relationship conforms, or FAILED if a conflict is found — in which case it is not enforced at all and has to be recreated after fixing the data.
Full references: GRAPH.CONSTRAINT CREATE, GRAPH.CONSTRAINT DROP.

Procedures

Call a procedure with CALL; YIELD is optional and selects specific columns.
Full reference: Procedures.

Graph algorithms

Weighted shortest paths:
Also available: BFS, WCC, label propagation, betweenness centrality, harmonic centrality, max flow, minimum spanning forest, and single-source shortest paths. Full reference: Algorithms.

Data types

Temporal values:
Full reference: Data types.

Importing CSV data

With a header row, address columns by name instead of position:
Use FIELDTERMINATOR for a delimiter other than a comma:
Every value arrives as a string, so cast with toInteger(), toFloat(), or toBoolean(). Local files resolve relative to the configured import folder; remote files must use HTTPS. Full reference: LOAD CSV.

Parameters

Never concatenate user input into a query. Pass parameters instead:
Every client library exposes a language-native way to pass parameters.

Running and profiling queries

That plan assumes a range index on :Person(age) already exists. Without one the same query reports Node By Label Scan, which is the quickest way to spot a missing index.

Gotchas

  • Indexes do not optimize <> filters — those still fall back to a full scan of the label or relationship type.
  • LIMIT does not restrict eager operations. UNWIND [1,2,3] AS v CREATE (a {property: v}) RETURN a LIMIT 1 creates three nodes and returns one.
  • When a relationship in a pattern is never referenced elsewhere, FalkorDB only verifies that at least one match exists. Reference the alias (for example WHERE ID(e) >= 0) when you need every match.
  • Aggregation functions are not allowed inside pattern comprehensions. Aggregate in a preceding WITH or use size() on the resulting list.
  • Regular-expression operators are unsupported; use STARTS WITH, ENDS WITH, CONTAINS, or string.matchRegEx().
  • Label expressions such as (n:A|B) in node patterns are unsupported. Filter with WHERE n:A OR n:B.
  • Vector index queries do not combine with property filters.
Full reference: Known limitations.

Frequently Asked Questions

MATCH only finds existing data and returns no rows when the pattern is absent. MERGE matches the pattern if it exists and creates it otherwise, so it behaves like an upsert. Use ON CREATE SET and ON MATCH SET to update properties differently in each case.
Use CREATE INDEX FOR (p:Person) ON (p.age) for a range index, CREATE FULLTEXT INDEX FOR (n:Movie) ON (n.title) for text search, and CREATE VECTOR INDEX FOR (p:Product) ON (p.embedding) OPTIONS {dimension: 768, similarityFunction: 'cosine'} for similarity search. List them all with CALL db.indexes().
Run GRAPH.EXPLAIN <graph> "<query>". The plan shows Index Scan when an index is used and Node By Label Scan when it is not. GRAPH.PROFILE adds per-operation timings.
Create a vector index, store embeddings with vecf32(), then run CALL db.idx.vector.queryNodes('Label', 'attribute', k, vecf32([...])) YIELD node, score. Use CALL db.idx.vector.queryRelationships('TYPE', 'attribute', k, vecf32([...])) YIELD relationship, score for relationship embeddings.
Mostly. FalkorDB implements a growing subset of OpenCypher version 9 with extensions such as vector indexes, full-text search, and built-in graph algorithms. Neo4j-only features such as label expressions are not available, and the OpenCypher regex operator is unsupported — use STARTS WITH, ENDS WITH, CONTAINS, or string.matchRegEx() instead. FalkorDB supports user-defined functions through the Flex UDF library. The named temporal arithmetic functions are also missing, though the + and - operators do work on temporal values. See Cypher coverage.
Run MATCH (n) DETACH DELETE n to empty the graph while keeping it, or use the GRAPH.DELETE command to drop the graph entirely.