Chapter 09 · Constraints and Indexes: Range, Text, Point, Token, Full-Text, and Schema Enforcement
Full-Text Indexes, Analyzers, Eventual Consistency Options, and Query Procedures
Build lexical AtlasMart retrieval with explicit analyzer, score and freshness contracts.
Learning outcomes
AtlasMart product search now needs lexical relevance rather than only exact/range predicates. Full-text indexes are Lucene-backed semantic indexes: they tokenize string content, return query scores, support analyzers, and must be queried explicitly. Their score and freshness contracts differ from normal search-performance indexes.
Create node and relationship full-text indexes over one or more labels/types/properties.
Explain analyzers, tokenization and Lucene query behavior.
Distinguish synchronous index updates from the eventual-consistency option.
Query full-text indexes explicitly and interpret scores as query-specific relevance evidence.
Prove freshness when eventual consistency is enabled instead of assuming immediate visibility.
The mandatory lab continues Neo4j Community
2026.07.1, database neo4j, explicit
CYPHER 25 for version-sensitive examples,
authentication enabled, no mandatory APOC/GDS plugin, and the
AtlasMart identifiers/model established in Chapters 01–08.
Neo4j 5.26.30 remains the LTS comparison line.
Community currently supports node and relationship
property-uniqueness constraints. Property-existence,
property-type and key constraints—and Cypher 25 graph
types—are Enterprise-only; Enterprise examples in this chapter
are optional and are never presented as Community output.
This generation environment does not run Neo4j or Docker.
Commands were checked against current official documentation
but were not executed here. Expected plans, counts, index
states and scores are therefore described by invariant rather
than fabricated as captured output. All disposable data uses
labTag='ch09'; all disposable schema objects are
named ch09_*. Never drop an index/constraint
merely because its name looks similar to a lab object—verify
SHOW INDEXES/SHOW CONSTRAINTS first.
1. Full-text is not a bigger text index
A text index accelerates particular string predicates in
ordinary MATCH/WHERE planning. A
full-text index tokenizes the content of STRING or
LIST<STRING> properties and is queried
explicitly through
db.index.fulltext.queryNodes() or
queryRelationships(). It can cover multiple
labels/types and multiple properties.
CYPHER 25CREATE FULLTEXT INDEX ch09_content_fulltext IF NOT EXISTSFOR (n:Product) ON EACH [n.name,n.description]OPTIONS {indexConfig:{`fulltext.analyzer`:'english'}};CALL db.awaitIndex('ch09_content_fulltext',300);
CYPHER 25CALL db.index.fulltext.queryNodes('ch09_content_fulltext','camera')YIELD node,scoreRETURN node.productId AS productId,node.name AS name,scoreORDER BY score DESC;
2. Analyzer choice changes the token stream
The analyzer controls tokenization, normalization and stop-word
behavior. The current default is
standard-no-stop-words; this lab chooses
english deliberately. Use the built-in procedure to
inspect what is actually available in the running release before
freezing a language-specific configuration.
CYPHER 25CALL db.index.fulltext.listAvailableAnalyzers()YIELD analyzer,description,stopwordsRETURN analyzer,description,stopwordsORDER BY analyzer;
3. Scores are evidence for one retrieval request
The returned score is produced by the full-text search engine for the current index/query. It is not a normalized probability and should not be compared as if a score of 0.8 from one query/index has the same meaning as 0.8 from another. Evaluate ranking with judged queries and application relevance metrics.
CYPHER 25CALL db.index.fulltext.queryNodes( 'ch09_content_fulltext', 'camera OR battery', {limit:10}) YIELD node,scoreRETURN node.productId,node.name,scoreORDER BY score DESC;
4. Eventual-consistency mode moves index updates off the commit path
By default, full-text updates participate in normal synchronous
index update behavior. Setting
fulltext.eventually_consistent=true applies changes
asynchronously in a background thread. That can reduce
commit-path work, but a just-committed value may not be
immediately searchable. Freshness becomes an explicit
service-level contract.
CYPHER 25CREATE FULLTEXT INDEX ch09_review_async_fulltext IF NOT EXISTSFOR (r:Review) ON EACH [r.text]OPTIONS {indexConfig:{`fulltext.analyzer`:'english',`fulltext.eventually_consistent`:true}};CALL db.awaitIndex('ch09_review_async_fulltext',300);CREATE (:Review {reviewId:'R-9001',text:'Camera battery life is excellent',labTag:'ch09'});CALL db.index.fulltext.awaitEventuallyConsistentIndexRefresh();CALL db.index.fulltext.queryNodes('ch09_review_async_fulltext','excellent')YIELD node,scoreRETURN node.reviewId,node.text,score;
5. Deliberately wrong: assert immediate search freshness on an eventual index
The write transaction can commit successfully while the asynchronous index update is still queued. A test that performs an immediate query and treats a miss as data loss is testing the wrong contract. For deterministic lab assertions, call the refresh-wait procedure; for production, define tolerated freshness and monitor queue/backlog behavior rather than forcing synchronous waits into every request.
Check your understanding
- Why is a full-text index not automatically used by MATCH?
- What property types can current full-text indexes include?
- What does an analyzer change?
- Does a higher score mean “90% correct”?
- How do you make an eventual-consistency lab assertion deterministic?
Review the answers
1. It is a semantic index queried explicitly through full-text procedures.
2. STRING and LIST
3. How indexed/query text is tokenized/normalized and which stop-word/stemming behavior applies.
4. No. It is query/index-specific relevance evidence, not a probability.
5. Wait with db.index.fulltext.awaitEventuallyConsistentIndexRefresh() before asserting the new content is searchable.
Summary and next step
Full-text indexing is an explicit retrieval subsystem with its own analyzer, score and freshness contracts. The final lesson audits the whole schema and removes structures that have no demonstrated workload value.
Authoritative references
- Current Neo4j versions — Release/LTS snapshot used for this chapter.
- Constraints — Current constraint types and edition boundaries.
- Create constraints — Current uniqueness, existence, type and key syntax and backing-index behavior.
- Search-performance indexes — Range, text, point and token lookup index semantics.
- Show indexes — Index lifecycle, state, population and usage evidence.
- Full-text indexes — Full-text schema, analyzers, query procedures and eventual-consistency behavior.
- Built-in index procedures — db.awaitIndex(es) and full-text refresh/analyzer procedures.
- Full-text index configuration — Default analyzer and eventual-consistency queue configuration.
- Built-in full-text procedures — Current query, analyzer and refresh-wait signatures.