Comprehensive guide for writing modern Neo4j Cypher queries. Essential for text2cypher MCP tools and LLMs generating Cypher queries. Covers removed/deprecated syntax, modern replacements, CALL subqueries for reads, COLLECT patterns, sorting best practices, Quantified Path Patterns (QPP) for efficient graph traversal, and WRITE patterns (MERGE, SET, constraints) for graph construction.
This skill helps generate Neo4j Cypher queries using modern syntax patterns and avoiding deprecated features. It covers both read operations (traversal, aggregation) and write operations (MERGE, constraints, provenance tracking).
Quick Compatibility Check
When generating Cypher queries, immediately avoid these REMOVED features:
❌ id() function → Use elementId()
❌ Implicit grouping keys → Use explicit WITH clauses
❌ Pattern expressions for lists → Use pattern comprehension or COLLECT subqueries
❌ Repeated relationship variables → Use unique variable names
❌ Automatic list to boolean coercion → Use explicit checks
Core Principles for Query Generation
Use modern syntax patterns - QPP for complex traversals, CALL subqueries for complex reads
Optimize during traversal - Filter early within patterns, not after expansion
相關技能
Always filter nulls when sorting - Add IS NOT NULL checks for sorted properties
Explicit is better than implicit - Always use explicit grouping and type checking
Critical Sorting Rule
ALWAYS filter NULL values when sorting:
// WRONG - May include null values
MATCH (n:Node)
RETURN n.name, n.value
ORDER BY n.value
// CORRECT - Filter nulls before sorting
MATCH (n:Node)
WHERE n.value IS NOT NULL
RETURN n.name, n.value
ORDER BY n.value
Query Pattern Selection Guide
For Simple Queries
Use standard Cypher patterns with modern syntax:
MATCH (n:Label {property: value})
WHERE n.otherProperty IS :: STRING
RETURN n
For Variable-Length Paths
Consider Quantified Path Patterns (QPP) for better performance:
// Instead of: MATCH (a)-[*1..5]->(b)
// Use: MATCH (a)-[]-{1,5}(b)
// With filtering:
MATCH (a)((n WHERE n.active)-[]->(m)){1,5}(b)
For Aggregations
Use COUNT{}, EXISTS{}, and COLLECT{} subqueries:
MATCH (p:Person)
WHERE count{(p)-[:KNOWS]->()} > 5
RETURN p.name,
exists{(p)-[:MANAGES]->()} AS isManager
For Complex Read Operations
Use CALL subqueries for sophisticated data retrieval:
MATCH (d:Department)
CALL (d) {
MATCH (d)<-[:WORKS_IN]-(p:Person)
WHERE p.salary IS NOT NULL // Filter nulls
WITH p ORDER BY p.salary DESC
LIMIT 3
RETURN collect(p.name) AS topEarners
}
RETURN d.name, topEarners
// Old: WHERE exists((n)-[:REL]->())
// Modern: WHERE EXISTS {MATCH (n)-[:REL]->()}
// Also valid: WHERE exists{(n)-[:REL]->()}
Element IDs
// Old: WHERE id(n) = 123
// Modern: WHERE elementId(n) = "4:abc123:456"
// Note: elementId returns a string, not integer
Sorting with Null Handling
// Always add null check
MATCH (n:Node)
WHERE n.sortProperty IS NOT NULL
RETURN n
ORDER BY n.sortProperty
// Or use NULLS LAST
MATCH (n:Node)
RETURN n
ORDER BY n.sortProperty NULLS LAST