How to Use Fulltext Indexes for Text Search in Neomodel: A Complete Guide
Neomodel enables full-text search by declaring FulltextIndex on node properties, automatically creating indexes via db.install_labels(), and querying through FulltextFilter in the .filter() API.
Fulltext indexes for text search in neomodel provide native Lucene-powered search capabilities directly through the Object Graph Mapper (OGM). By annotating properties with FulltextIndex and using FulltextFilter in queries, you can execute high-performance text searches without writing raw Cypher.
Declaring Fulltext Indexes on Node Properties
To enable full-text search, declare a FulltextIndex instance on any StringProperty in your node definition.
The FulltextIndex Class
The FulltextIndex class is defined in neomodel/properties.py (lines 47-66). It stores index configuration including the analyzer and consistency model.
from neomodel import StructuredNode, StringProperty, FulltextIndex
class Article(StructuredNode):
title = StringProperty(fulltext_index=FulltextIndex())
content = StringProperty(fulltext_index=FulltextIndex(eventually_consistent=True))
Configuration Options
analyzer: Specifies the Lucene analyzer (default:standard-no-stop-words).eventually_consistent: WhenTrue, allows the index to lag behind writes for better performance.
Creating Indexes in Neo4j
Indexes are not created automatically on model definition. You must explicitly install labels to trigger index creation.
Installing Labels with db.install_labels()
Call db.install_labels() to generate the CREATE FULLTEXT INDEX Cypher statements. This method invokes Database._create_node_fulltext_index in neomodel/sync_/database.py (lines 1077-1100).
from neomodel import db
# Run once during application initialization
db.install_labels(Article)
Generated Cypher Syntax
Behind the scenes, neomodel executes Cypher similar to:
CREATE FULLTEXT INDEX fulltext_index_Article_title
FOR (n:Article) ON EACH [n.title]
OPTIONS {
indexConfig: {
`fulltext.analyzer`: 'standard-no-stop-words',
`fulltext.eventually_consistent': false
}
}
Version Requirements
Fulltext index creation requires Neo4j 5.16 or newer. The Database._create_node_fulltext_index method checks VERSION_FULLTEXT_INDEXES_SUPPORT (defined in neomodel/constants.py, lines 43-45) and raises FeatureNotSupported on older versions.
Querying Fulltext Indexes
Once indexes exist, query them using the FulltextFilter class in conjunction with the .filter() method.
Using FulltextFilter
The FulltextFilter class is defined in neomodel/semantic_filters.py (lines 31-58). It encapsulates the search string, target property, result limit, and relevance threshold.
from neomodel import FulltextFilter
results = Article.nodes.filter(
fulltext_filter=FulltextFilter(
query_string="graph databases",
fulltext_attribute_name="content",
topk=10,
threshold=0.5
)
).all()
The Filter API
When .filter() receives a FulltextFilter, the Match.filter method in neomodel/sync_/match.py (lines 1727-1748) extracts the filter and stores it in self.fulltext_query. During query compilation, neomodel injects a CALL db.index.fulltext.queryNodes(...) clause.
Returned Results Format
Fulltext queries return a list of tuples where each tuple contains (node_instance, relevance_score):
for article, score in results:
print(f"Relevance: {score:.2f} - Title: {article.title}")
Combining Fulltext and Property Filters
FulltextFilter integrates seamlessly with standard property filters. You can chain fulltext search with exact matches, regex, or other semantic filters.
# Find articles with "Neo4j" in the title (property filter)
# AND "fulltext" in the body (fulltext index)
results = Article.nodes.filter(
title__icontains="Neo4j",
fulltext_filter=FulltextFilter(
query_string="fulltext",
fulltext_attribute_name="body",
topk=5
)
).all()
Complete Working Example
from neomodel import (
StructuredNode, StringProperty, db,
FulltextIndex, FulltextFilter
)
# 1. Define the model with fulltext indexes
class Document(StructuredNode):
title = StringProperty(fulltext_index=FulltextIndex())
content = StringProperty(
fulltext_index=FulltextIndex(eventually_consistent=True)
)
# 2. Install labels to create indexes (run once)
db.install_labels(Document)
# 3. Create sample data
Document(title="Introduction to Graphs", content="Graph databases use nodes and relationships.").save()
Document(title="Full-Text Search", content="Using fulltext indexes for efficient text search.").save()
# 4. Query the fulltext index
results = Document.nodes.filter(
fulltext_filter=FulltextFilter(
query_string="search",
fulltext_attribute_name="content",
topk=10,
threshold=0.1
)
).all()
# 5. Process results
for doc, score in results:
print(f"Score: {score:.2f} | Title: {doc.title}")
Summary
- Declare indexes using
FulltextIndexinStringPropertydefinitions withinneomodel/properties.py. - Create indexes by calling
db.install_labels(), which triggersDatabase._create_node_fulltext_indexinneomodel/sync_/database.pyto executeCREATE FULLTEXT INDEXCypher. - Query indexes by passing
FulltextFilterto.filter(), handled byMatch.filterinneomodel/sync_/match.pyto generateCALL db.index.fulltext.queryNodescalls. - Requirements include Neo4j 5.16+; older versions raise
FeatureNotSupported. - Results return as
(node, score)tuples, allowing relevance-based ranking.
Frequently Asked Questions
What Neo4j version is required for fulltext indexes in neomodel?
Neomodel requires Neo4j 5.16 or newer to create and use fulltext indexes. The Database._create_node_fulltext_index method in neomodel/sync_/database.py checks against VERSION_FULLTEXT_INDEXES_SUPPORT defined in neomodel/constants.py. If you attempt to install labels on an older Neo4j instance, neomodel raises a FeatureNotSupported exception.
Can I use fulltext indexes on relationship properties?
Yes, neomodel supports fulltext indexes on both node and relationship properties. The installation process uses Database._create_relationship_fulltext_index (also in neomodel/sync_/database.py) when StructuredRel classes define properties with fulltext_index=FulltextIndex(). The query mechanism remains identical: use FulltextFilter with the relationship property name.
How do I combine fulltext search with regular property filters?
You can combine FulltextFilter with standard property lookups in the same .filter() call. The Match.filter implementation in neomodel/sync_/match.py processes both the fulltext query and property filters, combining them into a single Cypher query. For example, you can filter by title__icontains="Neo4j" while simultaneously searching the body property via FulltextFilter.
What analyzer does neomodel use by default for fulltext indexes?
By default, neomodel uses the standard-no-stop-words analyzer. This is configured in the FulltextIndex class within neomodel/properties.py. When db.install_labels() creates the index, it generates Cypher with OPTIONS { indexConfig: { 'fulltext.analyzer': 'standard-no-stop-words' } }. You can override this by passing a different analyzer string to the FulltextIndex constructor.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →