Text search
Text search allows you to look up nodes and edges whose properties contain specific text. To make a node or edge searchable, you must first create a text index for it.
Text indices and search are powered by the Tantivy full-text search engine.
Text search is no longer an experimental feature as of Memgraph version 3.6. You can use text search without any special configuration flags.
Create text index
Before you can use text search, you need to create a text index. Text indices are created using the CREATE TEXT INDEX
command. To create the text index, you need to:
- Provide a name for the index.
- Specify the label or edge type the index applies to.
- (Optional) Define which properties should be indexed.
Create a text index on nodes
CREATE TEXT INDEX text_index_name ON :Label;
Create a text index on edges
CREATE TEXT EDGE INDEX text_index_name ON :EDGE_TYPE;
Index all properties
This statement creates a text index named complianceDocuments
for nodes with the Report
label, indexing all text-indexable properties:
CREATE TEXT INDEX complianceDocuments ON :Report;
Index specific properties
You can also create a text index on a subset of properties by specifying them explicitly:
CREATE TEXT INDEX index_name ON :Label(prop1, prop2, prop3);
For example, to create an index only on the title
and content
properties of Report
nodes:
CREATE TEXT INDEX complianceDocuments ON :Report(title, content);
Edge text indices
Text indices can also be created on edges. To create a text index on edges:
CREATE TEXT EDGE INDEX edge_index_name ON :EDGE_TYPE;
You can also specify specific properties for edge indices:
CREATE TEXT EDGE INDEX edge_index_name ON :EDGE_TYPE(prop1, prop2);
If you attempt to create an index with an existing name, the statement will fail.
What is indexed
For any given node or edge, if a text index applies to it:
- When no specific properties are listed, all properties with text-indexable types (
String
,Integer
,Float
, orBoolean
) are stored. - When specific properties are listed, only those properties (if they have text-indexable types) are stored.
Changes made within the same transaction are not visible to the index. To see your changes in text search results, you need to commit the transaction first.
Run text search
To run text search, you need to call text_search
query module procedures.
Unlike other index types, the query planner currently does not utilize text indices.
Show text indices
To list all text indices in Memgraph, use the SHOW INDEX INFO
statement.
Query text index
Within a single transaction, repeated text searches may return different results if other transactions have committed their changes to the same index. For consistent results, avoid performing multiple identical searches within the same transaction when concurrent modifications are expected.
Use the text_search.search()
and text_search.search_edges()
procedures to search for text within a text index. These procedures allow you to find nodes or edges that match your search query based on their indexed properties.
Input:
index_name: string
➡ The text index to search.search_query: string
➡ The query to search for in the index.limit: int (optional, default=1000)
➡ The maximum number of results to return.
Output:
When the index is defined on nodes:
node: Node
➡ A node in the text index matching the given query.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
When the index is defined on edges:
edge: Relationship
➡ An edge in the text index matching the given query.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
Usage:
The syntax for the search_query
parameter is available here. If the query contains property names, attach the data.
prefix to them.
CALL text_search.search("index_name", "data.title:Rules2024") YIELD node, score RETURN *;
To query an index on edges, use:
CALL text_search.search_edges("index_name", "data.title:Rules2024") YIELD edge, score RETURN *;
Example
CREATE TEXT INDEX complianceDocuments ON :Document; CREATE (:Document {title: 'Rules2024', version: 1}); CREATE (:Document {title: 'Rules2024', version: 2}); CREATE (:Document {title: 'Other', version: 2}); // Search for documents with title containing 'Rules2024' CALL text_search.search('complianceDocuments', 'data.title:Rules2024') YIELD node RETURN node.title AS title, node.version AS version ORDER BY version ASC;
Result:
+-------------+-------------+ | title | version | +-------------+-------------+ | "Rules2024" | 1 | | "Rules2024" | 2 | +-------------+-------------+
Boolean expressions
You can use boolean logic in your search queries to create more complex search conditions. Boolean operators include AND
, OR
, and NOT
, and you can use parentheses to group conditions.
Usage:
Boolean expressions allow you to combine multiple search conditions:
AND
- both conditions must be trueOR
- at least one condition must be trueNOT
- condition must be false- Use parentheses
()
to group conditions and control precedence
Example
CREATE TEXT INDEX complianceDocuments ON :Document; CREATE (:Document {title: 'Rules2023', fulltext: 'nothing'}); CREATE (:Document {title: 'Rules2024', fulltext: 'words', version: 2}); // Search with boolean logic: (title is Rules2023 OR Rules2024) AND fulltext contains 'words' CALL text_search.search('complianceDocuments', '(data.title:Rules2023 OR data.title:Rules2024) AND data.fulltext:words') YIELD node RETURN node.title AS title, node.version AS version ORDER BY version ASC, title ASC;
Result:
+-------------+-------------+ | title | version | +-------------+-------------+ | "Rules2024" | 2 | +-------------+-------------+
Search over all indexed properties
The text_search.search_all
and text_search.search_all_edges
procedures look for text-indexed nodes or edges where at least one property value matches the given query.
Unlike text_search.search
, these procedures search over all properties, and there is no need to specify property names in the query.
Input:
index_name: string
➡ The text index to be searched.search_query: string
➡ The query applied to the text-indexed nodes or edges.limit: int (optional, default=1000)
➡ The maximum number of results to return.
Output:
When the index is defined on nodes:
node: Node
➡ A node inindex_name
matching the givensearch_query
.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
When the index is defined on edges:
edge: Relationship
➡ An edge inindex_name
matching the givensearch_query
.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
Usage:
The following query searches the complianceDocuments
index for nodes where at least one property value contains Rules2024
:
CALL text_search.search_all("complianceDocuments", "Rules2024") YIELD node RETURN node;
To search edges:
CALL text_search.search_all_edges("complianceEdges", "Rules2024") YIELD edge RETURN edge;
Example
CREATE TEXT INDEX complianceDocuments ON :Document; CREATE (:Document {title: 'Rules2024', fulltext: 'text words', version: 1}); CREATE (:Document {title: 'Other', fulltext: 'Rules2024 here', version: 3}); // Search for 'Rules2024' across all properties CALL text_search.search_all('complianceDocuments', 'Rules2024') YIELD node RETURN node ORDER BY node.version ASC;
Result:
+----------------------------------------------------------------------+ | node | +----------------------------------------------------------------------+ | (:Document {fulltext: "text words", title: "Rules2024", version: 1}) | | (:Document {fulltext: "Rules2024 here", title: "Other", version: 3}) | +----------------------------------------------------------------------+
Regex search
The text_search.regex_search
and text_search.regex_search_edges
procedures look for text-indexed nodes or edges where at least one property value matches the given regular expression (regex).
Input:
index_name: string
➡ The text index to be searched.search_query: string
➡ The regex applied to the text-indexed nodes or edges.limit: int (optional, default=1000)
➡ The maximum number of results to return.
Output:
When the index is defined on nodes:
node: Node
➡ A node inindex_name
matching the givensearch_query
.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
When the index is defined on edges:
edge: Relationship
➡ An edge inindex_name
matching the givensearch_query
.score: double
➡ The relevance score of the match. Higher scores indicate more relevant results.
Usage:
Regex searches apply to all properties; do not include property names in the search query.
The following query searches the complianceDocuments
index for nodes where at least one property value satisfies the wor.*s
regex, e.g. “works” and “words”:
CALL text_search.regex_search("complianceDocuments", "wor.*s") YIELD node RETURN node;
To search edges:
CALL text_search.regex_search_edges("complianceEdges", "wor.*s") YIELD edge RETURN edge;
Example
CREATE TEXT INDEX complianceDocuments ON :Document; CREATE (:Document {fulltext: 'words and things'}); CREATE (:Document {fulltext: 'more words'}); // Search using regex pattern 'wor.*s' CALL text_search.regex_search('complianceDocuments', 'wor.*s') YIELD node RETURN node ORDER BY node.fulltext ASC;
Result:
+--------------------------------------------+ | node | +--------------------------------------------+ | (:Document {fulltext: "more words"}) | | (:Document {fulltext: "words and things"}) | +--------------------------------------------+
Aggregations
Aggregations allow you to perform calculations on text search results. By using them, you can efficiently summarize the results, calculate averages or totals, identify min/max values, and count indexed nodes or edges that meet specific criteria.
The text_search.aggregate
and text_search.aggregate_edges
procedures let you define an aggregation and apply it to the results of a search query.
Input:
index_name: string
➡ The text index to be searched.search_query: string
➡ The query applied to the text-indexed nodes or edges.aggregation_query: string
➡ The aggregation (JSON-formatted) to be applied to the output ofsearch_query
.limit: int (optional, default=1000)
➡ The maximum number of results to return.
Output:
aggregation: string
➡ JSON-formatted string with the output of aggregation.
Usage:
Aggregation queries and results are strings with Elasticsearch-compatible JSON format, where "field"
corresponds to node or edge properties. If the search or aggregation queries contain property names, attach the data.
prefix to them.
The following query counts all nodes in the complianceDocuments
index:
CALL text_search.aggregate( "complianceDocuments", "data.title:Rules2024", '{"count": {"value_count": {"field": "data.version"}}}' ) YIELD aggregation RETURN aggregation;
To aggregate edges:
CALL text_search.aggregate_edges( "complianceEdges", "data.title:Rules2024", '{"count": {"value_count": {"field": "data.version"}}}' ) YIELD aggregation RETURN aggregation;
Example
CREATE TEXT INDEX complianceDocuments ON :Document; CREATE (:Document {title: 'Rules2024', version: 1}); CREATE (:Document {title: 'Rules2024', version: 2}); // Count documents matching the search query CALL text_search.aggregate( 'complianceDocuments', 'data.title:Rules2024', '{"count":{"value_count":{"field":"data.version"}}}' ) YIELD aggregation RETURN aggregation;
Result:
+-------------------------------+ | aggregation | +-------------------------------+ | "{\"count\":{\"value\":2.0}}" | +-------------------------------+
Drop text index
Text indices are dropped with the DROP TEXT INDEX
command. You need to give the name of the index to be deleted.
DROP TEXT INDEX text_index_name;
Compatibility
Text search supports most usage modalities that are available in Memgraph. Refer to the table below for an overview:
Feature | Support |
---|---|
Multitenancy | ✅ Yes |
Durability | ✅ Yes |
Replication | ✅ Yes |
Concurrent transactions | ⚠️ Yes, but search results may vary within transactions |
Storage modes | ❌ No (doesn’t work in IN_MEMORY_ANALYTICAL) |