Range Index
FalkorDB supports single-property indexes for node labels and for relationship type. String, numeric, and geospatial data types can be indexed.
Supported Data Types
Range indexes support the following data types:
- String: Text values for exact matching and range queries
- Numeric: Integer and floating-point numbers for range comparisons
- Geospatial: Point data types for location-based queries
- Arrays: Single-property arrays containing scalar values (integers, floats, strings)
Note: Complex types like nested arrays, maps, or vectors are not supported for range indexing.
Creating an index for a node label
For a node label, the index creation syntax is:
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)")
await graph.query("CREATE INDEX FOR (p:Person) ON (p.age)");
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)").execute().await?;
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)");
GRAPH.QUERY DEMO_GRAPH "CREATE INDEX FOR (p:Person) ON (p.age)"
An old syntax is also supported:
graph.query("CREATE INDEX ON :Person(age)")
await graph.query("CREATE INDEX ON :Person(age)");
graph.query("CREATE INDEX ON :Person(age)").execute().await?;
graph.query("CREATE INDEX ON :Person(age)");
GRAPH.QUERY DEMO_GRAPH "CREATE INDEX ON :Person(age)"
After an index is explicitly created, it will automatically be used by queries that reference that label and any indexed property in a filter.
result = graph.explain("MATCH (p:Person) WHERE p.age > 80 RETURN p")
print(result)
# Output:
# Results
# Project
# Index Scan | (p:Person)
const result = await graph.explain("MATCH (p:Person) WHERE p.age > 80 RETURN p");
console.log(result);
// Output:
// Results
// Project
// Index Scan | (p:Person)
let result = graph.explain("MATCH (p:Person) WHERE p.age > 80 RETURN p").execute().await?;
println!("{}", result);
// Output:
// Results
// Project
// Index Scan | (p:Person)
String result = graph.explain("MATCH (p:Person) WHERE p.age > 80 RETURN p");
System.out.println(result);
// Output:
// Results
// Project
// Index Scan | (p:Person)
GRAPH.EXPLAIN DEMO_GRAPH "MATCH (p:Person) WHERE p.age > 80 RETURN p"
1) "Results"
2) " Project"
3) " Index Scan | (p:Person)"
This can significantly improve the runtime of queries with very specific filters. An index on :employer(name), for example, will dramatically benefit the query:
result = graph.query("MATCH (:Employer {name: 'Dunder Mifflin'})-[:EMPLOYS]->(p:Person) RETURN p")
const result = await graph.query("MATCH (:Employer {name: 'Dunder Mifflin'})-[:EMPLOYS]->(p:Person) RETURN p");
let result = graph.query("MATCH (:Employer {name: 'Dunder Mifflin'})-[:EMPLOYS]->(p:Person) RETURN p").execute().await?;
ResultSet result = graph.query("MATCH (:Employer {name: 'Dunder Mifflin'})-[:EMPLOYS]->(p:Person) RETURN p");
GRAPH.QUERY DEMO_GRAPH
"MATCH (:Employer {name: 'Dunder Mifflin'})-[:EMPLOYS]->(p:Person) RETURN p"
An example of utilizing a geospatial index to find Employer nodes within 5 kilometers of Scranton are:
result = graph.query("WITH point({latitude:41.4045886, longitude:-75.6969532}) AS scranton MATCH (e:Employer) WHERE distance(e.location, scranton) < 5000 RETURN e")
const result = await graph.query("WITH point({latitude:41.4045886, longitude:-75.6969532}) AS scranton MATCH (e:Employer) WHERE distance(e.location, scranton) < 5000 RETURN e");
let result = graph.query("WITH point({latitude:41.4045886, longitude:-75.6969532}) AS scranton MATCH (e:Employer) WHERE distance(e.location, scranton) < 5000 RETURN e").execute().await?;
ResultSet result = graph.query("WITH point({latitude:41.4045886, longitude:-75.6969532}) AS scranton MATCH (e:Employer) WHERE distance(e.location, scranton) < 5000 RETURN e");
GRAPH.QUERY DEMO_GRAPH
"WITH point({latitude:41.4045886, longitude:-75.6969532}) AS scranton MATCH (e:Employer) WHERE distance(e.location, scranton) < 5000 RETURN e"
Geospatial indexes can currently only be leveraged with < and <= filters; matching nodes outside the given radius are matched using conventional traversal.
Creating an index for a relationship type
For a relationship type, the index creation syntax is:
graph.query("CREATE INDEX FOR ()-[f:FOLLOW]-() ON (f.created_at)")
await graph.query("CREATE INDEX FOR ()-[f:FOLLOW]-() ON (f.created_at)");
graph.query("CREATE INDEX FOR ()-[f:FOLLOW]-() ON (f.created_at)").execute().await?;
graph.query("CREATE INDEX FOR ()-[f:FOLLOW]-() ON (f.created_at)");
GRAPH.QUERY DEMO_GRAPH "CREATE INDEX FOR ()-[f:FOLLOW]-() ON (f.created_at)"
Then the execution plan for using the index:
result = graph.explain("MATCH (p:Person {id: 0})-[f:FOLLOW]->(fp) WHERE 0 < f.created_at AND f.created_at < 1000 RETURN fp")
print(result)
# Output:
# Results
# Project
# Edge By Index Scan | [f:FOLLOW]
# Node By Index Scan | (p:Person)
const result = await graph.explain("MATCH (p:Person {id: 0})-[f:FOLLOW]->(fp) WHERE 0 < f.created_at AND f.created_at < 1000 RETURN fp");
console.log(result);
// Output:
// Results
// Project
// Edge By Index Scan | [f:FOLLOW]
// Node By Index Scan | (p:Person)
let result = graph.explain("MATCH (p:Person {id: 0})-[f:FOLLOW]->(fp) WHERE 0 < f.created_at AND f.created_at < 1000 RETURN fp").execute().await?;
println!("{}", result);
// Output:
// Results
// Project
// Edge By Index Scan | [f:FOLLOW]
// Node By Index Scan | (p:Person)
String result = graph.explain("MATCH (p:Person {id: 0})-[f:FOLLOW]->(fp) WHERE 0 < f.created_at AND f.created_at < 1000 RETURN fp");
System.out.println(result);
// Output:
// Results
// Project
// Edge By Index Scan | [f:FOLLOW]
// Node By Index Scan | (p:Person)
GRAPH.EXPLAIN DEMO_GRAPH "MATCH (p:Person {id: 0})-[f:FOLLOW]->(fp) WHERE 0 < f.created_at AND f.created_at < 1000 RETURN fp"
1) "Results"
2) " Project"
3) " Edge By Index Scan | [f:FOLLOW]"
4) " Node By Index Scan | (p:Person)"
This can significantly improve the runtime of queries that traverse super nodes or when we want to start traverse from relationships.
Deleting an index for a node label
For a node label, the index deletion syntax is:
graph.query("DROP INDEX ON :Person(age)")
await graph.query("DROP INDEX ON :Person(age)");
graph.query("DROP INDEX ON :Person(age)").execute().await?;
graph.query("DROP INDEX ON :Person(age)");
GRAPH.QUERY DEMO_GRAPH "DROP INDEX ON :Person(age)"
Deleting an index for a relationship type
For a relationship type, the index deletion syntax is:
graph.query("DROP INDEX ON :FOLLOW(created_at)")
await graph.query("DROP INDEX ON :FOLLOW(created_at)");
graph.query("DROP INDEX ON :FOLLOW(created_at)").execute().await?;
graph.query("DROP INDEX ON :FOLLOW(created_at)");
GRAPH.QUERY DEMO_GRAPH "DROP INDEX ON :FOLLOW(created_at)"
Array Indices
FalkorDB supports indexing on array properties containing scalar values (e.g., integers, floats, strings), enabling efficient lookups for elements within such arrays.
Note: Complex types like nested arrays, maps, or vectors are not supported for indexing.
The following example demonstrates how to index and search an array property:
# Create a node with an array property
graph.query("CREATE (:Person {samples: [-21, 30.5, 0, 90, 3.14]})")
# Create an index on the array property
graph.query("CREATE INDEX FOR (p:Person) ON (p.samples)")
# Use the index to search for nodes containing a specific value in the array
result = graph.query("MATCH (p:Person) WHERE 90 IN p.samples RETURN p")
// Create a node with an array property
await graph.query("CREATE (:Person {samples: [-21, 30.5, 0, 90, 3.14]})");
// Create an index on the array property
await graph.query("CREATE INDEX FOR (p:Person) ON (p.samples)");
// Use the index to search for nodes containing a specific value in the array
const result = await graph.query("MATCH (p:Person) WHERE 90 IN p.samples RETURN p");
// Create a node with an array property
graph.query("CREATE (:Person {samples: [-21, 30.5, 0, 90, 3.14]})").execute().await?;
// Create an index on the array property
graph.query("CREATE INDEX FOR (p:Person) ON (p.samples)").execute().await?;
// Use the index to search for nodes containing a specific value in the array
let result = graph.query("MATCH (p:Person) WHERE 90 IN p.samples RETURN p").execute().await?;
// Create a node with an array property
graph.query("CREATE (:Person {samples: [-21, 30.5, 0, 90, 3.14]})");
// Create an index on the array property
graph.query("CREATE INDEX FOR (p:Person) ON (p.samples)");
// Use the index to search for nodes containing a specific value in the array
ResultSet result = graph.query("MATCH (p:Person) WHERE 90 IN p.samples RETURN p");
# Create a node with an array property
GRAPH.QUERY DEMO_GRAPH "CREATE (:Person {samples: [-21, 30.5, 0, 90, 3.14]})"
# Create an index on the array property
GRAPH.QUERY DEMO_GRAPH "CREATE INDEX FOR (p:Person) ON (p.samples)"
# Use the index to search for nodes containing a specific value in the array
GRAPH.QUERY DEMO_GRAPH "MATCH (p:Person) WHERE 90 IN p.samples RETURN p"
Verifying Index Usage
To verify that an index is being used by your query, use GRAPH.EXPLAIN before and after creating the index:
# Before creating the index
result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p")
print(result) # Shows: Label Scan | (p:Person)
# Create the index
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)")
# After creating the index
result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p")
print(result) # Now shows: Index Scan | (p:Person)
// Before creating the index
let result = await graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p");
console.log(result); // Shows: Label Scan | (p:Person)
// Create the index
await graph.query("CREATE INDEX FOR (p:Person) ON (p.age)");
// After creating the index
result = await graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p");
console.log(result); // Now shows: Index Scan | (p:Person)
// Before creating the index
let result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p").execute().await?;
println!("{}", result); // Shows: Label Scan | (p:Person)
// Create the index
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)").execute().await?;
// After creating the index
let result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p").execute().await?;
println!("{}", result); // Now shows: Index Scan | (p:Person)
// Before creating the index
String result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p");
System.out.println(result); // Shows: Label Scan | (p:Person)
// Create the index
graph.query("CREATE INDEX FOR (p:Person) ON (p.age)");
// After creating the index
result = graph.explain("MATCH (p:Person) WHERE p.age > 30 RETURN p");
System.out.println(result); // Now shows: Index Scan | (p:Person)
# Before creating the index
GRAPH.EXPLAIN DEMO_GRAPH "MATCH (p:Person) WHERE p.age > 30 RETURN p"
# Output shows: Label Scan | (p:Person)
# Create the index
GRAPH.QUERY DEMO_GRAPH "CREATE INDEX FOR (p:Person) ON (p.age)"
# After creating the index
GRAPH.EXPLAIN DEMO_GRAPH "MATCH (p:Person) WHERE p.age > 30 RETURN p"
# Output now shows: Index Scan | (p:Person)
Index Management
Listing Existing Indexes
To view all indexes in your graph, use the db.indexes() procedure:
CALL db.indexes()
This returns information about all indexes including their type (RANGE), entity type (node/relationship), labels, and properties.
Performance Tradeoffs and Best Practices
When to Use Range Indexes
Range indexes are ideal for:
- Filtering by specific values: Queries with equality filters (e.g.,
WHERE p.name = 'Alice') - Range queries: Numeric or string comparisons (e.g.,
WHERE p.age > 30,WHERE p.name >= 'A' AND p.name < 'B') - Geospatial queries: Finding entities within a certain distance
- Array membership: Checking if a value exists in an array property
Performance Considerations
Benefits:
- Dramatically improves query performance for filtered searches
- Reduces the number of nodes/relationships that need to be scanned
- Enables efficient range scans and point lookups
Costs:
- Write overhead: Every insert or update to an indexed property requires updating the index
- Storage: Indexes consume additional memory and disk space
- Maintenance: Index structures need to be maintained during graph modifications
Recommendations:
- Index properties that are frequently used in
WHEREclauses - Avoid indexing properties that are rarely queried or have high write frequency
- For properties with very few distinct values (low cardinality), indexes may not provide significant benefits
- Monitor query performance with
GRAPH.PROFILEto validate index effectiveness
Example: Profiling Index Performance
# Profile query to see actual execution metrics
GRAPH.PROFILE DEMO_GRAPH "MATCH (p:Person) WHERE p.age > 30 RETURN p"
This shows detailed timing information and confirms whether the index was used.