Complete guide to querying NornicDB with Cypher - the graph query language.
- Introduction
- Basic Syntax
- Creating Data
- Reading Data
- Updating Data
- Deleting Data
- Pattern Matching
- Functions
- Aggregations
- Advanced Queries
Cypher is a declarative graph query language that allows you to express what data you want, not how to get it. It uses ASCII-art syntax to represent graph patterns.
- Nodes:
(n)- Entities in your graph - Relationships:
-[r]->- Connections between nodes - Labels:
(n:Person)- Types/categories - Properties:
{name: "Alice"}- Key-value data
() // Anonymous node
(n) // Node with variable
(n:Person) // Node with label
(n:Person:Employee) // Node with multiple labels
(n {name: "Alice"}) // Node with properties
(n:Person {name: "Alice", age: 30}) // Combined--> // Outgoing relationship
<-- // Incoming relationship
-- // Undirected relationship
-[r]-> // Relationship with variable
-[r:KNOWS]-> // Relationship with type
-[r:KNOWS {since: 2020}]-> // With properties
-[r:KNOWS|FOLLOWS]-> // Multiple types
-[*1..3]-> // Variable length (1-3 hops)// Single node
CREATE (alice:Person {name: "Alice", age: 30})
RETURN alice
// Multiple nodes
CREATE
(bob:Person {name: "Bob"}),
(carol:Person {name: "Carol"}),
(company:Company {name: "TechCorp"})
RETURN bob, carol, company// Create nodes and relationship together
CREATE (alice:Person {name: "Alice"})-[r:KNOWS {since: 2020}]->(bob:Person {name: "Bob"})
RETURN alice, r, bob
// Connect existing nodes
MATCH
(alice:Person {name: "Alice"}),
(bob:Person {name: "Bob"})
CREATE (alice)-[r:KNOWS]->(bob)
RETURN r// Create node if it doesn't exist
MERGE (alice:Person {name: "Alice"})
ON CREATE SET alice.created = timestamp()
ON MATCH SET alice.accessed = timestamp()
RETURN alice
// Create relationship if it doesn't exist
MATCH
(alice:Person {name: "Alice"}),
(bob:Person {name: "Bob"})
MERGE (alice)-[r:KNOWS]->(bob)
ON CREATE SET r.since = timestamp()
RETURN r// Find all nodes with label
MATCH (p:Person)
RETURN p
// Find nodes with properties
MATCH (p:Person {name: "Alice"})
RETURN p
// Find relationships
MATCH (p:Person)-[r:KNOWS]->(friend:Person)
RETURN p.name, friend.name
// Find paths
MATCH path = (alice:Person {name: "Alice"})-[:KNOWS*1..3]->(friend)
RETURN path// Simple conditions
MATCH (p:Person)
WHERE p.age > 30
RETURN p.name, p.age
// Multiple conditions
MATCH (p:Person)
WHERE p.age > 25 AND p.age < 40
RETURN p
// String matching
MATCH (p:Person)
WHERE p.name STARTS WITH "A"
RETURN p.name
// Regular expressions
MATCH (p:Person)
WHERE p.email =~ ".*@example\\.com"
RETURN p.name, p.email
// NULL checks
MATCH (p:Person)
WHERE p.email IS NOT NULL
RETURN p
// List membership
MATCH (p:Person)
WHERE p.name IN ["Alice", "Bob", "Carol"]
RETURN p// Return nodes
MATCH (p:Person)
RETURN p
// Return properties
MATCH (p:Person)
RETURN p.name, p.age
// Return with alias
MATCH (p:Person)
RETURN p.name AS personName, p.age AS personAge
// Return distinct
MATCH (p:Person)
RETURN DISTINCT p.city
// Return with expressions
MATCH (p:Person)
RETURN p.name, p.age, p.age + 10 AS ageIn10Years// Sort ascending
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age
// Sort descending
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
// Multiple sort keys
MATCH (p:Person)
RETURN p.name, p.age, p.city
ORDER BY p.city, p.age DESC// Limit results
MATCH (p:Person)
RETURN p
LIMIT 10
// Skip and limit (pagination)
MATCH (p:Person)
RETURN p
ORDER BY p.name
SKIP 20
LIMIT 10// Set single property
MATCH (alice:Person {name: "Alice"})
SET alice.age = 31
RETURN alice
// Set multiple properties
MATCH (alice:Person {name: "Alice"})
SET alice.age = 31, alice.city = "San Francisco"
RETURN alice
// Set from map
MATCH (alice:Person {name: "Alice"})
SET alice += {age: 31, city: "San Francisco"}
RETURN alice
// Add label
MATCH (alice:Person {name: "Alice"})
SET alice:Employee
RETURN labels(alice)
// Replace all properties
MATCH (alice:Person {name: "Alice"})
SET alice = {name: "Alice", age: 31}
RETURN alice// Remove property
MATCH (alice:Person {name: "Alice"})
REMOVE alice.email
RETURN alice
// Remove label
MATCH (alice:Person {name: "Alice"})
REMOVE alice:Employee
RETURN labels(alice)// Delete relationship
MATCH (alice:Person)-[r:KNOWS]->(bob:Person)
DELETE r
// Delete node (must have no relationships)
MATCH (bob:Person {name: "Bob"})
DELETE bob
// Delete node and relationships
MATCH (bob:Person {name: "Bob"})
DETACH DELETE bob
// Delete all data (⚠️ Use with caution!)
MATCH (n)
DETACH DELETE n// Direct relationship
MATCH (alice:Person)-[:KNOWS]->(friend:Person)
RETURN alice.name, friend.name
// Bidirectional
MATCH (alice:Person)-[:KNOWS]-(friend:Person)
RETURN alice.name, friend.name
// Multiple relationships
MATCH (alice:Person)-[:KNOWS]->(friend)-[:WORKS_AT]->(company:Company)
RETURN alice.name, friend.name, company.name// Friends of friends (2 hops)
MATCH (alice:Person {name: "Alice"})-[:KNOWS*2]-(fof:Person)
RETURN DISTINCT fof.name
// Up to 3 hops
MATCH (alice:Person {name: "Alice"})-[:KNOWS*1..3]-(connected:Person)
RETURN DISTINCT connected.name
// Any length (⚠️ Can be slow!)
MATCH (alice:Person {name: "Alice"})-[:KNOWS*]-(connected:Person)
RETURN DISTINCT connected.name// Shortest path between two nodes
MATCH path = shortestPath(
(alice:Person {name: "Alice"})-[:KNOWS*]-(dave:Person {name: "Dave"})
)
RETURN path, length(path)
// All shortest paths
MATCH path = allShortestPaths(
(alice:Person {name: "Alice"})-[:KNOWS*]-(dave:Person {name: "Dave"})
)
RETURN path// Optional match (like LEFT JOIN)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:WORKS_AT]->(c:Company)
RETURN p.name, c.nameMATCH (p:Person)
RETURN
toLower(p.name) AS lowercase,
toUpper(p.name) AS uppercase,
substring(p.name, 0, 3) AS first3,
replace(p.name, "Alice", "Alicia") AS replaced,
split(p.email, "@") AS emailParts,
trim(p.name) AS trimmedRETURN
abs(-5) AS absolute,
ceil(3.2) AS ceiling,
floor(3.8) AS floor,
round(3.14159, 2) AS rounded,
sqrt(16) AS squareRoot,
rand() AS randomRETURN
size([1,2,3,4,5]) AS listSize,
head([1,2,3]) AS first,
tail([1,2,3]) AS rest,
last([1,2,3]) AS lastElement,
range(1, 10) AS numbersRETURN
timestamp() AS currentTimestamp,
date() AS currentDate,
datetime() AS currentDateTime,
duration({days: 7}) AS oneWeekSee Cypher Functions Reference for complete list.
// Count nodes
MATCH (p:Person)
RETURN count(p) AS totalPeople
// Count relationships
MATCH ()-[r:KNOWS]->()
RETURN count(r) AS totalFriendships
// Count distinct
MATCH (p:Person)
RETURN count(DISTINCT p.city) AS uniqueCitiesMATCH (p:Person)
RETURN
sum(p.age) AS totalAge,
avg(p.age) AS averageAge,
min(p.age) AS youngest,
max(p.age) AS oldest// Collect into list
MATCH (p:Person)
RETURN collect(p.name) AS allNames
// Collect distinct
MATCH (p:Person)
RETURN collect(DISTINCT p.city) AS cities// Group by city
MATCH (p:Person)
RETURN p.city, count(p) AS peopleInCity
ORDER BY peopleInCity DESC
// Group by multiple fields
MATCH (p:Person)-[:WORKS_AT]->(c:Company)
RETURN c.name, p.city, count(p) AS employees
ORDER BY employees DESC// Filter aggregated results
MATCH (p:Person)
WITH p.city AS city, count(p) AS population
WHERE population > 10
RETURN city, population
// Multiple steps
MATCH (p:Person)-[:KNOWS]->(friend:Person)
WITH p, count(friend) AS friendCount
WHERE friendCount > 5
RETURN p.name, friendCount
ORDER BY friendCount DESC// Expand list into rows
UNWIND [1, 2, 3, 4, 5] AS number
RETURN number * 2 AS doubled
// Create multiple nodes from list
UNWIND ["Alice", "Bob", "Carol"] AS name
CREATE (p:Person {name: name})
RETURN pMATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN "Minor"
WHEN p.age < 65 THEN "Adult"
ELSE "Senior"
END AS ageGroup// Union (removes duplicates)
MATCH (p:Person)
RETURN p.name AS name
UNION
MATCH (c:Company)
RETURN c.name AS name
// Union all (keeps duplicates)
MATCH (p:Person)
RETURN p.name AS name
UNION ALL
MATCH (c:Company)
RETURN c.name AS name// Correlated subquery
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[:KNOWS]->(friend:Person)
RETURN count(friend) AS friendCount
}
RETURN p.name, friendCount// Bad: Hardcoded values
MATCH (p:Person {name: "Alice"})
RETURN p
// Good: Use parameters
MATCH (p:Person {name: $name})
RETURN p// Create index for better performance
CREATE INDEX person_name FOR (p:Person) ON (p.name)
CREATE INDEX person_email FOR (p:Person) ON (p.email)// Understand query plan
EXPLAIN MATCH (p:Person) WHERE p.age > 30 RETURN p
// Profile actual execution
PROFILE MATCH (p:Person)-[:KNOWS]->(friend) RETURN p, friend// Always limit for exploration
MATCH (p:Person)
RETURN p
LIMIT 100// DISTINCT can be expensive
MATCH (p:Person)
RETURN DISTINCT p.city
// Better: Use aggregation
MATCH (p:Person)
WITH p.city AS city
RETURN city- Cypher Functions Reference - Complete function list
- Graph Traversal - Advanced pattern matching
- Complete Examples - Full applications
- Performance Guide - Query optimization
Need more examples? → Complete Examples
Ready for advanced patterns? → Graph Traversal