How CCH Works
A classic contraction hierarchy bakes topology and edge weights together, making it expensive to rebuild when weights change. CCH decouples the work into three phases:- Preprocessing (topology only) — computes a nested-dissection elimination order and a chordal “upward graph”. This phase depends only on the graph structure, not on the weights, so it is reused across many weight updates.
- Customization (weights only) — applies the concrete edge weights to the precomputed structure, filling in shortcut weights. This is fast and re-runs whenever weights change.
- Query — a rank-pruned bidirectional Dijkstra over the hierarchy, followed by “unpacking” shortcuts back into the original edges to return a genuine path.
Creating a CCH index
A CCH index is created with DDL over a relationship pattern, naming the single edge property that holds the weight:ROAD relationships carry a w (distance/cost) property:
CREATE CCH INDEX returns indices_created = 1.
Indexing multiple relationship types
CCH is the only FalkorDB index type that may span several relationship types in a single index. This is useful when a route can traverse more than one kind of edge — for example roads and ferries:ROAD|FERRY and FERRY|ROAD name the same index, and ROAD|ROAD is just ROAD. Two indexes over the same relationship types but different weight properties are distinct.
Notes and constraints:
- A CCH index is only supported on relationships — a node pattern such as
(n:Label)is rejected. - Exactly one property — the edge weight — may be indexed.
- Relationship types and the weight attribute referenced at creation are created on demand if they do not yet exist (as with any
CREATE INDEX). - Edge direction is respected: a directed edge is never traversed backwards during build or query. Model bidirectional roads as two directed edges.
- When the weight property is missing on an edge, its weight defaults to
1. Parallel edges between the same pair collapse to the cheapest one, and self-loops are ignored. - Creating a second CCH index with the same key returns an error:
a CCH index already exists over these relationship types and weight attribute.
Querying a CCH index
A CCH index is queried with thedb.idx.cch.query procedure. Bind the source and target nodes with a MATCH, then pass them along with the index key:
pathWeight(DOUBLE) — the total weight of the shortest path.path(PATH) — the shortest path, fully unpacked into the original relationships (no shortcut edges appear in the result).
relTypes (order and duplicates do not matter):
relTypes and weightProp you pass must match an existing index key exactly — a query for ['ROAD'] will not fall back to a ['ROAD', 'FERRY'] index.
db.idx.cch.query is a read-only procedure, so it can also be issued through GRAPH.RO_QUERY. (CREATE/DROP CCH INDEX are write operations and are rejected on read-only connections.)
Query behavior:
- When
sourceNodeandtargetNodeare the same node, the result ispathWeight = 0and a single-node path. - When the target is unreachable from the source, the procedure returns no rows (it is not an error).
- The returned
pathWeightmatches the weight computed byalgo.SPpaths; where the shortest path is unique the paths are identical, and where there are ties either valid shortest path may be returned.
Deleting a CCH index
Drop a CCH index with the matching relationship pattern and weight property:no CCH index over these relationship types and weight attribute.
Incremental Maintenance
A CCH index keeps itself up to date as the underlying graph changes — you do not need to rebuild it manually. Pending updates are coalesced and applied automatically when a query commits, so a query always reads the last-committed hierarchy. FalkorDB chooses the cheapest maintenance strategy for each kind of change:- Edge weight change → a scoped re-customization of only the affected shortcuts (fast, weights-only).
- Edge added between a pair that already has a shortcut → a weight-only re-customization; an edge that introduces a new adjacency triggers a full rebuild.
- Edge deleted → the shortcut is retained and re-seeded; deletions accumulate toward a staleness threshold, after which the index rebuilds to reclaim stale structure.
- Node added or removed → a full rebuild, since the node id-space changes.
Index Management
Listing CCH Indexes
To view all indexes (including CCH) in your graph, use thedb.indexes() procedure:
entitytype:RELATIONSHIPtypes: the weight property mapped to['CCH']— for example{w: ['CCH']}properties: the weight property, e.g.['w']label: the deduplicated set of indexed relationship types, comma-joined in the index’s internal order (by relationship-type id, which is not necessarily alphabetical), e.g."ROAD,FERRY"
Verifying CCH Index Usage
Because a CCH index is queried explicitly throughdb.idx.cch.query, you can confirm the plan with GRAPH.EXPLAIN:
Persistence and Replication
A CCH index survives restarts and follows replicas automatically:- RDB: the full hierarchy is serialized with the graph, so a reloaded index answers queries immediately with no rebuild — and still without any shortcut edges in the graph.
- Replication / AOF: index creation and drops replicate as definition-only effects; each replica builds and maintains its own hierarchy. Weight changes replicate and re-customize the replica’s index. A full replica synchronization ships the complete hierarchy inside the RDB.
Performance Tradeoffs and Best Practices
When to Use CCH Indexes
CCH indexes are ideal for:- Route planning: repeated shortest-path queries over road, rail, or transit networks
- Logistics and delivery: cheapest-route lookups over weighted distribution graphs
- Network/utility graphs: least-cost paths where edges carry a numeric cost or latency
- Interactive applications: when point-to-point queries must return in microseconds
Performance Considerations
Benefits:- Microsecond-scale point-to-point shortest-path queries after the index is built
- Cheap re-customization when only edge weights change — topology preprocessing is reused
- Keeps the user graph clean — no shortcut edges or helper properties are written
- Self-maintaining, fully persisted to RDB, and replicated
- Build time: the initial preprocessing and customization take time proportional to the graph size
- Memory: the hierarchy is held in memory alongside the graph (counted under
indices_sz_mbinGRAPH.MEMORY USAGE) - Rebuild on topology change: adding new adjacencies, adding/removing nodes, or crossing the deletion staleness threshold triggers a full rebuild
- Create a CCH index when the same weighted graph is queried for many source/target pairs
- Model one-way connections as directed edges and bidirectional ones as two directed edges
- Store the weight on the indexed property; edges without it are treated as weight
1 - Keep separate indexes per metric (e.g. distance vs. travel-time) by using different weight properties
- Batch large topology changes together to amortize the resulting rebuild
See Also
- Range Index — for numeric and string range queries
- Full-text Index — for keyword and text-based search
- Vector Index — for semantic similarity search
Tip: Use a CCH index for repeated weighted shortest-path (routing) queries, a range index for exact or range-based property lookups, a full-text index for keyword search, and a vector index for semantic similarity search.
Frequently Asked Questions
What does CCH stand for?
What does CCH stand for?
CCH stands for Customizable Contraction Hierarchies, a routing technique that precomputes a hierarchy over a weighted graph so point-to-point shortest-path queries run in microseconds. It separates topology preprocessing from weight customization, so weight changes stay cheap.
How do I query a CCH index?
How do I query a CCH index?
Bind the source and target nodes with a
MATCH, then call db.idx.cch.query({sourceNode: a, targetNode: b, relTypes: ['ROAD'], weightProp: 'w'}) YIELD pathWeight, path. It returns the total path weight and the full path, unpacked into the original relationships.Does a CCH index add shortcut edges to my graph?
Does a CCH index add shortcut edges to my graph?
No. The hierarchy lives inside the index object. Nothing — no shortcut edges, ranks, or helper properties — is written into your graph, and the index maintains itself as the graph changes.
Can a CCH index cover more than one relationship type?
Can a CCH index cover more than one relationship type?
Yes. CCH is the only FalkorDB index type that may span multiple relationship types in a single index, e.g.
CREATE CCH INDEX FOR ()-[e:ROAD|FERRY]->() ON (e.w). The relationship-type set plus the weight property forms the index key, which is order-agnostic and deduplicated.What happens when the target is unreachable, or equals the source?
What happens when the target is unreachable, or equals the source?
An unreachable target returns no rows (not an error). When the source and target are the same node, the query returns
pathWeight = 0 and a single-node path.What weight is used when an edge has no weight property?
What weight is used when an edge has no weight property?
A missing weight defaults to
1. Parallel edges between the same pair collapse to the cheapest one, and self-loops are ignored.Do I need to rebuild the index after changing the graph?
Do I need to rebuild the index after changing the graph?
No. The index updates incrementally: weight changes trigger a cheap re-customization, while structural changes (new adjacencies, node add/remove, or many deletions) trigger an automatic rebuild. Updates are applied when a query commits.