Cypher Query Language¶
Uni supports a substantial subset of the OpenCypher query language, optimized for vectorized execution. This guide covers all supported syntax with practical examples.
Overview¶
Cypher is a declarative graph query language that uses ASCII-art patterns to describe graph structures:
// Pattern: (startNode)-[relationship]->(endNode)
MATCH (person:Person)-[:KNOWS]->(friend:Person)
WHERE person.age > 30
RETURN friend.name
Query Structure¶
A complete Cypher query follows this structure:
[MATCH clause] -- Pattern matching
[WHERE clause] -- Filtering
[WITH clause] -- Intermediate processing
[RETURN clause] -- Output projection
[ORDER BY clause] -- Sorting
[SKIP/LIMIT clause] -- Pagination
Example Query¶
MATCH (p:Paper)-[:AUTHORED_BY]->(a:Author)
WHERE p.year >= 2020 AND a.affiliation = 'MIT'
RETURN p.title, a.name, p.year
ORDER BY p.year DESC
LIMIT 10
MATCH Clause¶
The MATCH clause specifies graph patterns to find.
Node Patterns¶
// Any node
MATCH (n)
// Node with label
MATCH (p:Paper)
// Node with multiple labels
MATCH (n:Paper:Preprint)
// Node with variable binding
MATCH (paper:Paper)
RETURN paper.title
// Anonymous node (no variable)
MATCH (:Paper)-[:CITES]->(cited)
RETURN cited.title
Node Properties in Pattern¶
// Filter in pattern (equivalent to WHERE)
MATCH (p:Paper {year: 2023})
RETURN p.title
// Multiple properties
MATCH (p:Paper {year: 2023, venue: 'NeurIPS'})
RETURN p.title
Relationship Patterns¶
// Outgoing relationship
MATCH (a)-[:KNOWS]->(b)
// Incoming relationship
MATCH (a)<-[:KNOWS]-(b)
// Either direction
MATCH (a)-[:KNOWS]-(b)
// Any relationship type
MATCH (a)-[r]->(b)
// Relationship with variable
MATCH (p:Paper)-[c:CITES]->(cited:Paper)
RETURN c // Access edge properties
Multi-Hop Patterns¶
// 2-hop path
MATCH (a:Paper)-[:CITES]->(b:Paper)-[:CITES]->(c:Paper)
RETURN a.title, b.title, c.title
// Chain multiple relationships
MATCH (p:Paper)-[:AUTHORED_BY]->(a:Author)-[:AFFILIATED_WITH]->(u:University)
RETURN p.title, a.name, u.name
Variable-Length Paths¶
Variable-length path patterns allow traversing multiple hops in a single pattern.
// 1 to 3 hops
MATCH (a:Paper)-[:CITES*1..3]->(b:Paper)
RETURN a.title, b.title
// Exactly 2 hops
MATCH (a)-[:KNOWS*2]->(b)
// Any length (use with caution on large graphs)
MATCH (a)-[:KNOWS*]->(b)
// Zero or more hops
MATCH (a)-[:KNOWS*0..]->(b)
// With path variable
MATCH path = (a:Paper)-[:CITES*1..5]->(b:Paper)
WHERE a.title = 'Attention Is All You Need'
RETURN path, length(path) AS hops
WHERE Clause¶
The WHERE clause filters matched patterns.
Comparison Operators¶
// Equality
WHERE p.year = 2023
// Inequality
WHERE p.year <> 2020
WHERE p.year != 2020
// Comparison
WHERE p.year > 2020
WHERE p.year >= 2020
WHERE p.year < 2025
WHERE p.year <= 2025
Boolean Logic¶
// AND
WHERE p.year > 2020 AND p.venue = 'NeurIPS'
// OR
WHERE p.venue = 'NeurIPS' OR p.venue = 'ICML'
// NOT
WHERE NOT p.is_retracted
// Parentheses for grouping
WHERE (p.year > 2020 AND p.venue = 'NeurIPS') OR p.citations > 1000
NULL Handling¶
// Check for NULL
WHERE p.doi IS NULL
WHERE p.doi IS NOT NULL
// COALESCE (use default if NULL)
RETURN COALESCE(p.nickname, p.name) AS display_name
String Operations¶
// Starts with
WHERE p.title STARTS WITH 'Attention'
// Ends with
WHERE p.title ENDS WITH 'Networks'
// Contains (uses JSON FTS index if available)
WHERE p.title CONTAINS 'Transformer'
// Full-text search on JSON document column (requires JSON FTS index)
WHERE a._doc CONTAINS 'graph database'
// Path-specific JSON FTS search
WHERE a._doc.title CONTAINS 'graph'
// Regular expression matching
WHERE p.title =~ '.*Transform.*'
// Case-insensitive regex
WHERE p.email =~ '(?i)john@.*'
// Email pattern matching
WHERE p.email =~ '^[\\w.-]+@[\\w.-]+\\.\\w+$'
Note: The CONTAINS operator is automatically routed to JSON FTS indexes when the column has an FTS index, enabling BM25-based full-text search with relevance ranking.
Regex Notes:
- The =~ operator uses Rust's regex engine (similar to PCRE)
- NULL operands return NULL (Cypher semantics)
- Invalid regex patterns return an error
List Operations¶
// IN list
WHERE p.venue IN ['NeurIPS', 'ICML', 'ICLR']
// NOT IN
WHERE p.venue NOT IN ['Workshop', 'Demo']
Property Existence¶
RETURN Clause¶
The RETURN clause specifies output columns.
Basic Projections¶
// Single property
RETURN p.title
// Multiple properties
RETURN p.title, p.year, p.venue
// All properties (*)
RETURN p.*
// Entire node
RETURN p
Aliases¶
// AS keyword
RETURN p.title AS paper_title, a.name AS author_name
// Expression aliases
RETURN p.year + 1 AS next_year
Expressions¶
// Arithmetic
RETURN p.citations * 2 AS double_citations
// String concatenation
RETURN p.title + ' (' + p.venue + ')' AS formatted
// Conditional (CASE)
RETURN CASE
WHEN p.citations > 1000 THEN 'High Impact'
WHEN p.citations > 100 THEN 'Medium Impact'
ELSE 'Low Impact'
END AS impact_level
DISTINCT¶
// Remove duplicates
RETURN DISTINCT p.venue
// Distinct combinations
RETURN DISTINCT p.venue, p.year
Aggregations¶
Uni supports standard aggregation functions.
Aggregation Functions¶
| Function | Description | Example |
|---|---|---|
COUNT(*) |
Count all rows | RETURN COUNT(*) |
COUNT(x) |
Count non-null values | RETURN COUNT(p.doi) |
COUNT(DISTINCT x) |
Count distinct values | RETURN COUNT(DISTINCT p.venue) |
SUM(x) |
Sum numeric values | RETURN SUM(p.citations) |
AVG(x) |
Average | RETURN AVG(p.citations) |
MIN(x) |
Minimum | RETURN MIN(p.year) |
MAX(x) |
Maximum | RETURN MAX(p.year) |
COLLECT(x) |
Collect into list | RETURN COLLECT(p.title) |
Implicit Grouping¶
Non-aggregated columns become GROUP BY keys:
// Group by venue, count papers per venue
MATCH (p:Paper)
RETURN p.venue, COUNT(p) AS paper_count
ORDER BY paper_count DESC
Aggregation Examples¶
// Count papers per author
MATCH (p:Paper)-[:AUTHORED_BY]->(a:Author)
RETURN a.name, COUNT(p) AS papers, AVG(p.citations) AS avg_citations
ORDER BY papers DESC
// Most cited papers per venue
MATCH (p:Paper)
RETURN p.venue, MAX(p.citations) AS top_citations, COUNT(p) AS total
ORDER BY top_citations DESC
Window Functions¶
Window functions perform calculations across a set of rows related to the current row, without collapsing results like aggregations.
Basic Syntax¶
function_name(args) OVER (
[PARTITION BY partition_expr, ...]
[ORDER BY order_expr [ASC|DESC], ...]
)
Supported Window Functions¶
| Function | Description | Example |
|---|---|---|
row_number() |
Sequential row number | row_number() OVER (ORDER BY p.year) |
rank() |
Rank with gaps for ties | rank() OVER (ORDER BY p.citations DESC) |
dense_rank() |
Rank without gaps | dense_rank() OVER (ORDER BY p.citations DESC) |
sum() |
Running sum | sum(p.citations) OVER (ORDER BY p.year) |
avg() |
Running average | avg(p.citations) OVER (PARTITION BY p.venue) |
count() |
Running count | count(*) OVER (PARTITION BY p.venue) |
min() / max() |
Running min/max | max(p.citations) OVER (PARTITION BY p.author) |
Examples¶
Ranking within partitions:
MATCH (p:Paper)
RETURN p.title, p.venue, p.citations,
rank() OVER (PARTITION BY p.venue ORDER BY p.citations DESC) AS venue_rank
ORDER BY p.venue, venue_rank
Running totals:
MATCH (p:Paper)
WHERE p.author = 'Alice'
RETURN p.title, p.year, p.citations,
sum(p.citations) OVER (ORDER BY p.year) AS cumulative_citations
ORDER BY p.year
Top N per group:
MATCH (p:Paper)
WITH p, row_number() OVER (PARTITION BY p.venue ORDER BY p.citations DESC) AS rn
WHERE rn <= 3
RETURN p.venue, p.title, p.citations
ORDER BY p.venue, p.citations DESC
Scalar Functions¶
Uni provides a comprehensive set of scalar functions for data transformation.
String Functions¶
| Function | Description | Example |
|---|---|---|
toUpper(s) / upper(s) |
Convert to uppercase | RETURN toUpper('hello') → 'HELLO' |
toLower(s) / lower(s) |
Convert to lowercase | RETURN toLower('HELLO') → 'hello' |
trim(s) |
Remove leading/trailing whitespace | RETURN trim(' hi ') → 'hi' |
ltrim(s) |
Remove leading whitespace | RETURN ltrim(' hi') → 'hi' |
rtrim(s) |
Remove trailing whitespace | RETURN rtrim('hi ') → 'hi' |
substring(s, start, [len]) |
Extract substring | RETURN substring('hello', 1, 3) → 'ell' |
left(s, n) |
First n characters | RETURN left('hello', 2) → 'he' |
right(s, n) |
Last n characters | RETURN right('hello', 2) → 'lo' |
reverse(s) |
Reverse string | RETURN reverse('hello') → 'olleh' |
replace(s, old, new) |
Replace occurrences | RETURN replace('hello', 'l', 'x') → 'hexxo' |
split(s, delim) |
Split into list | RETURN split('a,b,c', ',') → ['a','b','c'] |
lpad(s, len, [pad]) |
Pad left to length | RETURN lpad('5', 3, '0') → '005' |
rpad(s, len, [pad]) |
Pad right to length | RETURN rpad('5', 3, '0') → '500' |
Math Functions¶
| Function | Description | Example |
|---|---|---|
abs(n) |
Absolute value | RETURN abs(-5) → 5 |
ceil(n) |
Round up | RETURN ceil(4.2) → 5 |
floor(n) |
Round down | RETURN floor(4.8) → 4 |
round(n) |
Round to nearest | RETURN round(4.5) → 5 |
sqrt(n) |
Square root | RETURN sqrt(16) → 4 |
sign(n) |
Sign (-1, 0, 1) | RETURN sign(-5) → -1 |
log(n) |
Natural logarithm | RETURN log(2.718) → ~1 |
log10(n) |
Base-10 logarithm | RETURN log10(100) → 2 |
exp(n) |
e^n | RETURN exp(1) → ~2.718 |
power(base, exp) / pow(base, exp) |
Exponentiation | RETURN power(2, 3) → 8 |
sin(n), cos(n), tan(n) |
Trigonometric | RETURN sin(0) → 0 |
List Functions¶
| Function | Description | Example |
|---|---|---|
size(list) |
Length of list/string/map | RETURN size([1,2,3]) → 3 |
head(list) |
First element | RETURN head([1,2,3]) → 1 |
tail(list) |
All but first element | RETURN tail([1,2,3]) → [2,3] |
last(list) |
Last element | RETURN last([1,2,3]) → 3 |
keys(map) |
Keys of a map | RETURN keys({a:1, b:2}) → ['a','b'] |
range(start, end, [step]) |
Generate number sequence | RETURN range(1, 5) → [1,2,3,4,5] |
Path Functions¶
| Function | Description | Example |
|---|---|---|
length(path) |
Number of relationships in path | RETURN length(path) |
nodes(path) |
List of nodes in path | RETURN nodes(path) |
relationships(path) |
List of relationships in path | RETURN relationships(path) |
Type Conversion Functions¶
| Function | Description | Example |
|---|---|---|
toInteger(x) |
Convert to integer | RETURN toInteger('42') → 42 |
toFloat(x) |
Convert to float | RETURN toFloat('3.14') → 3.14 |
toString(x) |
Convert to string | RETURN toString(42) → '42' |
toBoolean(x) |
Convert to boolean | RETURN toBoolean('true') → true |
Null Handling Functions¶
| Function | Description | Example |
|---|---|---|
coalesce(x, y, ...) |
First non-null value | RETURN coalesce(null, 'default') → 'default' |
nullif(a, b) |
Return null if a = b | RETURN nullif(5, 5) → null |
Example Usage¶
// String manipulation
MATCH (p:Paper)
RETURN toUpper(p.title) AS upper_title,
substring(p.abstract, 0, 100) AS abstract_preview
// Math operations
MATCH (p:Paper)
RETURN p.title, round(p.citations / 10.0) * 10 AS rounded_citations
// List operations
MATCH (a:Author)
WHERE size(a.affiliations) > 1
RETURN a.name, head(a.affiliations) AS primary_affiliation
// Type conversion
MATCH (p:Paper)
WHERE toInteger(p.year_str) > 2020
RETURN p.title
ORDER BY, SKIP, LIMIT¶
Sorting¶
// Ascending (default)
ORDER BY p.year
// Descending
ORDER BY p.year DESC
// Multiple columns
ORDER BY p.year DESC, p.title ASC
// By alias
RETURN p.title, p.citations AS cites
ORDER BY cites DESC
Pagination¶
// First 10 results
LIMIT 10
// Skip first 20, take next 10
SKIP 20
LIMIT 10
// Combined
RETURN p.title
ORDER BY p.year DESC
SKIP 100
LIMIT 25
CREATE Clause¶
Create new nodes and relationships.
Creating Nodes¶
// Simple node
CREATE (p:Paper {title: 'My Paper'})
// With properties
CREATE (p:Paper {
title: 'New Research',
year: 2024,
venue: 'ArXiv',
citations: 0
})
RETURN p
// Multiple nodes
CREATE (a:Author {name: 'Alice'}), (b:Author {name: 'Bob'})
Creating Relationships¶
// Between existing nodes
MATCH (p:Paper {title: 'Paper A'}), (a:Author {name: 'Alice'})
CREATE (p)-[:AUTHORED_BY {position: 1}]->(a)
// Create node and relationship together
CREATE (p:Paper {title: 'New Paper'})-[:AUTHORED_BY]->(a:Author {name: 'New Author'})
Returning Created Elements¶
WITH Clause¶
The WITH clause chains query parts together.
Intermediate Processing¶
// Filter after aggregation
MATCH (p:Paper)-[:AUTHORED_BY]->(a:Author)
WITH a, COUNT(p) AS paper_count
WHERE paper_count > 5
RETURN a.name, paper_count
Subquery-like Behavior¶
// First find top authors, then their papers
MATCH (a:Author)<-[:AUTHORED_BY]-(p:Paper)
WITH a, COUNT(p) AS papers
ORDER BY papers DESC
LIMIT 10
MATCH (a)<-[:AUTHORED_BY]-(recent:Paper)
WHERE recent.year >= 2022
RETURN a.name, recent.title
UNWIND Clause¶
Expand a list into individual rows.
// Expand literal list
UNWIND [1, 2, 3] AS num
RETURN num
// Expand list property
MATCH (p:Paper)
UNWIND p.keywords AS keyword
RETURN keyword, COUNT(*) AS usage
ORDER BY usage DESC
// Create multiple from list
UNWIND ['Alice', 'Bob', 'Charlie'] AS name
CREATE (a:Author {name: name})
CALL Clause (Procedures)¶
Invoke built-in procedures.
Vector Search¶
// Basic vector search
CALL uni.vector.query('Paper', 'embedding', $query_vector, 10)
YIELD node, distance
RETURN node.title, distance
// With threshold
CALL uni.vector.query('Paper', 'embedding', $query_vector, 100, NULL, 0.3)
YIELD node, distance
WHERE distance < 0.2
RETURN node.title, distance
Schema Introspection¶
// List labels
CALL uni.schema.labels()
YIELD label
RETURN label
// List relationship types
CALL uni.schema.relationshipTypes()
YIELD relationshipType
RETURN relationshipType
// List indexes
CALL uni.schema.indexes()
YIELD name, type, label, properties, state
RETURN *
Index Management¶
Create Indexes¶
// Scalar index
CREATE INDEX ON :Paper(year)
// Named index
CREATE INDEX paper_year FOR (p:Paper) ON (p.year)
// Vector index
CREATE VECTOR INDEX paper_embeddings
FOR (p:Paper) ON p.embedding
OPTIONS {type: 'hnsw'}
// JSON Full-Text index
CREATE JSON FULLTEXT INDEX article_fts
FOR (a:Article) ON _doc
OPTIONS {with_positions: true}
Drop Indexes¶
Show Indexes¶
Query Parameters¶
Use parameters to avoid injection and improve plan caching.
Parameter Syntax¶
CLI Usage¶
uni query "MATCH (p:Paper) WHERE p.year = \$year RETURN p" \
--params '{"year": 2023}' \
--path ./storage
Session Variables¶
Session variables ($session.*) provide scoped context for multi-tenant queries. They're set when creating a session and accessible in all queries within that session.
// Create session with tenant context
let session = db.session()
.set("tenant_id", "acme-corp")
.set("user_id", "user-123")
.build();
// All queries have access to $session.* variables
let results = session.query(r#"
MATCH (d:Document)
WHERE d.tenant_id = $session.tenant_id
RETURN d.title AS title
"#).await?;
Session variables use the $session.property_name syntax and are resolved at query execution time.
Temporal Queries¶
Uni provides functions for querying temporal (time-based) data with validity ranges.
validAt Function¶
Check if a node or edge was valid at a specific point in time:
// Basic validAt usage
MATCH (e:Event)
WHERE uni.temporal.validAt(e, 'valid_from', 'valid_to', datetime($time))
RETURN e.name, e.valid_from, e.valid_to
Parameters:
- e: Node or edge to check
- 'valid_from': Property name for start time
- 'valid_to': Property name for end time
- datetime($time): Point in time to check
Semantics: valid_from <= time < valid_to (half-open interval)
VALID_AT Macro¶
For convenience, use the VALID_AT macro with default property names:
// Macro form (uses 'valid_from' and 'valid_to' by default)
MATCH (e:Event)
WHERE e VALID_AT datetime('2024-06-15T12:00:00Z')
RETURN e.name
Custom Property Names¶
Specify custom property names if your schema differs:
// With custom property names
MATCH (c:Contract)
WHERE c VALID_AT(datetime($check_time), 'start_date', 'end_date')
RETURN c.name, c.value
Temporal Query Patterns¶
Find currently valid records:
MATCH (e:Employee)
WHERE uni.temporal.validAt(e, 'hire_date', 'termination_date', datetime())
RETURN e.name, e.department
Find records valid at a historical point:
MATCH (p:Price)
WHERE p.product_id = $product_id
AND uni.temporal.validAt(p, 'effective_from', 'effective_to', datetime('2024-01-01'))
RETURN p.amount
Temporal edges:
MATCH (e:Employee)-[r:WORKS_IN]->(d:Department)
WHERE uni.temporal.validAt(r, 'start_date', 'end_date', datetime($as_of_date))
RETURN e.name, d.name
Open-ended validity (NULL end date):
// Records with NULL valid_to are considered currently valid
MATCH (m:Membership)
WHERE m.valid_to IS NULL
OR m.valid_to > datetime()
RETURN m
DELETE Clause¶
Remove nodes and relationships from the graph.
Deleting Relationships¶
// Delete a specific relationship
MATCH (a:Author)-[r:AUTHORED]->(p:Paper)
WHERE a.name = 'Alice' AND p.title = 'Old Paper'
DELETE r
// Delete all relationships of a type
MATCH ()-[r:DEPRECATED]->()
DELETE r
Deleting Nodes¶
// Delete a node (must have no relationships)
MATCH (p:Paper {title: 'Deleted Paper'})
DELETE p
// Delete node and all its relationships (DETACH)
MATCH (p:Paper {title: 'Deleted Paper'})
DETACH DELETE p
// Delete multiple nodes
MATCH (p:Paper)
WHERE p.year < 1990
DETACH DELETE p
Conditional Deletion¶
// Delete only if condition is met
MATCH (p:Paper)
WHERE p.citations = 0 AND p.year < 2000
DETACH DELETE p
RETURN COUNT(*) AS deleted_count
UNION Clause¶
Combine results from multiple queries.
UNION (Distinct)¶
// Combine results, removing duplicates
MATCH (a:Author {affiliation: 'MIT'})
RETURN a.name AS name
UNION
MATCH (a:Author {affiliation: 'Stanford'})
RETURN a.name AS name
UNION ALL (Keep Duplicates)¶
// Combine results, keeping all rows
MATCH (p:Paper)-[:CITES]->(:Paper {title: 'Paper A'})
RETURN p.title AS title
UNION ALL
MATCH (p:Paper)-[:CITES]->(:Paper {title: 'Paper B'})
RETURN p.title AS title
Multi-way UNION¶
// Combine multiple queries
MATCH (a:Author) WHERE a.h_index > 50
RETURN a.name AS name, 'high_impact' AS category
UNION
MATCH (a:Author) WHERE a.papers > 100
RETURN a.name AS name, 'prolific' AS category
UNION
MATCH (a:Author) WHERE a.citations > 10000
RETURN a.name AS name, 'highly_cited' AS category
WITH RECURSIVE Clause¶
WITH RECURSIVE enables Common Table Expressions (CTEs) for complex recursive queries.
// Find all transitive citations (papers cited by papers cited by...)
WITH RECURSIVE citation_chain AS (
-- Base case: direct citations
MATCH (start:Paper {title: 'Original Paper'})-[:CITES]->(cited:Paper)
RETURN cited, 1 AS depth
UNION
-- Recursive case: citations of citations
MATCH (prev)-[:CITES]->(cited:Paper)
WHERE prev IN citation_chain AND depth < 5
RETURN cited, depth + 1
)
SELECT cited.title, depth FROM citation_chain
Alternative: Variable-Length Paths
For simpler recursive traversals, variable-length path patterns (*1..N) are often more concise:
EXPLAIN Clause¶
Inspect the query execution plan without running the query.
Basic EXPLAIN¶
// Show query plan
EXPLAIN MATCH (p:Paper)-[:CITES]->(cited:Paper)
WHERE p.year > 2020
RETURN cited.title, COUNT(*) AS citation_count
ORDER BY citation_count DESC
LIMIT 10
Understanding the Plan¶
The EXPLAIN output shows: - Scan operations: How data is accessed (index scan, full scan) - Filter operations: Where predicates are applied - Join operations: How patterns are matched - Aggregation: Grouping and aggregation steps - Sort/Limit: Final ordering and pagination - Index usage: Which indexes are used and why - Cost estimates: Estimated rows and query cost
EXPLAIN Output Example¶
Plan:
├─ Scan: Paper
│ ├─ Filter: year > 2020
│ │ └─ Index: paper_year_btree (BTREE) ✓
│ ├─ Estimated rows: 500
│ └─ Cost: 12.5
├─ Expand: CITES (OUTGOING)
│ ├─ Estimated paths: 2500
│ └─ Cost: 125.0
└─ Target: Paper
└─ Estimated rows: 2500
Index Usage:
✓ Paper.year → paper_year_btree (BTREE)
✗ Paper.venue → paper_venue_idx (not in filter)
Total estimated cost: 137.5
PROFILE Clause¶
PROFILE executes the query and returns runtime statistics alongside results.
// Execute with profiling
let (results, profile) = db.profile(
"MATCH (p:Person) WHERE p.age > 25 RETURN p.name"
).await?;
println!("Total time: {}ms", profile.total_time_ms);
println!("Rows scanned: {}", profile.rows_scanned);
println!("Peak memory: {} bytes", profile.peak_memory_bytes);
// Per-operator statistics
for op in &profile.operators {
println!("{}: {}ms, {} rows", op.name, op.time_ms, op.rows_produced);
}
PROFILE Output¶
The profile includes: - total_time_ms: Total query execution time - rows_scanned: Number of rows read from storage - peak_memory_bytes: Maximum memory used during execution - operators: Per-operator breakdown with timing and row counts
Common Query Patterns¶
Find Connected Nodes¶
MATCH (start:Paper {title: 'Attention Is All You Need'})
MATCH (start)-[:CITES]->(cited)
RETURN cited.title, cited.year
ORDER BY cited.year DESC
Bidirectional Relationships¶
// Papers that cite each other
MATCH (a:Paper)-[:CITES]->(b:Paper)-[:CITES]->(a)
RETURN a.title, b.title
Shortest Path¶
The shortestPath function finds the shortest path between two nodes.
// Basic shortest path
MATCH path = shortestPath((a:Author {name: 'Alice'})-[:COAUTHOR*]-(b:Author {name: 'Bob'}))
RETURN path
// With hop constraints (minimum and maximum hops)
MATCH path = shortestPath((a:Person)-[:KNOWS*2..6]-(b:Person))
WHERE a.name = 'Alice' AND b.name = 'Bob'
RETURN path, length(path) AS hops
// Minimum hops only (at least 2 hops)
MATCH path = shortestPath((a)-[:FOLLOWS*2..]-(b))
RETURN path
// Maximum hops only (at most 5 hops)
MATCH path = shortestPath((a)-[:KNOWS*..5]-(b))
RETURN path
// Zero-length path (source equals target with min_hops=0)
MATCH path = shortestPath((a)-[:KNOWS*0..3]-(a))
RETURN path
Hop Constraint Semantics:
- * or *1.. — Unlimited hops (default: 1 to ∞)
- *2..6 — Between 2 and 6 hops
- *..5 — At most 5 hops (1 to 5)
- *3.. — At least 3 hops (3 to ∞)
- *0.. — Zero or more hops (allows source == target)
Degree Counting¶
// Count outgoing relationships
MATCH (p:Paper)-[c:CITES]->()
RETURN p.title, COUNT(c) AS out_degree
ORDER BY out_degree DESC
Top-K with Ties¶
Graph Algorithms¶
Uni provides 36 high-performance graph algorithms available via the algo namespace.
Centrality Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| PageRank | algo.pageRank |
Importance based on incoming links |
| Betweenness | algo.betweenness |
Nodes on shortest paths |
| Closeness | algo.closeness |
Average distance to all nodes |
| Degree Centrality | algo.degreeCentrality |
Connection count |
| Harmonic Centrality | algo.harmonicCentrality |
Harmonic mean distance |
| Eigenvector Centrality | algo.eigenvectorCentrality |
Influence via neighbors |
| Katz Centrality | algo.katzCentrality |
Weighted path influence |
// PageRank
CALL algo.pageRank(['Paper'], ['CITES'])
YIELD nodeId, score
RETURN nodeId, score
ORDER BY score DESC
// Betweenness Centrality
CALL algo.betweenness(['Author'], ['COAUTHOR'], true, 100)
YIELD nodeId, score
// Closeness Centrality
CALL algo.closeness(['Station'], ['CONNECTS'])
YIELD nodeId, score
Community Detection Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| Louvain | algo.louvain |
Modularity-based community detection |
| Label Propagation | algo.labelPropagation |
Fast community detection |
| WCC | algo.wcc |
Weakly connected components |
| SCC | algo.scc |
Strongly connected components |
| K-Core | algo.kCore |
K-core decomposition |
| Triangle Count | algo.triangleCount |
Count triangles per node |
// Louvain
CALL algo.louvain(['User'], ['INTERACTS'])
YIELD nodeId, communityId
// Label Propagation
CALL algo.labelPropagation(['User'], ['INTERACTS'])
YIELD nodeId, communityId
// Weakly Connected Components
CALL algo.wcc(['User'], ['INTERACTS'])
YIELD nodeId, componentId
// Strongly Connected Components
CALL algo.scc(['Task'], ['DEPENDS_ON'])
YIELD nodeId, componentId
// K-Core Decomposition
CALL algo.kCore(['User'], ['FRIEND'], 3)
YIELD nodeId, coreNumber
// Triangle Count
CALL algo.triangleCount(['User'], ['FRIEND'])
YIELD nodeId, triangleCount
Path Finding Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| Dijkstra | algo.dijkstra |
Shortest weighted path |
| Bellman-Ford | algo.bellmanFord |
Shortest path with negative weights |
| A* | algo.astar |
Heuristic shortest path |
| Bidirectional Dijkstra | algo.bidirectionalDijkstra |
Two-way shortest path |
| K-Shortest Paths | algo.kShortestPaths |
Multiple shortest paths |
| All Simple Paths | algo.allSimplePaths |
All paths between nodes |
| APSP | algo.allPairsShortestPath |
All pairs shortest path |
// Dijkstra shortest path
CALL algo.dijkstra(['Station'], ['CONNECTS'], $startId, $endId, 'distance')
YIELD path, cost
// K-Shortest Paths
CALL algo.kShortestPaths(['Station'], ['CONNECTS'], $startId, $endId, 5)
YIELD path, cost
// All Pairs Shortest Path
CALL algo.allPairsShortestPath(['Station'], ['CONNECTS'])
YIELD sourceNodeId, targetNodeId, distance
Similarity & Traversal Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| Node Similarity | algo.nodeSimilarity |
Jaccard similarity |
| Random Walk | algo.randomWalk |
Random graph traversal |
| Maximal Cliques | algo.maximalCliques |
Find all maximal cliques |
| Graph Coloring | algo.graphColoring |
Vertex coloring |
// Node Similarity (Jaccard)
CALL algo.nodeSimilarity(['Product'], ['PURCHASED'])
YIELD node1, node2, similarity
// Random Walk
CALL algo.randomWalk(['Page'], ['LINKS'], 10, 5)
YIELD path
Structural Analysis Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| Topological Sort | algo.topologicalSort |
DAG ordering |
| Cycle Detection | algo.cycleDetection |
Find cycles |
| Bipartite Check | algo.bipartiteCheck |
Test bipartiteness |
| Bridges | algo.bridges |
Find bridge edges |
| Articulation Points | algo.articulationPoints |
Find cut vertices |
| Elementary Circuits | algo.elementaryCircuits |
Find all simple cycles |
// Topological sort (for DAGs)
CALL algo.topologicalSort(['Task'], ['DEPENDS_ON'])
YIELD nodeId, order
// Find articulation points
CALL algo.articulationPoints(['Router'], ['CONNECTS'])
YIELD nodeId
Flow & Matching Algorithms¶
| Algorithm | Procedure | Description |
|---|---|---|
| Maximum Matching | algo.maxMatching |
Maximum cardinality matching |
| Minimum Spanning Tree | algo.mst |
MST using Prim/Kruskal |
| Dinic's Algorithm | algo.dinic |
Maximum flow |
| Ford-Fulkerson | algo.fordFulkerson |
Maximum flow |
// Minimum Spanning Tree
CALL algo.mst(['City'], ['ROAD'], 'distance')
YIELD edge, cost
// Maximum Matching
CALL algo.maxMatching(['Worker', 'Job'], ['CAN_DO'])
YIELD node1, node2
Schema DDL¶
Uni supports Data Definition Language (DDL) statements for managing schema.
Create Label (Vertex Type)¶
// Create a label with properties
CREATE LABEL Paper (
title STRING NOT NULL,
year INT32,
abstract STRING,
embedding VECTOR(768)
)
// Create label with default values
CREATE LABEL Author (
name STRING NOT NULL,
h_index INT32 DEFAULT 0,
active BOOLEAN DEFAULT true
)
Create Edge Type¶
// Create edge type with source/destination constraints
CREATE EDGE TYPE AUTHORED FROM Author TO Paper (
position INT32,
corresponding BOOLEAN DEFAULT false
)
// Edge type between multiple label types
CREATE EDGE TYPE COLLABORATES FROM Author TO Author (
papers_count INT32
)
Alter Schema¶
// Add a property to an existing label
ALTER LABEL Paper ADD abstract STRING
// Drop a property
ALTER LABEL Paper DROP deprecated_field
// Rename a property
ALTER LABEL Paper RENAME old_name TO new_name
Drop Schema Elements¶
// Drop a label (must have no vertices)
DROP LABEL TempLabel
// Drop an edge type
DROP EDGE TYPE OLD_RELATIONSHIP
Constraints¶
Define and manage data integrity constraints.
Create Constraints¶
// Unique constraint
CREATE CONSTRAINT paper_doi_unique
FOR (p:Paper) REQUIRE p.doi IS UNIQUE
// Existence constraint (property must be present)
CREATE CONSTRAINT author_name_exists
FOR (a:Author) REQUIRE a.name IS NOT NULL
// Check constraint (custom validation)
CREATE CONSTRAINT paper_year_valid
FOR (p:Paper) REQUIRE p.year >= 1900 AND p.year <= 2100
Show Constraints¶
Drop Constraints¶
Database Management¶
Administrative commands and procedures for database maintenance.
BACKUP¶
Create a backup of the database to a specified destination.
// Backup to local path
BACKUP TO './backups/backup_2026_01_17'
// Backup to S3
BACKUP TO 's3://my-bucket/backups/backup_2026_01_17'
CHECKPOINT¶
Flush L0 to L1 so recent writes become part of a durable snapshot boundary.
VACUUM¶
Flush and compact all L1 runs into L2 for storage hygiene.
Compaction¶
// Trigger storage compaction
CALL uni.admin.compact()
YIELD files_compacted, bytes_before, bytes_after, duration_ms
// Check compaction status
CALL uni.admin.compactionStatus()
YIELD l1_runs, l1_size_bytes, in_progress, pending
Snapshots¶
// Create a named snapshot
CALL uni.admin.snapshot.create('before_migration')
YIELD snapshot_id
// List available snapshots
CALL uni.admin.snapshot.list()
YIELD snapshot_id, name, created_at, version_hwm
// Restore to a snapshot
CALL uni.admin.snapshot.restore('before_migration')
YIELD status
Schema Introspection¶
// List all labels
CALL uni.schema.labels()
YIELD label, propertyCount, nodeCount, indexCount
RETURN label, nodeCount
// List all relationship types
CALL uni.schema.relationshipTypes()
YIELD type, sourceLabels, targetLabels, propertyCount
RETURN type
// List all edge types (alias)
CALL uni.schema.edgeTypes()
YIELD type, sourceLabels, targetLabels, propertyCount
// List all indexes
CALL uni.schema.indexes()
YIELD name, type, label, properties, state
WHERE type = 'VECTOR'
RETURN name, label
// List all constraints
CALL uni.schema.constraints()
YIELD name, type, label, properties, enabled
RETURN name, type
// Get detailed label info
CALL uni.schema.labelInfo('Paper')
YIELD property, dataType, nullable, indexed, unique
RETURN property, dataType
Schema DDL Procedures¶
Create and modify schema at runtime via procedures:
// Create a new label
CALL uni.schema.createLabel('Product', {
properties: {
name: { type: 'STRING', nullable: false },
price: { type: 'FLOAT64' },
embedding: { type: 'VECTOR' }
}
})
// Create an edge type
CALL uni.schema.createEdgeType('PURCHASED', ['Customer'], ['Product'], {
properties: {
quantity: { type: 'INT32' },
timestamp: { type: 'TIMESTAMP' }
}
})
// Create an index
CALL uni.schema.createIndex('Product', 'name', { type: 'BTREE' })
// Create a vector index
CALL uni.schema.createIndex('Product', 'embedding', {
type: 'VECTOR'
})
// Create a constraint
CALL uni.schema.createConstraint('Product', 'UNIQUE', ['sku'])
// Drop operations
CALL uni.schema.dropLabel('TempLabel')
CALL uni.schema.dropEdgeType('OLD_RELATIONSHIP')
CALL uni.schema.dropIndex('Product', 'old_index')
CALL uni.schema.dropConstraint('Product', 'constraint_name')
CRDT Properties¶
Uni supports CRDT (Conflict-free Replicated Data Type) properties for distributed, eventually-consistent data. CRDT properties can be defined in the schema and manipulated via Cypher.
Schema Definition¶
Define CRDT properties in your schema:
{
"properties": {
"Paper": {
"title": { "type": "String", "nullable": false },
"view_count": { "type": "Crdt", "crdt_type": "GCounter" },
"tags": { "type": "Crdt", "crdt_type": "ORSet" },
"metadata": { "type": "Crdt", "crdt_type": "LWWMap" }
}
}
}
Supported CRDT Types¶
| Type | Description | Cypher Operations |
|---|---|---|
GCounter |
Grow-only counter | Increment only |
GSet |
Grow-only set | Add only |
ORSet |
Add/remove set (add-wins) | Add, remove |
LWWRegister |
Single value (last-writer-wins) | Set value |
LWWMap |
Key-value map (per-key LWW) | Put, remove keys |
Rga |
Ordered sequence | Insert, delete at position |
Querying CRDT Properties¶
// Read CRDT values (returns materialized value)
MATCH (p:Paper {id: 'paper_001'})
RETURN p.view_count, p.tags
// GCounter returns total count
// ORSet returns list of visible elements
// LWWMap returns map of non-tombstoned entries
Updating CRDT Properties¶
CRDT updates use special syntax to ensure conflict-free merging:
// Increment a GCounter
MATCH (p:Paper {id: 'paper_001'})
SET p.view_count = crdt.increment(p.view_count, 1)
// Add to an ORSet
MATCH (p:Paper {id: 'paper_001'})
SET p.tags = crdt.orset_add(p.tags, 'machine-learning')
// Remove from an ORSet
MATCH (p:Paper {id: 'paper_001'})
SET p.tags = crdt.orset_remove(p.tags, 'deprecated-tag')
// Update LWWMap
MATCH (p:Paper {id: 'paper_001'})
SET p.metadata = crdt.map_put(p.metadata, 'reviewed', true)
CRDT Functions¶
| Function | CRDT Types | Description |
|---|---|---|
crdt.gcounter() |
GCounter | Create new counter |
crdt.increment(counter, n) |
GCounter | Increment by n |
crdt.value(counter) |
GCounter | Get current value |
crdt.gset() |
GSet | Create new grow-only set |
crdt.gset_add(set, elem) |
GSet | Add element |
crdt.orset() |
ORSet | Create new add-wins set |
crdt.orset_add(set, elem) |
ORSet | Add element |
crdt.orset_remove(set, elem) |
ORSet | Remove element |
crdt.contains(set, elem) |
GSet, ORSet | Check membership |
crdt.elements(set) |
GSet, ORSet | Get all elements |
crdt.lww() |
LWWRegister | Create new register |
crdt.lww_set(register, value) |
LWWRegister | Set value |
crdt.lww_get(register) |
LWWRegister | Get current value |
crdt.lww_map() |
LWWMap | Create new map |
crdt.map_put(map, key, value) |
LWWMap | Put key-value |
crdt.map_get(map, key) |
LWWMap | Get value by key |
crdt.rga() |
Rga | Create new sequence |
crdt.rga_to_list(rga) |
Rga | Convert to list |
Distributed Sync¶
CRDT properties enable conflict-free synchronization across distributed nodes:
┌─────────────┐ ┌─────────────┐
│ Node A │◄───────►│ Node B │
│ │ │ │
│ increment │ │ increment │
│ view_count │ │ view_count │
│ +5 │ │ +3 │
└──────┬──────┘ └──────┬──────┘
│ │
└───────────┬───────────┘
│
▼
┌─────────────┐
│ Merged │
│ view_count │
│ = 8 │
└─────────────┘
For detailed CRDT semantics and merge behavior, see CRDT Types.
Cypher Feature Status¶
| Feature | Status |
|---|---|
| MATCH (basic patterns) | Stable |
| WHERE (comparisons, boolean) | Stable |
| RETURN, ORDER BY, LIMIT, SKIP | Stable |
| Aggregations (COUNT, SUM, AVG, MIN, MAX, COLLECT) | Stable |
| Window Functions (OVER clause) | Stable |
| Scalar Functions (40+ functions) | Stable |
| CREATE (nodes and relationships) | Stable |
| WITH clause | Stable |
| UNWIND | Stable |
| CALL procedures | Stable |
| Index management | Stable |
| JSON Full-Text Search (CONTAINS) | Stable |
Variable-length paths (*1..3) |
Stable |
| MERGE | Stable |
| SET / REMOVE | Stable |
| DELETE / DETACH DELETE | Stable |
| UNION / UNION ALL | Stable |
| EXPLAIN (with index usage) | Stable |
Temporal Queries (uni.temporal.validAt, VALID_AT) |
Stable |
| Schema DDL (CREATE/ALTER/DROP LABEL) | Stable |
Schema DDL Procedures (uni.schema.createLabel, etc.) |
Stable |
| Constraints (UNIQUE, EXISTS, CHECK) | Stable |
| Composite Key Constraints | Stable |
Graph Algorithms (uni.algo.*) |
Stable |
| CRDT Properties | Stable |
| Database Management (BACKUP, COPY, compaction) | Stable |
| Snapshots (create, list, restore) | Stable |
Schema Introspection (uni.schema.*) |
Stable |
| Inverted Index (ANY IN pattern) | Stable |
| OPTIONAL MATCH | Stable |
| Subqueries (EXISTS, CALL) | Stable |
| WITH RECURSIVE (CTEs) | Stable |
| PROFILE (runtime statistics) | Stable |
Session Variables ($session.*) |
Stable |
| Multi-label Nodes | Stable |
Regular Expression Matching (=~) |
Stable |
shortestPath with Hop Constraints (*1..5) |
Stable |
| BTree Index STARTS WITH Optimization | Stable |
Next Steps¶
- Vector Search — Semantic similarity queries
- Data Ingestion — Bulk import and streaming writes
- Performance Tuning — Query optimization strategies
- Troubleshooting — Common issues and solutions