neo4j-cypher-skill

Generates, optimizes, and validates Cypher 25 queries for Neo4j 2025.x and 2026.x. Use when writing new Cypher queries, optimizing slow queries, graph pattern matching, vector or fulltext search, subqueries, or batch writes. Covers MATCH, MERGE, CREATE, WITH, RETURN, CALL, UNWIND, FOREACH, LOAD CSV,

By neo4j-contrib · 1,132 installs

npx skills add neo4j-contrib/neo4j-skills --skill neo4j-cypher-skill

Source repository · Upstream listing

When to Use Writing, optimizing, or debugging Cypher queries Graph pattern matching, QPEs, variable length paths Vector/fulltext search, subqueries, batch writes, LOAD CSV When NOT to Use Driver migration/API changes → neo4j migration skill DB admin (users, config, backups) → neo4j cli tools skill Hybrid search that combines vector with fulltext or other ranked sources → neo4j vector index skill GQL conformance note: LET , FINISH , FILTER , and INSERT are valid Cypher 25 clauses (introduced via GQL conformance, mostly in Neo4j 2025.06). On older versions, fall back to WITH / (omit RETURN) / WHERE / CREATE . INSERT requires & separated multi labels and does not support dynamic labels/types. Pre flight ? Known Unknown <db name schema.json found in project Use it directly — skip live inspection — Schema (from context or live DB) Use directly Run Schema First Protocol Neo4j version Use version features Default to 2025.01 safe set Executing (not generating)? Use EXPLAIN + write gate State query is unvalidated Schema unknown + no tool → produce non executable sketch outside a code block: Never fill guessed names — realistic guesses get copied blindly. Defaults — apply every query 1. CYPHER 25 — first token; never repeat after UNION or inside subqueries 2. Schema first — inspect before writing; if schema in prompt, use it directly 3. MERGE on constrained key only; rel MERGE on already bound endpoints only 4. Label free MATCH (n) forbidden unless bound or followed by WHERE n:$($label) 5. LIMIT 25 default on all exploratory reads; push WITH n LIMIT before high cardinality operations (variable length traversals, fan out MATCH, Cartesian products) 6. Comments: // only — is SQL, invalid 7. REPEATABLE ELEMENTS / DIFFERENT RELATIONSHIPS go after MATCH , not end of pattern 8. SHOW commands: YIELD before WHERE ; combinable with general Cypher clauses incl. UNION / RETURN [2026.05] — SHOW DATABASES still requires system db (use USE system ). CALL on system db: YIELD then WHERE [2026.07] 9. Inline node predicates (:Label WHERE p=x) — valid in MATCH only 10. WHERE cannot follow bare UNWIND — use WITH x WHERE 11. (a) [:R] (b) — undirected matches both directions, double counts; use directed unless unknown 12. DETACH DELETE — plain DELETE throws if node has relationships Style Element Convention Node labels PascalCase :Person Rel types SCREAMING SNAKE CASE :KNOWS Properties/vars camelCase firstName Clauses UPPERCASE MATCH Booleans/null lowercase true false null Strings single quoted; double only if contains ' Schema is truth. :Person , :KNOWS , name in examples are illustrative — substitute real names from schema. Schema First Protocol Priority order: 1. <db name schema.json anywhere in project → read directly, state file name + schema retrieved at , skip live inspection. If significantly outdated and DB reachable, offer re fetch. Full rules: [references/schema guardrail.md](references/schema guardrail.md). Existence — labels/rel types/properties must be in schema; try synonym resolution before asking Property type — reason about intent first (e.g. string vs INTEGER may be null check); ask only if unclear Relationship direction — wrong direction → correct silently and note Synonym mapping — unambiguous → resolve silently; ambiguous → pick most likely, note; ask if unresolvable Scripts: generate schema.py (live DB + APOC), define schema.py (no DB), import neo4j schema.py (converts neo4j graphrag python , graph schema introspector , graph schema json js utils , mcp neo4j data modeling ). 2. Schema in context → use it, skip inspection. 3. Schema missing → run: Property types per label — check APOC first: Validate before returning any query: label exists · rel type+direction correct · property on that label · index ONLINE. Key Patterns MERGE SET n = {} replaces all props. SET n += {} merges (safe partial update). Use += for updates. WITH scope Every var not listed in WITH is dropped. WITH carries all forward. Subqueries — cheat sheet CALL { WITH x ... } deprecated → CALL (x) { ... } . COLLECT {} returns exactly one column. CALL IN TRANSACTIONS (bulk writes) Input stream must be outside subquery. Auto commit only — never wrap in beginTransaction() . PERIODIC COMMIT deprecated. DISJOINT BY [2026.06, Cypher 25] on IN CONCURRENT TRANSACTIONS prevents deadlocks by scheduling batches that share lock prone resources sequentially — use when importing relationships: DISJOINT BY (expr,...) declares lock keys (outer query variables only); DISJOINT BY AUTO infers them via static analysis; DISJOINT BY NONE disables. Overrides dbms.cypher.transactions.default subquery batch strategy . EXPLAIN / PROFILE shows keys in DISJOINT BY (...) on TransactionForeach . QPE basics Quantifier outside group: (pattern){N,M} . Groups start+end with node. REPEATABLE ELEMENTS needs bounded {m,n} . ACYCLIC implies nodes cannot repeat within a path (stronger than default DIFFERENT RELATIONSHIPS ). Match mode — add after MATCH : DIFFERENT RELATIONSHIPS (default) — each rel traversed once per path REPEATABLE ELEMENTS [2025.x] — nodes/rels revisitable; use for circular routes, weight optimized paths, constrained backtracking; requires bounded {m,n} Conditional CALL subqueries [2025.06] Use WHEN…THEN…ELSE for if else if write logic; mutually exclusive (first match wins). Not available pre 2025.06. Dynamic relationship types [2025.x] Spatial / Point Create POINT index: CREATE POINT INDEX name IF NOT EXISTS FOR (n:Place) ON (n.coords) Aggregation grouping keys Non aggregating expressions in RETURN / WITH are implicit grouping keys — GROUP BY optional: Explicit GROUP BY subclause on WITH / RETURN [2026.07, Cypher 25] states grouping keys explicitly — GQL aligned alternative to implicit grouping; implicit grouping stays valid: GROUP BY () = no grouping keys (one row); GROUP BY ALL = every non aggregating return item is a key. Grouping keys absent from the projection are not returned. Rules → [references/cypher syntax.md](references/cypher syntax.md). count(n) counts non null; count( ) counts rows including nulls. collect(DISTINCT expr) deduplicates. count() is faster than size(collect()) — count() reads the internal store; collect() builds a list first. ORDER BY / WHERE subclause expressions referencing a projection item more complex than a variable or var.prop are deprecated [2026.07] — alias the expression in the projection and order by the alias. Same for names that shadow an incoming variable. ORDER BY / WHERE may now call aggregation functions absent from the projection list when the projection clause already aggregates. Common Syntax Traps (top causes of broken queries) Wrong Right ORDER BY n.prop AS x DESC ORDER BY n.prop DESC ORDER BY preAggVar after agg RETURN Use RETURN alias count(r WHERE r.x=5) sum(CASE WHEN r.x=5 THEN 1 ELSE 0 END) UNWIND list AS x WHERE x 5 UNWIND list AS x WITH x WHERE x 5 least(a,b) / greatest(a,b) CASE WHEN a<b THEN a ELSE b END comment // comment shortestPath((a) [ ] (b)) SHORTEST 1 (a)(() [] ()){1,}(b) id(n) elementId(n) [:REL 1..5] (() [:REL] ()){1,5} CALL { WITH x ... } CALL (x) { ... } COLLECT { (a) [:R] (b) } COLLECT { MATCH ... RETURN b } SET n = {k:v} partial update SET n += {k:v} DELETE n with relationships DETACH DELETE n WHERE n.x = null WHERE n.x IS NULL toInteger(null) throws toIntegerOrNull(null) n.$key dynamic property n[$key] SET n:$label SET n:$($label) ZONED DATETIME = date(...) → 0 rows Use datetime(...) or .year accessor ISO string with Z suffix stored/compared as UTC Z ≠ UTC in Neo4j — Z is parsed as an offset, not the UTC timezone; planner and range indexes treat them differently. Explicitly coerce: datetime({datetime: datetime('2025 09 10T03:43:00Z'), timezone: 'UTC'}) ([neo4j 13519](https://github.com/neo4j/neo4j/issues/13519)) FOREACH ... RETURN UNWIND ... RETURN Full trap table → [references/syntax traps.md](references/syntax traps.md) Output Mode and Write Gate Default: parameterized queries. Return named properties, not full nodes or RETURN . Exception: schema/diagnostic queries ( CALL db.schema.visualization() , SHOW INDEXES YIELD , EXPLAIN ) where the object is the point. Validation workflow: 1. EXPLAIN before any write — catches syntax errors, missing indexes 2. New read: test with LIMIT 1 first 3. Write: verify read half as RETURN before replacing with SET / CREATE / DELETE 4. PROFILE to measure db hits; check for AllNodesScan , CartesianProduct , Eager Query API v2 (no driver needed — works for schema inspection, EXPLAIN, reads, writes): Write execution gate — only when agent executes (MCP/cypher shell/HTTP), NOT when generating for code/scripts/user to run: 1. Run EXPLAIN → report estimated rows affected 2. Wait for user confirmation before executing Version Gates Default to 2025.01 safe features when version unknown. Feature Min version Fallback CYPHER 25 , QPEs, CALL (x) {} 2025.01 require 2025+ Match modes ( DIFFERENT RELATIONSHIPS , REPEATABLE ELEMENTS ) 2025.01 require 2025+ Dynamic labels $($expr) , coll.sort() 2025.01 APOC or app side CONCURRENT TRANSACTIONS , REPORT STATUS 2025.01 drop / omit SEARCH clause (vector/fulltext) 2026.01 CALL db.index.vector.queryNodes(...) (deprecated 2026.04) ACYCLIC path mode (no repeated nodes in path) 2026.03 post filter with size(nodes(p)) = size(apoc.coll.toSet(nodes(p))) string.indexOf() , string.join() , string.regexReplace() 2026.05 apoc.text. or app side GROUP BY subclause on WITH / RETURN , cardinality() 2026.07 implicit grouping keys; size() / size(keys(map)) WHERE on procedure calls run against the system database 2026.07 filter rows client side GQL aliases: FOR = UNWIND , PROPERTY EXISTS = IS NOT NULL , IS [NOT] LABELED = n:Label ; function aliases ( local time , zoned datetime , duration between , collect list , etc.) 2026.02–04 GQL compliance only — use Cypher equivalents; full list → [references/cypher syntax.md](references/cypher syntax.md) GRAPH TYPE schema DDL ( ALTER CURRENT GRAPH TYPE SET/ADD/ALTER/DROP , SHOW CURRENT GRAPH TYPE ) 2026.02 (preview), GA 2026.06 Use individual CREATE CONSTRAINT / CREATE INDEX GROUP BY subclause on WITH / RETURN (explicit grouping keys, GQL alignment) 2026.07 Implicit grouping — list non aggregating expressions in the projection cardinality() — keys in a MAP, elements in a LIST, nodes+rels in a PATH 2026.07 size() for LIST/MAP keys, length() for PATH Aggregation functions in ORDER BY / WHERE that are not projection items (aggregating projection only) 2026.07 Project the aggregate as an alias, then order/filter on the alias WHERE after YIELD in procedure calls on the system database 2026.07 YIELD + RETURN , filter client side Performance EXPLAIN/PROFILE red flags: AllNodesScan CartesianProduct NodeByLabelScan Eager Fix Eager — three approaches (choose simplest that works): 1. Add specific labels to MATCH nodes to eliminate read/write ambiguity: MATCH (x:CallingPoint) instead of bare MATCH (x) when writing :City nodes 2. Collect first, then write : WITH collect(u) AS users UNWIND users AS u ... 3. CALL IN TRANSACTIONS : isolates each batch in its own tr