TairSearch is a full-text search module built into Tair (Redis OSS-Compatible). It uses a query syntax similar to Elasticsearch, delivering millisecond-level write and search performance.
Overview
TairSearch provides the following capabilities:
-
Low latency and high performance: Millisecond-level write and full-text search powered by the Tair in-memory engine. For details, see TairSearch performance whitepaper.
-
Incremental and partial updates: Add, update, remove, and auto-increment individual document fields without rewriting entire documents.
-
Elasticsearch-like query syntax: JSON-based Query DSL supporting
bool,match,term, paging, and sorting -- familiar to developers with Elasticsearch experience. -
Aggregation:
terms, metrics, andfilteraggregations for analytics on search results. See Aggregations. -
Auto-complete: Prefix-based fuzzy matching for search-as-you-type experiences.
-
Built-in and custom analyzers: Built-in analyzers for English (
standard,stop), Chinese (jieba,IK), and other languages. Custom analyzers with user-defined dictionaries and stop words are also supported. For details, see Search analyzers. -
Shard index query: Search across multiple shard indexes with
TFT.MSEARCHand get aggregated results. -
Document compression: Compress stored documents to reduce memory usage. Disabled by default.
-
Query caching: Cache recent query results to improve hot-data query performance.
Prerequisites
The instance must be a Tair memory-optimized instance (DRAM-based) running one of these versions:
|
Engine version |
Minimum minor version |
|
Redis 5.0-compatible |
1.7.27 or later |
|
Redis 6.0-compatible |
6.2.4.1 or later |
|
Redis 7.0-compatible |
All minor versions |
Update the instance to the latest minor version for the most features and highest stability. For instructions, see Upgrade minor and proxy versions. For cluster or read/write splitting instances, also update the proxy nodes to the latest minor version to make sure all commands work as expected.
Release notes
Redis 5.0-compatible DRAM-based instances
|
Date |
Version |
Changes |
|
2022-03-11 |
V1.7.27 |
TairSearch released |
|
2022-05-24 |
V1.8.5 |
Aggregation feature |
|
2022-09-06 |
V5.0.15 |
|
|
2023-01-13 |
V5.0.25 |
Analyzers |
|
2023-03-15 |
V5.0.28 |
Query caching, document compression, |
|
2023-06-12 |
V5.0.35 |
ARRAY data type, Okapi BM25 similarity algorithm |
Redis 6.0-compatible DRAM-based instances
|
Date |
Version |
Changes |
|
2023-02-07 |
V6.2.4.1 |
TairSearch (all features of V5.0.25) |
|
2023-03-14 |
V6.2.5.0 |
Query caching, document compression, |
|
2023-06-12 |
V6.2.7.3 |
ARRAY data type, Okapi BM25 (all features of V5.0.35) |
|
2023-12-21 |
V23.12.1.2 |
|
Redis 7.0-compatible DRAM-based instances
|
Date |
Version |
Changes |
|
2024-07-22 |
V24.7.0.0 |
TairSearch support |
Best practices
Usage notes
-
TairSearch data is stored on the Tair instance and consumes instance memory.
-
To reduce memory usage:
-
Set
indextotrueonly for fields that need to be searchable. Setindextofalsefor other fields. -
Use
_sourcewithincludes/excludespatterns to store only the document fields you need. -
Choose an appropriate analyzer to avoid unnecessary tokenization and increased memory usage.
-
Enable document compression for large documents.
-
-
Keep the number of documents per index within 5 million. This prevents data skew in cluster instances, balances read and write requests, and reduces large keys and hotkeys. Split large datasets across multiple indexes and use
TFT.MSEARCHto query them together.
Syntax conventions
|
Convention |
Meaning |
|
|
|
Command keyword |
|
|
*italic* |
Variable |
|
|
|
Optional parameter. Parameters without brackets are required. |
|
|
`A\ |
B` |
Mutually exclusive parameters. Specify only one. |
|
|
The preceding parameter can be repeated. |
Full-text search commands
TFT.CREATEINDEX
Creates an index with a mapping definition. The mapping syntax is similar to Elasticsearch explicit mapping. An index must be created before documents can be added.
Syntax
TFT.CREATEINDEX index mappings [settings]
To prevent large keys, split large indexes into smaller indexes with identicalmappingsandsettings, then query them together withTFT.MSEARCH.
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index to create. |
|
*mappings* |
Yes |
The JSON mapping definition. See Mapping options. |
|
*settings* |
No |
The JSON settings for the index. See Index settings. |
Mapping options
The mappings object supports the following top-level fields:
dynamic
The mapping mode. Valid values:
-
"strict"-- Data can only be written to fields defined inproperties. Writing to undefined fields fails. -
Not specified (default) -- Non-strict mode. The system does not check whether written fields are defined in
properties.
enabled
Controls type checking for non-indexed fields (index set to false). Valid values: true (default) and false. When true, a write fails if the field type does not match the type defined in properties. Indexed fields are always type-checked regardless of this setting.
_source
Controls whether the original document is stored. This setting does not affect index creation.
-
enabled--true(default) stores the original document.falsedoes not store it. -
includes-- An array of field name patterns to include. Wildcards are supported. -
excludes-- An array of field name patterns to exclude. Wildcards are supported. If a field matches bothincludesandexcludes,excludestakes precedence (the field is not stored).
Examples:
"_source": {"enabled": true}
This is the default -- stores all fields. Equivalent to "_source": {"enabled": true, "excludes": [], "includes": []}.
"_source": {"enabled": true, "excludes": [], "includes": ["id", "name"]}
Stores only the id and name fields.
properties
A collection of field definitions. Each field supports the following attributes:
|
Attribute |
Description |
Default |
|
|
The data type. See Field types. |
Required |
|
|
Whether this field is indexed (searchable). |
|
|
|
The field weight for scoring. A positive floating-point number. |
|
|
|
The similarity algorithm: |
|
Field types
|
Type |
Description |
Type-specific options |
|
|
A string that is not tokenized. Suitable for exact-match queries. |
|
|
|
A string that is tokenized by an analyzer before indexing. |
|
|
|
64-bit integer. Suitable for timestamps (Unix epoch). |
-- |
|
|
32-bit integer. |
-- |
|
|
64-bit floating-point number. |
-- |
ARRAY support: All field types support array variants. Append [] to the type name. For example, keyword[] declares an array of keywords.
TFT.CREATEINDEX idx:products '{"mappings":{"properties":{"tags":{"type":"keyword[]"}}}}'
Text analyzer options
|
Option |
Description |
Default |
|
|
The analyzer used for indexing. See Built-in analyzers. Custom analyzers can also be specified, configured via the |
|
|
|
The analyzer used at search time. Same valid values as |
Same as |
Built-in analyzers
standard (default), jieba (recommended for Chinese, works better than chinese), stop, IK, pattern, whitespace, simple, keyword, chinese, french, dutch, russian.
For details, see Built-in analyzers.
Index settings
The optional settings object supports the following fields:
analysis
Defines custom analyzers. For details, see Custom analyzers.
index
Configures custom similarity algorithms. Use this to customize Okapi BM25 parameters:
|
Parameter |
Description |
Default |
Valid values |
|
|
Controls how much term frequency affects the score. Higher values increase the effect. |
|
Greater than |
|
|
Controls how much document length affects the score. Higher values increase the effect. |
|
|
Example:
TFT.CREATEINDEX idx:products '{"mappings":{"properties":{"name":{"type":"keyword","similarity":"my_bm25"}}},"settings":{"index":{"similarity":{"my_bm25":{"type":"BM25","k1":1.0,"b":1.0}}}}}'
queries_cache
Configures the query result cache. By default, the cache stores up to 10,000 results without a memory limit. When full, results are evicted using the Least Recently Used (LRU) algorithm.
|
Parameter |
Type |
Description |
Default |
|
|
STRING |
Maximum memory for all query caches. Supports |
No limit |
|
|
INTEGER |
Time to Live for cached results, in seconds. |
|
Example:
"settings": {"queries_cache": {"size": "100mb", "ttl": 3}}
queries_cacheis a node-level parameter. Configuring it on one index affects all other indexes in the same process. Use the samequeries_cacheconfiguration across all indexes.
compress_doc
Configures transparent document compression. Enabling compression increases CPU consumption and read/write latency. Enable this only when the index is large (kilobytes or more) and storage cost is the primary concern.
|
Parameter |
Type |
Description |
Default |
|
|
STRING |
Compression threshold. Documents are compressed only when they reach this size. Supports |
|
|
|
BOOLEAN |
Whether to enable compression. |
|
Example:
"settings": {"compress_doc": {"size": "1kb", "enable": true}}
Compression applies only to documents written or updated after you enable the setting with TFT.UPDATEINDEX. Existing documents are not retroactively compressed.
Return value
-
On success:
OK -
On failure: An error message
Example
TFT.CREATEINDEX idx:product '{"mappings":{"_source":{"enabled":true},"properties":{"product_id":{"type":"keyword","ignore_above":128},"product_name":{"type":"text"},"product_title":{"type":"text","analyzer":"jieba"},"price":{"type":"double"}}}}'OK
See also: TFT.UPDATEINDEX, TFT.GETINDEX
TFT.UPDATEINDEX
Adds new fields to an index mapping or modifies index settings.
Syntax
TFT.UPDATEINDEX index mappings [settings]
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*mappings* |
Yes |
New |
|
*settings* |
No |
Modified index settings. Only |
For the full syntax of mappings and settings, see TFT.CREATEINDEX.
Return value
-
On success:
OK -
On failure: An error message
Example
TFT.UPDATEINDEX idx:product '{"mappings":{"properties":{"product_group":{"type":"text","analyzer":"chinese"}}}}'OK
See also: TFT.CREATEINDEX, TFT.GETINDEX
TFT.GETINDEX
Retrieves the mapping and settings of an index.
Syntax
TFT.GETINDEX index
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
Return value
-
On success: The JSON-formatted mapping content of the index
-
On failure: An error message
Example
TFT.GETINDEX idx:product{"idx:product0310":{"mappings":{"_source":{"enabled":true,"excludes":[],"includes":["product_id"]},"dynamic":"false","properties":{"price":{"boost":1.0,"enabled":true,"ignore_above":-1,"index":true,"similarity":"classic","type":"double"},"product_id":{"boost":1.0,"enabled":true,"ignore_above":128,"index":true,"similarity":"classic","type":"keyword"},"product_name":{"boost":1.0,"enabled":true,"ignore_above":-1,"index":true,"similarity":"classic","type":"text"},"product_title":{"analyzer":"chinese","boost":1.0,"enabled":true,"ignore_above":-1,"index":true,"similarity":"classic","type":"text"}}}}}
See also: TFT.CREATEINDEX, TFT.UPDATEINDEX
TFT.ADDDOC
Adds a single document to an index.
Syntax
TFT.ADDDOC index document [WITH_ID doc_id]
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*document* |
Yes |
The JSON document. Field values must match the data types defined in the mapping. |
|
|
No |
A custom document ID (string). If omitted, an ID is auto-generated. If the specified ID already exists, the existing document is overwritten. |
If_sourceis configured withincludes, only the fields listed inincludesare stored when adding or updating a document.
Return value
-
On success: A JSON object containing the document ID, e.g.,
{"_id":"00001"} -
On failure: An error message
Examples
Add a document with a custom ID:
TFT.ADDDOC idx:product '{"product_id":"product test"}' WITH_ID 00001{"_id":"00001"}
Add a document with an array field:
TFT.ADDDOC idx:product '{"product_id":["an","2","3df"]}' WITH_ID 00001
See also: TFT.MADDDOC, TFT.UPDATEDOCFIELD, TFT.DELDOC
TFT.MADDDOC
Adds multiple documents to an index in a single atomic operation. If any document has an invalid format, none of the documents are added.
Syntax
TFT.MADDDOC index document doc_id [document1 doc_id1] ...
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*document* |
Yes |
A JSON document. Field values must match the data types defined in the mapping. |
|
*doc_id* |
Yes |
The document ID (string). |
If_sourceis configured withincludes, only the fields listed inincludesare stored when adding or updating a document.
Return value
-
On success:
OK -
On failure: An error message
Example
TFT.MADDDOC idx:product '{"product_id":"test1"}' 00011 '{"product_id":"test2"}' 00012OK
See also: TFT.ADDDOC, TFT.DELDOC
TFT.UPDATEDOCFIELD
Updates specific fields in a document. If the document does not exist, it is created (equivalent to TFT.ADDDOC). If a field does not exist, it is added.
Syntax
TFT.UPDATEDOCFIELD index doc_id document
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document to update. |
|
*document* |
Yes |
A JSON object containing the fields to update. Indexed fields must match the data types defined in the mapping. Non-indexed fields can be of any type. |
If_sourceis configured withincludes, only the fields listed inincludesare stored when adding or updating a document.
Return value
-
On success:
OK -
On failure: An error message
Example
TFT.UPDATEDOCFIELD idx:product 00011 '{"product_id":"test8","product_group":"BOOK"}'OK
See also: TFT.ADDDOC, TFT.DELDOCFIELD
TFT.DELDOCFIELD
Deletes one or more fields from a document. If the field is indexed, its index entry is also removed.
Syntax
TFT.DELDOCFIELD index doc_id field [field1 field2 ...]
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document. |
|
*field* |
Yes |
One or more field names to delete. |
The operation fails if the specified field does not exist in the document (for example, a field filtered out by _source).
Return value
-
On success: The number of deleted fields (integer)
-
On failure: An error message
Example
TFT.DELDOCFIELD idx:product 00011 product_group(integer) 1
See also: TFT.UPDATEDOCFIELD
TFT.INCRLONGDOCFIELD
Increments an integer field in a document. The field must be of type LONG or INTEGER. The ARRAY data type is not supported.
Syntax
TFT.INCRLONGDOCFIELD index doc_id field increment
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document. |
|
*field* |
Yes |
The field to increment. Must be |
|
*increment* |
Yes |
The increment value. Can be a positive or negative integer. |
If the document does not exist, it is auto-created with the field initialized to0before applying the increment. The operation fails if the field does not exist in the document (for example, a field filtered out by_source).
Return value
-
On success: The updated field value (integer)
-
On failure: An error message
Example
TFT.INCRLONGDOCFIELD idx:product 00011 stock 100(integer)100
See also: TFT.INCRFLOATDOCFIELD, TFT.UPDATEDOCFIELD
TFT.INCRFLOATDOCFIELD
Increments a floating-point field in a document. The field must be of type DOUBLE. The ARRAY data type is not supported.
Syntax
TFT.INCRFLOATDOCFIELD index doc_id field increment
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document. |
|
*field* |
Yes |
The field to increment. Must be |
|
*increment* |
Yes |
The increment value. Can be a positive or negative floating-point number. |
If the document does not exist, it is auto-created with the field initialized to0before applying the increment. The operation fails if the field does not exist in the document (for example, a field filtered out by_source).
Return value
-
On success: The updated field value (string)
-
On failure: An error message
Example
TFT.INCRFLOATDOCFIELD idx:product 00011 stock 299.6"299.6"
See also: TFT.INCRLONGDOCFIELD, TFT.UPDATEDOCFIELD
TFT.GETDOC
Retrieves a document by its ID.
Syntax
TFT.GETDOC index doc_id
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document. |
Return value
-
On success: A JSON object with the document ID and content
-
On failure: An error message
Example
TFT.GETDOC idx:product 00011{"_id":"00011","_source":{"product_id":"test8"}}
See also: TFT.EXISTS, TFT.SEARCH
TFT.EXISTS
Checks whether a document exists in an index.
Syntax
TFT.EXISTS index doc_id
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
The ID of the document. |
Return value
-
1if the document exists -
0if the index or document does not exist -
On failure: An error message
Example
TFT.EXISTS idx:product 00011(integer) 1
See also: TFT.GETDOC, TFT.DOCNUM
TFT.DOCNUM
Gets the number of documents in an index.
Syntax
TFT.DOCNUM index
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
Return value
-
On success: The document count (integer)
-
On failure: An error message
Example
TFT.DOCNUM idx:product(integer) 3
See also: TFT.EXISTS, TFT.SCANDOCID
TFT.SCANDOCID
Scans document IDs in an index using a cursor, similar to the Redis SCAN command.
Syntax
TFT.SCANDOCID index cursor [MATCH *value*] [COUNT count]
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*cursor* |
Yes |
The cursor position. Use |
|
|
No |
A glob-style pattern to filter document IDs. Example: |
|
|
No |
The maximum number of items to return per scan. Default: |
Return value
A two-element array:
-
The cursor for the next iteration.
0indicates the scan is complete. -
An array of document IDs.
Example
TFT.SCANDOCID idx:product 0 COUNT 31) "0"
2) 1) "00001"
2) "00011"
3) "00012"
See also: TFT.DOCNUM, TFT.GETDOC
TFT.DELDOC
Deletes one or more documents from an index by document ID.
Syntax
TFT.DELDOC index doc_id [doc_id] ...
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*doc_id* |
Yes |
One or more document IDs to delete. |
Return value
-
On success: The number of documents actually deleted (string). Non-existent IDs are ignored.
-
On failure: An error message
Example
TFT.DELDOC idx:product 00011 00014"1" # Document 00014 does not exist, so only document 00011 is deleted.
See also: TFT.DELALL, TFT.ADDDOC
TFT.DELALL
Deletes all documents from an index while retaining the index and its mapping.
Syntax
TFT.DELALL index
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
Return value
-
On success:
OK -
On failure: An error message
Example
TFT.DELALL idx:productOK
See also: TFT.DELDOC, DEL
TFT.ANALYZER
Tests how an analyzer tokenizes a given text.
Syntax
TFT.ANALYZER analyzer_name text [INDEX index_name] [SHOW_TIME]
Parameters
|
Parameter |
Required |
Description |
|
*analyzer_name* |
Yes |
The name of a built-in or custom analyzer. |
|
*text* |
Yes |
The text to tokenize. Must be UTF-8 encoded. |
|
|
Conditional |
The index containing the analyzer definition. Required when using a custom analyzer or a built-in analyzer with modified stop words or dictionaries. |
|
|
No |
Include tokenization duration in the output, in microseconds. The first run with a large dictionary (e.g., Jieba, IK) may take several seconds due to dictionary loading. |
Return value
-
On success: A JSON object containing token details
-
On failure: An error message
Example
TFT.ANALYZER standard "Tair is a nosql database"{
"tokens": [
{"token": "Tair", "start_offset": 0, "end_offset": 4, "position": 0},
{"token": "nosql", "start_offset": 10, "end_offset": 15, "position": 3},
{"token": "database", "start_offset": 16, "end_offset": 24, "position": 4}
]
}
DEL (native Redis)
Use the native Redis DEL command to delete one or more TairSearch keys (indexes).
DEL key [key ...]
For details, see the DEL command reference.
TFT.SEARCH
Searches for documents in an index using the Query DSL.
Syntax
TFT.SEARCH index query
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*query* |
Yes |
A JSON Query DSL statement. See sections below for the full syntax. |
The query syntax is similar to Elasticsearch Query DSL.
Return value
On success, a JSON object containing search results:
{
"hits": {
"hits": [
{"_id": "...", "_index": "...", "_score": 1.0, "_source": {...}}
],
"max_score": 1.0,
"total": {
"relation": "eq",
"value": 3
}
}
}
The total.relation field indicates how the count relates to total.value:
-
"eq"-- The count is exact. -
"gte"-- The count is at least this value (whentrack_total_hitsisfalse).
Example
TFT.SEARCH idx:product '{"sort":[{"price":{"order":"desc"}}]}'{"hits":{"hits":[{"_id":"fruits_3","_index":"idx:product","_score":1.0,"_source":{"product_id":"fruits_3","product_name":"orange","price":30.2,"stock":3000}},{"_id":"fruits_2","_index":"idx:product","_score":1.0,"_source":{"product_id":"fruits_2","product_name":"banana","price":24.0,"stock":2000}},{"_id":"fruits_1","_index":"idx:product","_score":1.0,"_source":{"product_id":"fruits_1","product_name":"apple","price":19.5,"stock":1000}}],"max_score":1.0,"total":{"relation":"eq","value":3}}}
See also: TFT.MSEARCH, TFT.EXPLAINCOST, TFT.EXPLAINSCORE
Query types
The query field in the Query DSL supports the following query types.
match
Full-text match query. The query text is tokenized and matched against the index. For exact-match or prefix scenarios that do not require tokenization or fuzzy matching, use term or prefix instead for better performance.
|
Parameter |
Type |
Description |
Default |
|
|
STRING |
The search text. When fuzzy match is disabled, the text is tokenized using the specified |
Required |
|
|
STRING |
The analyzer to use. Valid values: |
|
|
|
STRING |
The relationship between tokens. |
|
|
|
INTEGER |
Minimum number of tokens that must match. Effective when fuzzy match is disabled and |
-- |
|
|
INTEGER |
Enable fuzzy match. Set to |
Disabled |
|
|
INTEGER |
Number of leading characters that must match exactly in fuzzy mode. |
|
Example -- basic match:
{"query": {"match": {"f0": {"query": "redis cluster", "operator": "OR"}}}}
Example -- fuzzy match:
{"query": {"match": {"f0": {"query": "excelentl", "fuzziness": 1}}}}
term
Exact token query without tokenization. More efficient than match for exact lookups. Supports all field types.
|
Parameter |
Type |
Description |
Default |
|
|
STRING |
The exact value to match. |
Required |
|
|
DOUBLE |
Score weight for this query clause. |
|
|
|
BOOLEAN |
Convert the query value to lowercase before matching. Set to |
|
Examples:
{"query": {"term": {"f0": {"value": "redis", "boost": 2.0}}}}{"query": {"term": {"f0": {"value": "redis", "lowercase": false}}}}
terms
Multi-value exact query. Matches documents containing any of the specified values. Maximum 1,024 values per query.
|
Parameter |
Type |
Description |
Default |
|
|
BOOLEAN |
Convert query values to lowercase before matching. |
|
Example:
{"query": {"terms": {"f0": ["redis", "excellent"]}}}
If the analyzer used for indexing does not lowercase the text, setlowercasetofalseto avoid missed matches.
range
Range query. Supports all field types.
|
Operator |
Meaning |
|
|
Less than |
|
|
Greater than |
|
|
Less than or equal to |
|
|
Greater than or equal to |
Example:
{"query": {"range": {"f0": {"lt": "abcd", "gt": "ab"}}}}
wildcard
Wildcard pattern query. Supported wildcards: * (matches any character sequence) and ? (matches a single character).
Example:
{"query": {"wildcard": {"f0": {"value": "*b?Se"}}}}
prefix
Prefix query. Matches documents where the field value starts with the specified string.
Example:
{"query": {"prefix": {"f0": "abcd"}}}
bool
Compound query combining multiple query clauses. Maximum 1,024 clauses per bool query. Nested bool queries are supported.
|
Clause |
Description |
|
|
Results must match all clauses in this list. |
|
|
Results must not match any clause in this list. |
|
|
Results should match at least |
|
|
Minimum number of |
Example -- must:
{"query": {"bool": {"must": [{"match": {"DB": "redis"}}, {"match": {"Architectures": "cluster"}}]}}}
Example -- should with minimum_should_match:
{"query": {"bool": {"minimum_should_match": 1, "should": [{"match": {"DB": "redis"}}, {"match": {"Architectures": "cluster"}}]}}}
Example -- nested bool:
TFT.SEARCH idx:product '{"query":{"bool":{"must":[{"term":{"product_name":"apple"}},{"bool":{"minimum_should_match":1,"should":[{"term":{"price":19.5}},{"term":{"product_id":"fruits_2"}}]}}]}}}'
dis_max
Best-match compound query. Returns documents matching one or more clauses and assigns the highest individual clause score.
|
Parameter |
Type |
Description |
Default |
|
|
ARRAY |
An array of query clauses. |
Required |
|
|
DOUBLE |
A multiplier (0 to 1) applied to scores from non-best-matching clauses. Final score = best clause score + (sum of other clause scores x |
|
Example: With three clauses scoring 5, 1, and 3 for a document and tie_breaker of 0.5, the final score is 5 + ((1 + 3) x 0.5) = 7.
{"query": {"dis_max": {"queries": [{"term": {"f0": "redis"}}, {"term": {"f1": "database"}}, {"match": {"title": "redis memory database"}}], "tie_breaker": 0.5}}}
constant_score
Wraps another query and returns a constant score for all matching documents.
|
Parameter |
Type |
Description |
Default |
|
|
OBJECT |
The query to wrap. |
Required |
|
|
DOUBLE |
The constant score to assign. |
|
Example:
{"query": {"constant_score": {"filter": {"term": {"f0": "abc"}}, "boost": 1.2}}}
match_all
Matches all documents in the index with a uniform score.
|
Parameter |
Type |
Description |
Default |
|
|
DOUBLE |
The score to assign to all documents. |
|
Example:
{"query": {"match_all": {"boost": 2.3}}}
In compound queries (bool,dis_max,constant_score), you can setboostvalues onterm,terms,range, andwildcardclauses to control scoring weight. The defaultboostis1. Specify the boost inside the parameter syntax, for example:{"term": {"f0": {"value": "redis", "boost": 2}}}.
Sorting
Use the sort field to control result ordering. The ARRAY data type is not supported for sorting.
|
Value |
Description |
|
|
Sort by relevance score in descending order. |
|
|
Sort by document ID in ascending order. |
|
*field_name* |
Sort by a specific field in ascending order. The field must be indexed ( |
For multi-field sorting, use an array. Control sort direction with order ("desc" or "asc"):
{"sort": [{"price": {"order": "desc"}}, {"_doc": {"order": "desc"}}]}
Source filtering
Use the _source field to control which document fields appear in results:
{"_source": {"includes": ["f0"]}}
Supports both includes and excludes with wildcard patterns.
Paging
|
Parameter |
Type |
Description |
Default |
|
|
INTEGER |
The zero-based offset of the first result to return. |
|
|
|
INTEGER |
The maximum number of results to return. Valid values: |
|
Usingfromandsizetogether provides paging. Query performance decreases asfromincreases.
track_total_hits
Controls whether to count all matching documents for term, terms, or match queries (compound queries are also supported, but fuzzy match within match is disabled).
|
Value |
Behavior |
|
|
Returns only the top 100 documents sorted by score. |
|
|
Counts all matching documents. |
Setting track_total_hits to true with a large number of matching documents can cause slow queries. Use with caution.
TFT.MSEARCH
Searches across multiple indexes that share identical mappings and settings, then aggregates and returns the combined results.
Syntax
TFT.MSEARCH index_count index [index1] ... query
Parameters
|
Parameter |
Required |
Description |
|
*index_count* |
Yes |
The number of indexes to search. Valid values: |
|
*index* |
Yes |
One or more index names. All indexes must have identical |
|
*query* |
Yes |
A JSON Query DSL statement. Supports |
TFT.MSEARCHdoes not support thefromparameter. Use cursor-based paging instead (see below).
Cursor-based paging
Use size, reply_with_keys_cursor, and keys_cursor together for paging:
|
Parameter |
Type |
Description |
Default |
|
|
INTEGER |
Number of results to return per page. |
|
|
|
BOOLEAN |
Include cursor positions in the response. |
|
|
|
OBJECT |
Cursor positions from the previous query. Use |
|
How it works:
-
First query: Set
reply_with_keys_cursortotrueand omitkeys_cursor(or set to0). Tair retrievessizedocuments from each index, then gathers, scores, sorts, and aggregates the combined results, returning the topsizedocuments. The response includeskeys_cursorpositions for each index. -
Next query: Pass the returned
keys_cursorfrom the previous response. Tair continues retrieval from the cursor positions in each index.
Aggregation behavior
TFT.MSEARCH aggregates child result sets from each index rather than aggregating the combined dataset:
-
Rate and sort: Scores and sorts child result sets.
-
sum, max, min, avg, value_count: Aggregates across child result sets.
-
sum_of_squares, variance, std_deviation: Averages across child result sets.
-
Terms aggregation and filter aggregation: Aggregates across child result sets separately.
Return value
On success, a JSON object similar to TFT.SEARCH output, plus an aux_info field:
{
"aux_info": {
"index_crc64": 15096806844241479487,
"keys_cursor": {"key0": 2, "key1": 5, "key2": 3},
"field_type": {"f0": "long"}
}
}
|
Field |
Description |
|
|
CRC-64 hash of the mappings and settings. Can be ignored for business requests. |
|
|
Cursor positions for the next paged query. Included only when |
|
|
Data types of sorted fields. Included only when sorting by an index field. |
Example
Set up three indexes with the same mapping:
TFT.CREATEINDEX key0 '{"mappings":{"properties":{"f0":{"type":"long"}}}}'
TFT.CREATEINDEX key1 '{"mappings":{"properties":{"f0":{"type":"long"}}}}'
TFT.CREATEINDEX key2 '{"mappings":{"properties":{"f0":{"type":"long"}}}}'
TFT.ADDDOC key0 '{"f0":120}'
TFT.ADDDOC key0 '{"f0":130}'
TFT.ADDDOC key1 '{"f0":140}'
TFT.ADDDOC key1 '{"f0":150}'
TFT.ADDDOC key2 '{"f0":160}'
TFT.ADDDOC key2 '{"f0":170}'
First page:
TFT.MSEARCH 3 key0 key1 key2 '{"size":2,"query":{"range":{"f0":{"gt":120,"lte":170}}},"sort":[{"f0":{"order":"desc"}}],"reply_with_keys_cursor":true}'{"hits":{"hits":[{"_id":"16625439765504840","_index":"key2","_score":1.0,"_source":{"f0":170}},{"_id":"16625439741096630","_index":"key2","_score":1.0,"_source":{"f0":160}}],"max_score":1.0,"total":{"relation":"eq","value":5}},"aux_info":{"index_crc64":10084399559244916810,"keys_cursor":{"key0":0,"key1":0,"key2":2}}}
Second page (pass the keys_cursor from the previous response):
TFT.MSEARCH 3 key0 key1 key2 '{"size":2,"query":{"range":{"f0":{"gt":120,"lte":170}}},"sort":[{"f0":{"order":"desc"}}],"reply_with_keys_cursor":true,"keys_cursor":{"key0":0,"key1":0,"key2":2}}'{"hits":{"hits":[{"_id":"16625439652681160","_index":"key1","_score":1.0,"_source":{"f0":150}},{"_id":"16625439624704580","_index":"key1","_score":1.0,"_source":{"f0":140}}],"max_score":1.0,"total":{"relation":"eq","value":5}},"aux_info":{"index_crc64":10084399559244916810,"keys_cursor":{"key0":0,"key1":2,"key2":2}}}
See also: TFT.SEARCH
TFT.EXPLAINCOST
Retrieves the execution time breakdown of a query.
Syntax
TFT.EXPLAINCOST index query
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*query* |
Yes |
A Query DSL statement (same syntax as |
Return value
A JSON object with the following sections:
|
Section |
Description |
|
|
Query execution time in microseconds ( |
|
|
Aggregation time in microseconds. Omitted if no aggregation is specified. |
|
|
Time for collecting and sorting results in microseconds, plus the collector type: |
Example
TFT.EXPLAINCOST idx:product '{"sort":[{"price":{"order":"desc"}}]}'{
"QUERY_COST": {
"query": "MATCHALL_QUERY",
"doc_num": 1,
"time_cost_us": 2
},
"COLLECTOR_COST": {
"collector_type": "CustomSortCollector",
"time_cost_us": 20
}
}
See also: TFT.SEARCH, TFT.EXPLAINSCORE
TFT.EXPLAINSCORE
Retrieves detailed scoring breakdowns for search results, showing how document scores are calculated.
Available only for DRAM-based instances compatible with Redis 6.0 or later.
Syntax
TFT.EXPLAINSCORE index query [doc_id] ...
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*query* |
Yes |
A Query DSL statement (same syntax as |
|
*doc_id* |
No |
One or more document IDs. When specified, only these documents are included in the output. |
Return value
The same result set as TFT.SEARCH, plus an _explanation object for each document containing:
|
Field |
Description |
|
|
The document score. |
|
|
The formula used for score calculation. |
|
|
The document field matching the query terms. |
|
|
The query term found in the document. |
|
|
The scoring weight applied to this query clause. |
|
|
Metadata and the step-by-step scoring process. |
Example
TFT.EXPLAINSCORE today_shares '{"query":{"wildcard":{"shares_name":{"value":"*BY"}}}}'{
"hits": {
"hits": [
{
"_id": "17036492095306830",
"_index": "today_shares",
"_score": 1.0,
"_source": {
"shares_name": "YBY",
"logictime": 14300410,
"purchase_type": 1,
"purchase_price": 11.1,
"purchase_count": 100,
"investor": "Mila"
},
"_explanation": {
"score": 1.0,
"description": "score, computed as query_boost",
"field": "shares_name",
"term": "*BY",
"query_boost": 1.0
}
}
],
"max_score": 1.0,
"total": {"relation": "eq", "value": 1}
}
}
See also: TFT.SEARCH, TFT.EXPLAINCOST
Auto-complete commands
TFT.ADDSUG
Adds one or more auto-complete text entries with associated weights.
Syntax
TFT.ADDSUG index text weight [text weight] ...
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*text* |
Yes |
The auto-complete text entry. |
|
*weight* |
Yes |
The scoring weight (positive integer). Higher weights rank higher in results. |
Return value
-
On success: The number of added text entries (integer)
-
On failure: An error message
Example
TFT.ADDSUG idx:redis 'redis is a memory database' 3 'redis cluster' 10(integer) 2
See also: TFT.GETSUG, TFT.DELSUG, TFT.SUGNUM
TFT.DELSUG
Deletes one or more auto-complete text entries from an index.
Syntax
TFT.DELSUG index text [text] ...
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*text* |
Yes |
The exact text entry to delete. Must be a complete and exact match. |
Return value
-
On success: The number of deleted text entries (integer)
-
On failure: An error message
Example
TFT.DELSUG idx:redis 'redis is a memory database' 'redis cluster'(integer) 2
See also: TFT.ADDSUG, TFT.SUGNUM
TFT.SUGNUM
Gets the number of auto-complete text entries in an index.
Syntax
TFT.SUGNUM index
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
Return value
-
On success: The entry count (integer)
-
On failure: An error message
Example
TFT.SUGNUM idx:redis(integer) 3
See also: TFT.ADDSUG, TFT.GETSUG
TFT.GETSUG
Retrieves auto-complete suggestions matching a prefix. Results are returned in descending order of weight.
Syntax
TFT.GETSUG index prefix [MAX_COUNT count] [FUZZY]
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
|
*prefix* |
Yes |
The prefix to match. |
|
|
No |
Maximum number of entries to return. Valid values: |
|
|
No |
Enable fuzzy matching. |
Return value
-
On success: A list of matching auto-complete text entries
-
On failure: An error message
Example
TFT.GETSUG idx:redis res MAX_COUNT 2 FUZZY1) "redis cluster"
2) "redis lock"
See also: TFT.ADDSUG, TFT.GETALLSUGS
TFT.GETALLSUGS
Retrieves all auto-complete text entries in an index.
Syntax
TFT.GETALLSUGS index
Parameters
|
Parameter |
Required |
Description |
|
*index* |
Yes |
The name of the index. |
Return value
-
On success: A list of all auto-complete text entries
-
On failure: An error message
Example
TFT.GETALLSUGS idx:redis1) "redis cluster"
2) "redis lock"
3) "redis is a memory database"
See also: TFT.GETSUG, TFT.ADDSUG
Aggregations
Add an aggs (or aggregations) clause to a TFT.SEARCH query to perform aggregations on the result set.
Basic usage
Specify a custom aggregation name, an aggregation type, and a field to aggregate on:
TFT.SEARCH shares '{"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Sum":{"sum":{"field":"purchase_price"}}}}'
The response includes both the search results from query and the aggregation results from aggs:
{"hits":{"hits":[{"_id":"16581351808123930","_index":"today_shares0718","_score":1.0,"_source":{"shares_name":"XAX","logictime":14300210,"purchase_type":1,"purchase_price":101.1,"purchase_count":100,"investor":"Jay"}},{"_id":"16581351809626430","_index":"today_shares0718","_score":1.0,"_source":{"shares_name":"XAX","logictime":14300310,"purchase_type":1,"purchase_price":111.1,"purchase_count":100,"investor":"Jay"}}],"max_score":1.0,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Sum":{"value":212.2}}}
Add "size": 0 to the query to return only aggregation results without documents.
Metrics aggregation
Metrics aggregations perform numerical calculations on numeric fields (integer, double, etc.) and do not support nested sub-aggregations.
|
Metric |
Description |
Supported field types |
|
|
Sum of field values |
Numeric |
|
|
Maximum value |
Numeric |
|
|
Minimum value |
Numeric |
|
|
Average value |
Numeric |
|
|
Sum of squared values |
Numeric |
|
|
Statistical variance |
Numeric |
|
|
Standard deviation |
Numeric |
|
|
Count of values (without deduplication) |
Numeric, |
|
|
All of the above metrics in a single response |
Numeric |
Onlyvalue_countsupportskeywordfields. All other metrics require numeric fields.
Output: A DOUBLE value computed from the specified field.
Terms aggregation
Counts deduplicated values in a keyword field. Supports nested sub-aggregations.
|
Parameter |
Description |
Default |
|
|
The aggregation field. Only |
Required |
|
|
Number of buckets to return. Valid values: |
|
|
|
Sort order. |
|
|
|
Minimum document count for a bucket to be included in the results. |
|
|
|
A regex pattern or exact value that bucket keys must match. For string fields, regex matching is supported. For array fields, exact matching is required. |
-- |
|
|
A regex pattern or exact value that bucket keys must not match. If both |
-- |
Output: A JSON object with buckets. Each bucket contains key (the field value) and doc_count (the document count):
{
"aggregations": {
"Per_Investor_Freq": {
"buckets": [
{"doc_count": 2, "key": "Jay"},
{"doc_count": 1, "key": "Mila"}
]
}
}
}
Example:
{"aggs": {"Per_Investor_Freq": {"terms": {"field": "investor"}}}}
Filter aggregation
Filters query results with a query statement and counts matching documents. Supports nested sub-aggregations.
Output: The number of documents (doc_count) matching the filter condition.
Aggregation examples
The following examples use a stock trading dataset.
Step 1: Create the index
TFT.CREATEINDEX today_shares '{"mappings":{"properties":{"shares_name":{"type":"keyword"},"logictime":{"type":"long"},"purchase_type":{"type":"integer"},"purchase_price":{"type":"double"},"purchase_count":{"type":"long"},"investor":{"type":"keyword"}}}}'
Step 2: Add documents
TFT.ADDDOC today_shares '{"shares_name":"XAX","logictime":14300210,"purchase_type":1,"purchase_price":101.1,"purchase_count":100,"investor":"Jay"}'
TFT.ADDDOC today_shares '{"shares_name":"XAX","logictime":14300310,"purchase_type":1,"purchase_price":111.1,"purchase_count":100,"investor":"Jay"}'
TFT.ADDDOC today_shares '{"shares_name":"YBY","logictime":14300410,"purchase_type":1,"purchase_price":11.1,"purchase_count":100,"investor":"Mila"}'
Step 3: Run queries
sum -- Total purchase amount for an investor
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Sum":{"sum":{"field":"purchase_price"}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Sum":{"value":212.2}}}
max -- Highest purchase price for an investor
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Max":{"max":{"field":"purchase_price"}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Max":{"value":111.1}}}
avg -- Average purchase price for an investor
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Avg":{"avg":{"field":"purchase_price"}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Avg":{"value":106.1}}}
std_deviation -- Standard deviation of purchase prices
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Std_Deviation":{"std_deviation":{"field":"purchase_price"}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Std_Deviation":{"value":5.0}}}
extended_stats -- All statistics at once
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"investor":"Jay"}},"aggs":{"Jay_Extended_Stats":{"extended_stats":{"field":"purchase_price"}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":2}},"aggregations":{"Jay_Extended_Stats":{"count":2,"sum":212.2,"max":111.1,"min":101.1,"avg":106.1,"sum_of_squares":10221.21,"variance":25.0,"std_deviation":5.0}}}
terms -- Investors with at least two transactions
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"purchase_type":1}},"aggs":{"Per_Investor_Freq":{"terms":{"field":"investor","min_doc_count":2,"order":{"_key":"desc"}}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":3}},"aggregations":{"Per_Investor_Freq":{"buckets":[{"key":"Jay","doc_count":2}]}}}
Nested terms aggregation -- Per-stock transaction count with average price
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"purchase_type":1}},"aggs":{"Per_Investor_Freq":{"terms":{"field":"shares_name","include":"[A-Z]+","exclude":["XAX"]},"aggs":{"Price_Avg":{"avg":{"field":"purchase_price"}}}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":3}},"aggregations":{"Per_Investor_Freq":{"buckets":[{"key":"YBY","doc_count":1,"Price_Avg":{"value":11.1}}]}}}
Nested filter aggregation -- Filter by investor with extended stats
TFT.SEARCH today_shares '{"size":0,"query":{"term":{"purchase_type":1}},"aggs":{"Jay_BuyIn_Filter":{"filter":{"term":{"investor":"Jay"}},"aggs":{"Jay_BuyIn_Quatation":{"extended_stats":{"field":"purchase_price"}}}}}}'{"hits":{"hits":[],"max_score":null,"total":{"relation":"eq","value":3}},"aggregations":{"Jay_BuyIn_Filter":{"doc_count":2,"Jay_BuyIn_Quatation":{"count":2,"sum":212.2,"max":111.1,"min":101.1,"avg":106.1,"sum_of_squares":10221.21,"variance":25.0,"std_deviation":5.0}}}}