[[search-request-track-total-hits]]
=== Track total hits

Generally the total hit count can't be computed accurately without visiting all
matches, which is costly for queries that match lots of documents. The
`track_total_hits` parameter allows you to control how the total number of hits
should be tracked.
Given that it is often enough to have a lower bound of the number of hits,
such as "there are at least 10000 hits", the default is set to `10,000`.
This means that requests will count the total hit accurately up to `10,000` hits.
It's is a good trade off to speed up searches if you don't need the accurate number
of hits after a certain threshold.

When set to `true` the search response will always track the number of hits that
match the query accurately (e.g. `total.relation` will always be equal to `"eq"`
when `track_total_hits` is set to true). Otherwise the `"total.relation"` returned
in the `"total"` object in the search response determines how the `"total.value"`
should be interpreted. A value of `"gte"` means that the `"total.value"` is a
lower bound of the total hits that match the query and a value of `"eq"` indicates
that `"total.value"` is the accurate count.

[source,js]
--------------------------------------------------
GET twitter/_search
{
    "track_total_hits": true,
     "query": {
        "match" : {
            "message" : "Elasticsearch"
        }
     }
}
--------------------------------------------------
// TEST[setup:twitter]
// CONSOLE

\... returns:

[source,js]
--------------------------------------------------
{
    "_shards": ...
    "timed_out": false,
    "took": 100,
    "hits": {
        "max_score": 1.0,
        "total" : {
            "value": 2048,    <1>
            "relation": "eq"  <2>
        },
        "hits": ...
    }
}
--------------------------------------------------
// TESTRESPONSE[s/"_shards": \.\.\./"_shards": "$body._shards",/]
// TESTRESPONSE[s/"took": 100/"took": $body.took/]
// TESTRESPONSE[s/"max_score": 1\.0/"max_score": $body.hits.max_score/]
// TESTRESPONSE[s/"value": 2048/"value": $body.hits.total.value/]
// TESTRESPONSE[s/"hits": \.\.\./"hits": "$body.hits.hits"/]

<1> The total number of hits that match the query.
<2> The count is accurate (e.g. `"eq"` means equals).

It is also possible to set `track_total_hits` to an integer.
For instance the following query will accurately track the total hit count that match
the query up to 100 documents:

[source,js]
--------------------------------------------------
GET twitter/_search
{
    "track_total_hits": 100,
     "query": {
        "match" : {
            "message" : "Elasticsearch"
        }
     }
}
--------------------------------------------------
// CONSOLE
// TEST[continued]

The `hits.total.relation` in the response will indicate if the
value returned in `hits.total.value` is accurate (`"eq"`) or a lower
bound of the total (`"gte"`).

For instance the following response:

[source,js]
--------------------------------------------------
{
    "_shards": ...
    "timed_out": false,
    "took": 30,
    "hits" : {
        "max_score": 1.0,
        "total" : {
            "value": 42,         <1>
            "relation": "eq"     <2>
        },
        "hits": ...
    }
}
--------------------------------------------------
// TESTRESPONSE[s/"_shards": \.\.\./"_shards": "$body._shards",/]
// TESTRESPONSE[s/"took": 30/"took": $body.took/]
// TESTRESPONSE[s/"max_score": 1\.0/"max_score": $body.hits.max_score/]
// TESTRESPONSE[s/"value": 42/"value": $body.hits.total.value/]
// TESTRESPONSE[s/"hits": \.\.\./"hits": "$body.hits.hits"/]

<1> 42 documents match the query
<2> and the count is accurate (`"eq"`)

\... indicates that the number of hits returned in the `total`
is accurate.

If the total number of his that match the query is greater than the
value set in `track_total_hits`, the total hits in the response
will indicate that the returned value is a lower bound:

[source,js]
--------------------------------------------------
{
    "_shards": ...
    "hits" : {
        "max_score": 1.0,
        "total" : {
            "value": 100,         <1>
            "relation": "gte"     <2>
        },
        "hits": ...
    }
}
--------------------------------------------------
// TESTRESPONSE
// TEST[skip:response is already tested in the previous snippet]

<1> There are at least 100 documents that match the query
<2> This is a lower bound (`"gte"`).

If you don't need to track the total number of hits at all you can improve query
times by setting this option to `false`:

[source,js]
--------------------------------------------------
GET twitter/_search
{
    "track_total_hits": false,
     "query": {
        "match" : {
            "message" : "Elasticsearch"
        }
     }
}
--------------------------------------------------
// CONSOLE
// TEST[continued]

\... returns:

[source,js]
--------------------------------------------------
{
    "_shards": ...
    "timed_out": false,
    "took": 10,
    "hits" : { <1>
        "max_score": 1.0,
        "hits": ...
    }
}
--------------------------------------------------
// TESTRESPONSE[s/"_shards": \.\.\./"_shards": "$body._shards",/]
// TESTRESPONSE[s/"took": 10/"took": $body.took/]
// TESTRESPONSE[s/"max_score": 1\.0/"max_score": $body.hits.max_score/]
// TESTRESPONSE[s/"hits": \.\.\./"hits": "$body.hits.hits"/]

<1> The total number of hits is unknown.

Finally you can force an accurate count by setting `"track_total_hits"`
to `true` in the request.
