From 411ad809b083370f5a0b7ae087fff37bf40ba6d3 Mon Sep 17 00:00:00 2001 From: Raz Monsonego Date: Sun, 16 Aug 2026 18:41:02 +0300 Subject: [PATCH 1/2] Clarify that the FT.SEARCH count is the number of hits The first element of the FT.SEARCH reply (total_results in RESP3) was described only as "the total number of results", which reads as the number of documents in the reply. It is the number of hits: every document that matches the query, independent of LIMIT. - Say "number of hits, that is, the number of documents that match the query" in the Return and Return information sections. - Contrast it with the LIMIT-bounded document list, with an example and a pointer to LIMIT 0 0 for the count alone. - Note the one case where the number is not a full match count: the DIALECT 4 / WITHOUTCOUNT sorting optimizations cap it at the LIMIT window (QOptimizer_UpdateTotalResults in RediSearch), and WITHCOUNT restores an accurate count. Co-Authored-By: Claude Opus 5 (1M context) --- content/commands/ft.search.md | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/content/commands/ft.search.md b/content/commands/ft.search.md index 88454b52f3..437116fcbb 100644 --- a/content/commands/ft.search.md +++ b/content/commands/ft.search.md @@ -505,12 +505,29 @@ selects the dialect version under which to execute the query. If not specified, ## Return -FT.SEARCH returns an array reply, where the first element is an integer reply of the total number of results, and then array reply pairs of document ids, and array replies of attribute/value pairs. +FT.SEARCH returns an array reply, where the first element is an integer reply of the total number of hits, that is, the number of documents that match the query, and then array reply pairs of document ids, and array replies of attribute/value pairs. + +The number of hits is not the number of documents returned by the reply. `LIMIT` controls how many of the matching documents follow the count, and it defaults to `0 10`, so a query with more than 10 hits returns a count that is greater than the number of documents in the reply. Use `LIMIT 0 0` to get the number of hits without any documents. + +For example, if 42 documents match the query, the count is `42` even though only the first 10 documents are returned: + +```sh +127.0.0.1:6379> FT.SEARCH idx "@title:redis" NOCONTENT + 1) (integer) 42 + 2) "doc:1" + 3) "doc:2" +... +11) "doc:10" + +127.0.0.1:6379> FT.SEARCH idx "@title:redis" LIMIT 0 0 +1) (integer) 42 +``` {{% alert title="Notes" color="warning" %}} -- If `NOCONTENT` is given, an array is returned where the first element is the total number of results, and the rest of the members are document ids. -- If a relevant key expires or is updated while a query is running, an attempt to load the updated key's value will return a null array. However, the key is still counted in the total number of results. +- If `NOCONTENT` is given, an array is returned where the first element is the number of hits, and the rest of the members are document ids. +- If a relevant key expires or is updated while a query is running, an attempt to load the updated key's value will return a null array. However, the key is still counted as a hit. +- The number of hits is accurate unless the query uses the sorting optimizations of `DIALECT 4` or `WITHOUTCOUNT`. Those queries stop as soon as they collect the results requested by `LIMIT`, so the reported number is capped at the size of the `LIMIT` window instead of counting every matching document. Add `WITHCOUNT` to a `SORTBY` clause to get an accurate count. {{% /alert %}} @@ -856,15 +873,15 @@ Query with `CONTAINS` operator: tab2="RESP3" >}} One of the following: -* [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) with the first element being the total number of results, followed by document IDs and their field-value pairs as [arrays]({{< relref "/develop/reference/protocol-spec#arrays" >}}). +* [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) with the first element being the number of hits, that is, the total number of documents that match the query, followed by document IDs and their field-value pairs as [arrays]({{< relref "/develop/reference/protocol-spec#arrays" >}}). The number of documents in the reply is limited by `LIMIT` and is therefore often smaller than the number of hits. * [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) in these cases: no such index, syntax error in query. -tab-sep- One of the following: * [Map]({{< relref "/develop/reference/protocol-spec#maps" >}}) with the following fields: - - `total_results`: [Integer]({{< relref "/develop/reference/protocol-spec#integers" >}}) - total number of results - - `results`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of [maps]({{< relref "/develop/reference/protocol-spec#maps" >}}) containing document information + - `total_results`: [Integer]({{< relref "/develop/reference/protocol-spec#integers" >}}) - number of hits, that is, the total number of documents that match the query, regardless of how many are returned in `results` + - `results`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of [maps]({{< relref "/develop/reference/protocol-spec#maps" >}}) containing document information, limited by `LIMIT` - `attributes`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of attribute names - `format`: [Simple string]({{< relref "/develop/reference/protocol-spec#simple-strings" >}}) - result format - `warning`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of warning messages From 41e7e18c79e6a20e8553b499868b505d27b241aa Mon Sep 17 00:00:00 2001 From: Raz Monsonego Date: Tue, 18 Aug 2026 10:59:29 +0300 Subject: [PATCH 2/2] Apply review wording from @dwdougherty Drop "hits" in favor of "documents that match the query" throughout the Return and Return information sections, and take David's phrasing for the intro paragraphs, the three notes, and the RESP2/RESP3 reply descriptions verbatim. Kept the inline code formatting on LIMIT / 0 10 / LIMIT 0 0, which the suggestion text dropped only because it was typed as plain prose; the rest of the page and David's other suggestions backtick those tokens. Co-Authored-By: Claude Opus 5 (1M context) --- content/commands/ft.search.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/commands/ft.search.md b/content/commands/ft.search.md index 437116fcbb..bf7b653cc9 100644 --- a/content/commands/ft.search.md +++ b/content/commands/ft.search.md @@ -505,11 +505,11 @@ selects the dialect version under which to execute the query. If not specified, ## Return -FT.SEARCH returns an array reply, where the first element is an integer reply of the total number of hits, that is, the number of documents that match the query, and then array reply pairs of document ids, and array replies of attribute/value pairs. +FT.SEARCH returns an array reply. The first element is an integer reply that contains the total number of documents that match the query. The remaining elements are pairs: a document ID followed by an array of attribute-value pairs. -The number of hits is not the number of documents returned by the reply. `LIMIT` controls how many of the matching documents follow the count, and it defaults to `0 10`, so a query with more than 10 hits returns a count that is greater than the number of documents in the reply. Use `LIMIT 0 0` to get the number of hits without any documents. +This total is not necessarily the number of documents returned in the reply. `LIMIT` controls how many matching documents follow the count. It defaults to `0 10`, so a query with more than 10 matching documents returns a count greater than the number of returned documents. Use `LIMIT 0 0` to get only the total. -For example, if 42 documents match the query, the count is `42` even though only the first 10 documents are returned: +For example, if 42 documents match the query, the count is 42, even though the reply returns only the first 10 documents: ```sh 127.0.0.1:6379> FT.SEARCH idx "@title:redis" NOCONTENT @@ -525,9 +525,9 @@ For example, if 42 documents match the query, the count is `42` even though only {{% alert title="Notes" color="warning" %}} -- If `NOCONTENT` is given, an array is returned where the first element is the number of hits, and the rest of the members are document ids. -- If a relevant key expires or is updated while a query is running, an attempt to load the updated key's value will return a null array. However, the key is still counted as a hit. -- The number of hits is accurate unless the query uses the sorting optimizations of `DIALECT 4` or `WITHOUTCOUNT`. Those queries stop as soon as they collect the results requested by `LIMIT`, so the reported number is capped at the size of the `LIMIT` window instead of counting every matching document. Add `WITHCOUNT` to a `SORTBY` clause to get an accurate count. +- If you specify `NOCONTENT`, the command returns an array where the first element is the total number of matching documents and the remaining elements are document IDs. +- If a matching key expires or is updated while a query is running, Redis returns a null array when it tries to load the updated key's value. The key still counts as a matching document. +- The number of matching documents is accurate unless the query uses the sorting optimizations of `DIALECT 4` or `WITHOUTCOUNT`. These queries stop as soon as they collect the results requested by `LIMIT`, so the reported number is capped at the size of the `LIMIT` window instead of counting every matching document. Add `WITHCOUNT` to a `SORTBY` clause to get an accurate count. {{% /alert %}} @@ -873,14 +873,14 @@ Query with `CONTAINS` operator: tab2="RESP3" >}} One of the following: -* [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) with the first element being the number of hits, that is, the total number of documents that match the query, followed by document IDs and their field-value pairs as [arrays]({{< relref "/develop/reference/protocol-spec#arrays" >}}). The number of documents in the reply is limited by `LIMIT` and is therefore often smaller than the number of hits. +* [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}): the first element is the total number of documents that match the query. The remaining elements are document IDs followed by their field-value pairs as [arrays]({{< relref "/develop/reference/protocol-spec#arrays" >}}). The number of documents returned in the reply is limited by `LIMIT`, so it is often smaller than the total number of matching documents. * [Simple error reply]({{< relref "/develop/reference/protocol-spec#simple-errors" >}}) in these cases: no such index, syntax error in query. -tab-sep- One of the following: * [Map]({{< relref "/develop/reference/protocol-spec#maps" >}}) with the following fields: - - `total_results`: [Integer]({{< relref "/develop/reference/protocol-spec#integers" >}}) - number of hits, that is, the total number of documents that match the query, regardless of how many are returned in `results` + - `total_results`: [Integer]({{< relref "/develop/reference/protocol-spec#integers" >}}) - the total number of documents that match the query, regardless of how many are returned in `results` - `results`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of [maps]({{< relref "/develop/reference/protocol-spec#maps" >}}) containing document information, limited by `LIMIT` - `attributes`: [Array]({{< relref "/develop/reference/protocol-spec#arrays" >}}) of attribute names - `format`: [Simple string]({{< relref "/develop/reference/protocol-spec#simple-strings" >}}) - result format