neo4j-contrib/neo4j-skills13 files

Neo4j Cypher Skill

Generates, optimizes, and validates Cypher 25 queries for Neo4j 2025.x and 2026.x.

Specification
Skill ID
neo4j-contrib/neo4j-skills/neo4j-cypher-skill
Publisher
neo4j-contrib
Repository
neo4j-skills
Installs
0
Files
13
Requires
Neo4j >= 2025.01 (safe baseline); Cypher 25
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

neo4j-contrib/neo4j-skills/neo4j-cypher-skillInstalls these files
  • README.md
  • SKILL.md
  • references/advanced-patterns.md
  • references/apoc.md
  • references/cypher-syntax.md
  • references/graph-type.md
  • references/indexes.md
  • references/performance.md
  • references/schema-guardrail.md
  • references/syntax-traps.md
  • scripts/define_schema.py
  • scripts/generate_schema.py
  • scripts/import_neo4j_schema.py

What this skill tells the agent

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

?KnownUnknown
<db-name>-schema.json found in projectUse it directly — skip live inspection—
Schema (from context or live DB)Use directlyRun Schema-First Protocol
Neo4j versionUse version featuresDefault to 2025.01 safe set
Executing (not generating)?Use EXPLAIN + write gateState query is unvalidated

Schema unknown + no tool → produce non-executable sketch outside a code block:

(<SOURCE_LABEL> {<KEY>: $value})-[:<REL_TYPE>]->(<TARGET_LABEL>)

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

ElementConvention
Node labelsPascalCase :Person
Rel typesSCREAMING_SNAKE_CASE :KNOWS
Properties/varscamelCase firstName
ClausesUPPERCASE MATCH
Booleans/nulllowercase true false null
Stringssingle-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.
  • 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).

  1. Schema in context → use it, skip inspection.
  1. Schema missing → run:
CALL db.schema.visualization() YIELD nodes, relationships RETURN nodes, relationships;
SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state WHERE state = 'ONLINE';
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties;
SHOW PROCEDURES YIELD name RETURN split(name,'.')[0] AS namespace, count(*) AS procedures;

Property types per label — check APOC first:

// If APOC available (preferred — use this):
CALL apoc.meta.schema() YIELD value RETURN value;

// No APOC AND database ≤ 100k nodes/rels only (expensive on large graphs):
CALL db.schema.nodeTypeProperties() YIELD nodeType, propertyName, propertyTypes, mandatory;
CALL db.schema.relTypeProperties() YIELD relType, propertyName, propertyTypes, mandatory;

Validate before returning any query: label exists · rel type+direction correct · property on that label · index ONLINE.


Key Patterns

MERGE

// MERGE on constrained key; set extras in ON CREATE/ON MATCH
CYPHER 25
MATCH (a:Person {id: $a}) MATCH (b:Person {id: $b})
MERGE (a)-[r:KNOWS]->(b)
  ON CREATE SET r.since = date()
  ON MATCH  SET r.lastSeen = date()

SET n = {} replaces all props. SET n += {} merges (safe partial update). Use += for updates.

WITH scope

CYPHER 25
MATCH (a:Person)-[:KNOWS]->(b:Person)
WITH a, count(*) AS friends   // b dropped here
WHERE friends > 5
RETURN a.name, friends ORDER BY friends DESC

Every var not listed in WITH is dropped. WITH * carries all forward.

Subqueries — cheat sheet

EXISTS { (a)-[:R]->(b) }                           // boolean check
COUNT  { (a)-[:R]->(b) WHERE a.x > 0 }             // count
COLLECT { MATCH (a)-[:R]->(b) RETURN b.name }       // collect list (full MATCH+RETURN required)
CALL (p) { MATCH (p)-[:ACTED_IN]->(m) RETURN m }    // correlated subquery (explicit import)
OPTIONAL CALL (p) { ... }                           // nullable subquery

CALL { WITH x ... } deprecated → CALL (x) { ... }. COLLECT {} returns exactly one column.

CALL IN TRANSACTIONS (bulk writes)

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
CALL (row) {
  MERGE (p:Person {id: row.id}) SET p += row
} IN TRANSACTIONS OF 1000 ROWS ON ERROR CONTINUE REPORT STATUS AS s

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:

CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///rels.csv' AS line
CALL (line) {
  MATCH (a:Movie {id: line.movieId}), (b:Person {id: line.personId})
  MERGE (b)-[:ACTED_IN]->(a)
} IN CONCURRENT TRANSACTIONS OF 1000 ROWS DISJOINT BY (line.movieId, line.personId)

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

CYPHER 25
MATCH SHORTEST 1 (a:Person {name:'Alice'})(()-[:KNOWS]->()){1,}(b:Person {name:'Bob'})
RETURN b.name

// ACYCLIC [2026.03] — no repeated nodes within a path (prevents cycles)
CYPHER 25
MATCH p = ACYCLIC (start:Router {name: $from})-[:LINK]-+(end:Router {name: $to})
RETURN [n IN nodes(p) | n.name] AS route
ORDER BY length(p) LIMIT 5

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

Use this skill in a project

Open any RiverX project, search the Skills panel for neo4j-contrib/neo4j-skills/neo4j-cypher-skill, and install it in one click.

Use this skill