> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Redis Search Queries

Queries are JSON strings that describe which documents to return. If the index doesn't exist, queries return `null`.

Use the [`SEARCH.QUERY`](https://upstash.com/docs/redis/commands/search/search-query) command to run a query.

We recommend searching by field values directly because we automatically provide intelligent matching behavior out of the box:

> [tab] TypeScript
>
> ```ts theme={"system"}
> // Basic search
> await index.query({
>   filter: { name: "headphones" },
> });
>
> // Search across multiple fields (implicit AND)
> await index.query({
>   filter: { name: "wireless", category: "electronics" },
> });
>
> // Search with exact values for non-text fields
> await index.query({
>   filter: { inStock: true, price: 199.99 },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Basic search
> index.query(filter={"name": "headphones"})
>
> # Search across multiple fields (implicit AND)
> index.query(filter={"name": "wireless", "category": "electronics"})
>
> # Search with exact values for non-text fields
> index.query(filter={"inStock": True, "price": 199.99})
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Search for a term in a specific field
> SEARCH.QUERY products '{"name": "headphones"}'
>
> # Search across multiple fields (implicit AND)
> SEARCH.QUERY products '{"name": "wireless", "category": "electronics"}'
>
> # Search with exact values for non-text fields
> SEARCH.QUERY products '{"inStock": true, "price": 199.99}'
> ```

***

### Response Format

The query response is an array of matching documents. Each document includes the Redis key, a relevance score, and the document content.

> [tab] TypeScript
>
> ```ts theme={"system"}
> const results = await index.query({
>   filter: { name: "headphones" },
> });
>
> // results is an array of objects:
> // [
> //   { key: "product:1", score: 5.23, data: { name: "Wireless Headphones", price: 99.99, ... } },
> //   { key: "product:2", score: 3.11, data: { name: "Studio Headphones", price: 149.99, ... } },
> // ]
> ```

> [tab] Python
>
> ```python theme={"system"}
> results = index.query(filter={"name": "headphones"})
>
> # results is a list of objects:
> # [
> #   QueryResult(key="product:1", score=5.23, data={"name": "Wireless Headphones", "price": 99.99, ...}),
> #   QueryResult(key="product:2", score=3.11, data={"name": "Studio Headphones", "price": 149.99, ...}),
> # ]
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> SEARCH.QUERY products '{"name": "headphones"}'
>
> # Response:
> # [
> #   ["product:1", "5.23", [["$", "{\"name\": \"Wireless Headphones\", \"price\": 99.99}"]]],
> #   ["product:2", "3.11", [["$", "{\"name\": \"Studio Headphones\", \"price\": 149.99}"]]]
> # ]
> ```

When `select` / `NOCONTENT` is used, the response shape changes — see [Controlling Output](#3-controlling-output).

If no documents match, an empty array is returned. If the index doesn't exist, `null` is returned.

For exact syntax and a detailed breakdown of the raw response structure, see the [`SEARCH.QUERY` command reference](https://upstash.com/docs/redis/commands/search/search-query).

***

### Smart Matching

When you provide a value directly to a field (without explicit operators),
we automatically apply [smart matching](./query-operators/field-operators/smart-matching).
For numeric, boolean, and date fields, this performs an exact equality check.
For text fields, it works like this:

* **Single-word values**: Performs a term search, matching the word against tokens in the field.
* **Multi-word values**: Combines phrase matching, term matching, and fuzzy matching with
  different boost weights to rank exact phrases highest while still finding partial matches.
* **Double-quoted phrases**: Forces exact phrase matching (e.g., `"\"noise cancelling\""` matches
  only those words adjacent and in order).

For more control, use explicit operators like [`$phrase`](./query-operators/field-operators/phrase),
or [`$fuzzy`](./query-operators/field-operators/fuzzy).

***

### Hybrid Search

Retrievers add semantic search to queries. The [`$ann`](https://upstash.com/docs/redis/search/retrievers/ann) retriever
searches `HYBRID` fields by meaning, and the [`$rrf`](https://upstash.com/docs/redis/search/retrievers/rrf) retriever merges
the results of several retrievers into one list. See [Hybrid Search](https://upstash.com/docs/redis/search/hybrid-search) for a guide
and [Retrievers](https://upstash.com/docs/redis/search/retrievers/overview) for the reference.

***

## Multi-value fields

`TEXT`, `HYBRID`, `KEYWORD`, `U64`, `I64`, `F64`, and `DATE` fields can contain multiple values. A condition
matches the document when at least one array element matches it. The query value remains a single
value; you do not need to search the whole stored array.

Given this document:

```json theme={"system"}
{
  "tags": ["new", "sale"],
  "descriptions": ["Wireless headphones", "Includes a travel case"],
  "prices": [199.99, 179.99]
}
```

each query below matches it:

> [tab] TypeScript
>
> ```ts theme={"system"}
> await products.query({
>   filter: { tags: { $eq: "sale" } },
> });
>
> await products.query({
>   filter: { prices: { $lt: 180 } },
> });
>
> await products.query({
>   filter: { descriptions: { $phrase: "wireless headphones" } },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> products.query(filter={"tags": {"$eq": "sale"}})
>
> products.query(filter={"prices": {"$lt": 180}})
>
> products.query(filter={"descriptions": {"$phrase": "wireless headphones"}})
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> SEARCH.QUERY products '{"tags":{"$eq":"sale"}}'
>
> SEARCH.QUERY products '{"prices":{"$lt":180}}'
>
> SEARCH.QUERY products '{"descriptions":{"$phrase":"wireless headphones"}}'
> ```

For `TEXT`, phrase matching stays within one array element. For example, `"headphones includes"`
does not match the document above because those words are in different elements.

Search results keep the field's stored array unchanged. When you use `HIGHLIGHT` on a multi-value
`TEXT` field, only matching string elements are marked up in the response. Other elements keep their
original values, and highlighting does not change the stored document.

See [Multi-value fields in schema definitions](https://upstash.com/docs/redis/search/schema-definition#multi-value-fields)
for supported types and the required storage format for JSON, string, hash, and stream indexes.

***

## Query Options

### 1. Pagination with Limit and Offset

Limit controls how many results to return. Offset controls how many results to skip. Together, they provide a way to paginate results.

| Parameter | Description | Default | Constraints |
| - | - | - | - |
| `LIMIT` | Number of results to return | 10 | Must be between 1 and 1000 |
| `OFFSET` | Number of results to skip | 0 | Must be between 0 and 10,000 |

> [!NOTE]
>
> `LIMIT` and `OFFSET` are separate keywords. This differs from RediSearch which uses the combined
> `LIMIT <offset> <count>` syntax. Using `LIMIT 0 10` will return an error.

There is no separate limit on the length of a filter or query string, or on how
deeply filters are nested. A query is bounded only by your plan's maximum
request size (10 MB on the free plan).

> [tab] TypeScript
>
> ```ts theme={"system"}
> // Page 1: first 10 results (with optional offset)
> const page1 = await index.query({
>   filter: { description: "wireless" },
>   limit: 10,
> });
>
> // Page 2: results 11-20
> const page2 = await index.query({
>   filter: { description: "wireless" },
>   limit: 10,
>   offset: 10,
> });
>
> // Page 3: results 21-30
> const page3 = await index.query({
>   filter: { description: "wireless" },
>   limit: 10,
>   offset: 20,
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Page 1: first 10 results
> page1 = index.query(filter={"description": "wireless"}, limit=10)
>
> # Page 2: results 11-20
> page2 = index.query(filter={"description": "wireless"}, limit=10, offset=10)
>
> # Page 3: results 21-30
> page3 = index.query(filter={"description": "wireless"}, limit=10, offset=20)
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Page 1: first 10 results (with optional offset)
> SEARCH.QUERY products '{"description": "wireless"}' LIMIT 10
>
> # Page 2: results 11-20
> SEARCH.QUERY products '{"description": "wireless"}' LIMIT 10 OFFSET 10
>
> # Page 3: results 21-30
> SEARCH.QUERY products '{"description": "wireless"}' LIMIT 10 OFFSET 20
> ```

### 2. Sorting Results

Normally, search results are sorted in descending order of query relevance.

It is possible to override this, and sort the results by a certain field
in ascending or descending order.

Only fields defined as `.fast()` in the schema can be used as the sort field (enabled by default).

When using `orderBy`, the score in results reflects the sort field's value rather than relevance.

> [tab] TypeScript
>
> ```ts theme={"system"}
> // Sort by price, cheapest first
> await products.query({
>   filter: { category: "electronics" },
>   orderBy: { price: "ASC" },
> });
>
> // Sort by date, newest first
> await articles.query({
>   filter: { author: "john" },
>   orderBy: { publishedAt: "DESC" },
> });
>
> // Sort by rating, highest first, which can be combined with LIMIT and OFFSET
> await products.query({
>   filter: { inStock: true },
>   orderBy: { rating: "DESC" },
>   limit: 5,
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Sort by price, cheapest first
> products.query(filter={"category": "electronics"}, order_by={"price": "ASC"})
>
> # Sort by date, newest first
> articles.query(filter={"author": "john"}, order_by={"publishedAt": "DESC"})
>
> # Sort by rating, highest first
> products.query(filter={"inStock": True}, order_by={"rating": "DESC"}, limit=5)
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Sort by price, cheapest first
> SEARCH.QUERY products '{"category": "electronics"}' ORDERBY price ASC
>
> # Sort by date, newest first
> SEARCH.QUERY articles '{"author": "john"}' ORDERBY publishedAt DESC
>
> # Sort by rating, highest first, which can be combined with LIMIT and OFFSET
> SEARCH.QUERY products '{"inStock": true}' ORDERBY rating DESC LIMIT 5
> ```

### 3. Controlling Output

By default, search results include document key, relevance score, and the contents of the document
(including the non-indexed fields).

For JSON and string indexes, that means the stored JSON objects as a whole. For hash indexes, it
means all fields and values. For stream indexes, the document key is the stream entry ID and the
content contains that entry's field-value pairs.

> [tab] TypeScript
>
> ```ts {3} theme={"system"}
> // Example: Return documents without content
> await products.query({
>   select: {},
>   filter: { name: "headphones" },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Return documents without content
> products.query(select={}, filter={"name": "headphones"})
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Return only keys and scores
> SEARCH.QUERY products '{"name": "headphones"}' NOCONTENT
> ```

> [tab] TypeScript
>
> ```ts {3} theme={"system"}
> // Example: Return only `name` and `price`
> await products.query({
>   select: { name: true, price: true },
>   filter: { name: "headphones" },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Return only `name` and `price`
> products.query(select={"name": True, "price": True}, filter={"name": "headphones"})
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Return specific fields only
> SEARCH.QUERY products '{"name": "headphones"}' SELECT 2 name price
> ```

> [!NOTE]
>
> When using [aliased fields](https://upstash.com/docs/redis/search/schema-definition#aliased-fields),
> use the **actual document field name** (not the alias) when selecting fields to return.
> This is because aliasing happens at the index level and does not modify the underlying documents.

***

### 4. Score Function

Score function lets you tweak the relevance scores of search results using numeric field values from your documents.
This is useful when you want to incorporate signals like popularity, recency, or price into the final ranking.

Only `.fast()` fields of type `i64`, `u64`, or `f64` can be used with score function.

#### Field Values

Each `FIELDVALUE` entry references a numeric field and optionally configures
how its value is transformed before being applied to the score:

* **`MODIFIER`** (default: `none`) — A mathematical transformation applied to the field value before use:

| Modifier | Description |
| - | - |
| `none` | Use the field value as-is, with no transformation. |
| `log` | Common logarithm (log base 10). |
| `log1p` | Common logarithm of `1 + value`. |
| `log2p` | Common logarithm of `2 + value`. |
| `ln` | Natural logarithm (log base e). |
| `ln1p` | Natural logarithm of `1 + value`. |
| `ln2p` | Natural logarithm of `2 + value`. |
| `square` | Square of the value (`value²`). |
| `sqrt` | Square root of the value. |
| `reciprocal` | Reciprocal of the value (`1 / value`). |

* **`FACTOR`** (default: `1.0`) — A float multiplier applied **after** the modifier transformation.
* **`MISSING`** (default: `0.0`) — A float fallback value used when:

  * The field value is missing from the document.
  * The field value is invalid for the chosen modifier (e.g., a negative value with `log`).

  If both the field value and the missing value are invalid for the modifier, the final contribution is `MISSING * FACTOR`.

#### Combine Mode

When multiple `FIELDVALUE` entries are specified, `COMBINEMODE` controls how their results are combined:

* **`multiply`** (default) — Multiply all field value results together.
* **`sum`** — Add all field value results together.

#### Score Mode

`SCOREMODE` controls how the combined field value result is applied to the original query relevance score:

* **`multiply`** (default) — Multiply the original score by the combined result.
* **`sum`** — Add the combined result to the original score.
* **`replace`** — Replace the original score with the combined result entirely.

> [tab] TypeScript
>
> ```ts theme={"system"}
> // Boost results by a popularity field
> await products.query({
>   filter: { name: "headphones" },
>   scoreFunc: "popularity",
> });
>
> // Use log1p modifier to dampen high popularity values, with a factor of 2
> await products.query({
>   filter: { name: "headphones" },
>   scoreFunc: {
>     field: "popularity",
>     modifier: "log1p",
>     factor: 2.0,
>   },
> });
>
> // Fallback to 1.0 when the field is missing or invalid
> await products.query({
>   filter: { name: "headphones" },
>   scoreFunc: {
>     field: "popularity",
>     modifier: "log1p",
>     missing: 1.0,
>   },
> });
>
> // Combine two field values: boost by popularity and recency
> await products.query({
>   filter: { name: "headphones" },
>   scoreFunc: {
>     fields: [
>       { field: "popularity", modifier: "log1p" },
>       { field: "recencyScore", modifier: "sqrt" },
>     ],
>     combineMode: "sum",
>   },
> });
>
> // Replace the original score entirely with the combined result
> await products.query({
>   filter: { name: "headphones" },
>   scoreFunc: {
>     field: "rating",
>     scoreMode: "replace",
>   },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Boost results by a popularity field
> products.query(filter={"name": "headphones"}, score_func="popularity")
>
> # Use log1p modifier to dampen high popularity values, with a factor of 2
> products.query(
>     filter={"name": "headphones"},
>     score_func={"field": "popularity", "modifier": "log1p", "factor": 2.0},
> )
>
> # Fallback to 1.0 when the field is missing or invalid
> products.query(
>     filter={"name": "headphones"},
>     score_func={"field": "popularity", "modifier": "log1p", "missing": 1.0},
> )
>
> # Combine two field values: boost by popularity and recency
> products.query(
>     filter={"name": "headphones"},
>     score_func={
>         "fields": [
>             {"field": "popularity", "modifier": "log1p"},
>             {"field": "recencyScore", "modifier": "sqrt"},
>         ],
>         "combineMode": "sum",
>     },
> )
>
> # Replace the original score entirely with the combined result
> products.query(
>     filter={"name": "headphones"},
>     score_func={"field": "rating", "scoreMode": "replace"},
> )
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Boost results by a popularity field
> SEARCH.QUERY products '{"name": "headphones"}' SCOREFUNC FIELDVALUE popularity
>
> # Use log1p modifier to dampen high popularity values, with a factor of 2
> SEARCH.QUERY products '{"name": "headphones"}' SCOREFUNC FIELDVALUE popularity MODIFIER log1p FACTOR 2.0
>
> # Fallback to 1.0 when the field is missing or invalid
> SEARCH.QUERY products '{"name": "headphones"}' SCOREFUNC FIELDVALUE popularity MODIFIER log1p MISSING 1.0
>
> # Combine two field values: boost by popularity and recency
> SEARCH.QUERY products '{"name": "headphones"}' SCOREFUNC COMBINEMODE sum FIELDVALUE popularity MODIFIER log1p FIELDVALUE recencyScore MODIFIER sqrt
>
> # Replace the original score entirely with the combined result
> SEARCH.QUERY products '{"name": "headphones"}' SCOREFUNC SCOREMODE replace FIELDVALUE rating
> ```

> [!WARNING]
>
> `SCOREFUNC` cannot be used together with `ORDERBY`. Since `ORDERBY` overrides the relevance
> score with the sort field's value, combining it with score function is not supported.

***

### 5. Highlighting

Highlighting allows you to see why a document matched the query by marking the matching portions of the document's fields.

By default, `<em>` and `</em>` are used as the highlight tags.

> [tab] TypeScript
>
> ```ts theme={"system"}
> // Highlight matching terms
> await products.query({
>   filter: { description: "wireless noise cancelling" },
>   highlight: { fields: ["description"] },
> });
>
> // Custom open and close highlight tags
> await products.query({
>   filter: { description: "wireless" },
>   highlight: { fields: ["description"], preTag: "!!", postTag: "**" },
> });
> ```

> [tab] Python
>
> ```python theme={"system"}
> # Highlight matching terms
> products.query(
>     filter={"description": "wireless noise cancelling"},
>     highlight={"fields": ["description"]},
> )
>
> # Custom open and close highlight tags
> products.query(
>     filter={"description": "wireless"},
>     highlight={"fields": ["description"], "preTag": "!!", "postTag": "**"},
> )
> ```

> [tab] Redis CLI
>
> ```bash theme={"system"}
> # Highlight matching terms
> SEARCH.QUERY products '{"description": "wireless noise cancelling"}' HIGHLIGHT FIELDS 1 description
>
> # Custom open and close highlight tags
> SEARCH.QUERY products '{"description": "wireless"}' HIGHLIGHT FIELDS 1 description TAGS !! **
> ```

Note that highlighting only works for operators that resolve to terms, such as term or phrase queries.

> [!NOTE]
>
> When using [aliased fields](https://upstash.com/docs/redis/search/schema-definition#aliased-fields),
> use the **alias name** (not the actual document field name) when specifying fields to highlight.
> The highlighting feature works with indexed field names, which are the aliases.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.