Skip to main content

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)")

An old syntax is also supported:

graph.query("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)

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")

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")

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)")

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)

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)")

Deleting an index for a relationship type

For a relationship type, the index deletion syntax is:

graph.query("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")

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)

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 WHERE clauses
  • 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.PROFILE to 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.