Choosing Between Range and Full‑Text Indexes in Neo4j for Property Lookups
Learn how to decide between Neo4j range and full‑text indexes based on property type, query needs, and write overhead, with concrete creation and validation steps.
01 Nov 2025, 01:44 UTC

Decision: When to Use a Range Index vs a Full‑Text Index
You need to retrieve nodes by a property value. The choice affects query performance, write overhead, and the kinds of predicates you can support efficiently.
Constraints and Goals
- Property type: numeric, date, or short string for exact/range matches → favor range index.
- Need for keyword, partial, or phrase search on longer text → favor full‑text index.
- Write‑heavy workload: minimize index maintenance cost.
- Desire for automatic planner use without procedural calls.
Option Comparison
| Feature | Range Index | Full‑Text Index |
|---|---|---|
| Supported predicates | =, <, >, <=, >=, IN, IS NULL (automatically used) | Keyword, wildcard, phrase, fuzzy via db.index.fulltext.queryNodes |
| Lookup complexity | O(log n) B‑tree | Lucene‑based inverted index (approx. O(log n) + term scoring) |
| Automatic planner use | Yes – Cypher planner adds NodeIndexSeek | No – must call procedure explicitly |
| Storage overhead | Low (single value per node) | Higher (tokens, term frequencies, positions) |
| Write impact | Moderate (index update per property change) | Higher (analysis and tokenization per change) |
| Typical use case | Numeric IDs, timestamps, ages, short codes | Free‑text fields, names, descriptions, addresses |
Trade‑offs
Range indexes give the fastest look‑ups for equality and range scans and incur the least write penalty, but they cannot support partial‑match or linguistic searches. Full‑text indexes enable powerful text analysis (stemming, stop‑word removal, phrase queries) at the cost of extra storage, higher write latency, and the need to invoke a procedure in every query.
Implementation Example
Assume a Person label with properties age (integer) and name (string).
Create a Range Index on age
// Run in Neo4j Browser or cypher-shell
CREATE INDEX person_age IF NOT EXISTS
FOR (p:Person) ON (p.age);
Create a Full‑Text Index on name
// Procedure call – analyzer can be "standard", "english", etc.
CALL db.index.fulltext.createNodeIndex(
"personNameIdx",
["Person"],
["name"],
{ analyzer: "standard" }
);
Query using the Range Index (automatic)
// Equality or range predicate
MATCH (p:Person)
WHERE p.age >= 30
RETURN p.name, p.age
ORDER BY p.age;
Query using the Full‑Text Index (explicit)
// Prefix search – adjust term as needed
CALL db.index.fulltext.queryNodes("personNameIdx", "Jo*"
) YIELD node AS p
RETURN p.name, p.age;
Validation Steps
After creating indexes and running queries, verify that the planner is using the expected index type.
Check Range Index Usage
EXPLAIN
MATCH (p:Person)
WHERE p.age >= 30
RETURN p;
Look for a NodeIndexSeek operator labeled with the index name person_age in the output.
Check Full‑Text Index Usage
EXPLAIN
CALL db.index.fulltext.queryNodes("personNameIdx", "Jo*")
YIELD node RETURN node;
The plan should show a NodeFullTextIndexSeek operator for personNameIdx. If you see a NodeByLabelScan instead, the index is not being used (perhaps the query term does not match the analyzer).
Confirm Index State
SHOW INDEXES
YIELD name, type, state, labelsOrTypes, properties
WHERE name IN ["person_age", "personNameIdx"]
RETURN name, type, state, labelsOrTypes, properties;
Ensure state is ONLINE for both indexes.
Limitations and Practical Checks
- Full‑text indexes do not support numeric range predicates; you must keep a separate range index if you need both kinds of search on the same property.
- Write throughput drops proportionally to the number and complexity of indexes; monitor
dbms.query.transaction.metrics or use:sysinfoin Neo4j Browser to observe page‑cache and disk I/O. - The analyzer choice influences tokenization; test with sample data using
CALL db.index.fulltext.analyze("personNameIdx", "Your text")to verify expected tokens before committing to an analyzer.
By comparing the plan operators and execution times (via PROFILE instead of EXPLAIN), you can confirm whether the selected index delivers the expected performance for your workload.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.