hanamizuki/solopreneur/neo4j-cypher

Claude Code and agent skill by hanamizuki/solopreneur

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

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

Install

npx vskill@latest install hanamizuki/solopreneur/neo4j-cypher

SKILL.md

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)
  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:
cypher
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:

cypher
// 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
cypher
// 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
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
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
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
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
  • 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]
cypher
CYPHER 25
MATCH (move:Item {id: $id})
OPTIONAL MATCH (insertBefore:Item {id: $before})
OPTIONAL MATCH (insertAfter:Item {id: $after})
CALL (move, insertBefore, insertAfter) {
WHEN insertBefore IS NULL THEN {
MATCH (last:Item) WHERE NOT (last)-[:NEXT]->() AND last <> move
CREATE (last)-[:NEXT]->(move)
}
WHEN insertAfter IS NULL THEN {
CREATE (move)-[:NEXT]->(insertBefore)
}
ELSE {
CREATE (insertAfter)-[:NEXT]->(move)
CREATE (move)-[:NEXT]->(insertBefore)
}
}

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]
cypher
// Create/match/merge with dynamic rel type (must resolve to exactly one STRING)
CYPHER 25 CREATE (a:Node)-[:$($relType)]->(b:Node)
CYPHER 25 MATCH (a:Node)-[:$($relType)]->(b:Node) RETURN a.name, b.name
Spatial / Point
cypher
// WGS84 geographic point
SET n.coords = point({longitude: $lon, latitude: $lat})
// Distance in metres; requires POINT index for performance
MATCH (a:Place {name: $origin}) MATCH (b:Place)
RETURN b.name, point.distance(a.coords, b.coords) AS distM
ORDER BY distM LIMIT 10
// Bounding-box pre-filter (uses POINT index) then distance
MATCH (b:Place)
WHERE point.withinBBox(b.coords,
point({longitude: $west, latitude: $south}),
point({longitude: $east, latitude: $north}))
RETURN b.name, point.distance(b.coords, $origin) AS distM

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 — no GROUP BY needed:

cypher
// actor + director are grouping keys; count(*) is the aggregate
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)
RETURN a.name, d.name, count(*) AS collaborations
ORDER BY collaborations DESC

Explicit GROUP BY [2026.07, Cypher 25] states grouping keys — GQL-aligned alternative to implicit grouping:

cypher
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)
RETURN a.name, d.name, count(*) AS collaborations GROUP BY a.name, d.name
ORDER BY collaborations DESC

Subclause expressions (ORDER BY/WHERE) referencing projection items more complex than a variable or property access are deprecated [2026.07] — alias the expression in RETURN/WITH, then reference the alias.

count(n) counts non-null; count(*) counts rows including nulls. collect(DISTINCT expr) deduplicates. GROUP BY subclause [2026.07] states grouping keys explicitly on WITH/RETURN; implicit grouping stays valid. count() is faster than size(collect()) — count() reads the internal store; collect() builds a list first.

GROUP BY subclause on WITH/RETURN [2026.07, Cypher 25] states grouping keys explicitly (GQL alignment); implicit grouping still valid.

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)

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
145
Repository forks
9
7-day trend score
25

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/hanamizuki/solopreneur/neo4j-cypher/badge)](https://verified-skill.com/skills/hanamizuki/solopreneur/neo4j-cypher)

Something wrong with this skill or its page? Tell us.