Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
327 changes: 323 additions & 4 deletions _aggregations/bucket/terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@

The `terms` aggregation dynamically creates a bucket for each unique term of a field.

## Parameters

The `terms` aggregation takes the following parameters.

| Parameter | Required/Optional | Data type | Description |
| :--- | :--- | :--- | :--- |
| `field` | Optional | String | The field to aggregate on. Must be a `keyword`, `numeric`, `ip`, `boolean`, or `date` field. Either `field` or `script` is required. |
| `script` | Optional | Object | A script that generates values to aggregate on. Either `field` or `script` is required. When used with `field`, the script acts as a value script and receives the field value as `_value`. |
| `size` | Optional | Integer | The number of buckets to return. Default is `10`. |
| `shard_size` | Optional | Integer | The number of candidate terms collected from each shard. Higher values improve accuracy. Default is `size * 1.5 + 10`. |
| `min_doc_count` | Optional | Integer | The minimum document count for a bucket to be included in the response. Default is `1`. |
| `shard_min_doc_count` | Optional | Integer | The minimum document count on a shard level for a term to be a candidate. Default is `0`. |
| `show_term_doc_count_error` | Optional | Boolean | When `true`, includes a per-bucket error estimate. Default is `false`. |
| `order` | Optional | Object | Controls the sort order of buckets. Accepts `_count`, `_key`, or the name of a subaggregation metric, each with `asc` or `desc`. Default is `{"_count": "desc"}`. |
| `include` | Optional | String, Array, or Object | Filters which term values can create buckets. Accepts a regex string, an array of exact values, or a `partition` object. |
| `exclude` | Optional | String or Array | Filters which term values cannot create buckets. Accepts a regex string or an array of exact values. |
| `missing` | Optional | String or Number | The value to use for documents missing the target field, placing them in the corresponding bucket. By default, missing documents are ignored. |
| `execution_hint` | Optional | String | Controls how terms are collected. Valid values are `map` (holds values directly in memory) and `global_ordinals` (uses ordinal mappings, more memory efficient for high-cardinality fields). OpenSearch selects the best option automatically. |
| `collect_mode` | Optional | String | Controls how nested aggregations are computed. Valid values are:<br> - `depth_first`: Expands all branches before pruning. <br> - `breadth_first`: Prunes at each level before expanding, reducing memory for deeply nested aggregations. <br><br>Default is `depth_first`. |

## Example

The following example uses the `terms` aggregation to find the number of documents per response code in web log data:

```json
Expand Down Expand Up @@ -63,7 +85,7 @@
{: .warning}


## Size and shard size parameters
## The size and shard_size parameters

The number of buckets returned by the `terms` aggregation is controlled by the `size` parameter, which is 10 by default.

Expand Down Expand Up @@ -187,14 +209,311 @@
Additionally, there is memory overhead associated with performing `breadth_first` collection, which is linearly related to the number of matching documents. This is because `breadth_first` collection works by caching and replaying the pruned set of buckets from the parent level.


## Account for pre-aggregated data
## Sort order

By default, buckets are sorted by `_count` descending (most documents first). You can change the sort order using the `order` parameter. The following example sorts response codes alphabetically:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field": "response.keyword",
"order": { "_key": "asc" }
}
}
}
}
```
{% include copy-curl.html %}

The response returns buckets sorted by key in ascending order:

```json
{
...
"aggregations" : {
"response_codes" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "200",
"doc_count" : 12832
},
{
"key" : "404",
"doc_count" : 801
},
{
"key" : "503",
"doc_count" : 441
}
]
}
}
}
```

You can also sort by a subaggregation metric. The following example sorts response codes by average bytes descending:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field": "response.keyword",
"order": { "avg_bytes": "desc" }
},
"aggs": {
"avg_bytes": {
"avg": { "field": "bytes" }
}
}
}
}
}
```
{% include copy-curl.html %}

To order by a metric nested deeper in the aggregation tree, use the `>` separator to specify the path. The following example sorts operating systems by average bytes of only their successful (200) responses:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os": {
"terms": {
"field": "machine.os.keyword",
"size": 3,
"order": { "successful>avg_bytes": "desc" }
},
"aggs": {
"successful": {
"filter": { "term": { "response.keyword": "200" } },
"aggs": {
"avg_bytes": {
"avg": { "field": "bytes" }
}
}
}
}
}
}
}
```
{% include copy-curl.html %}

The response sorts OS buckets by the nested `avg_bytes` metric:

Check warning on line 312 in _aggregations/bucket/terms.md

View workflow job for this annotation

GitHub Actions / style-job

[vale] reported by reviewdog 🐶 [OpenSearch.SubstitutionsWarningCaseSensitive] Use 'OpenSearch or operating system' instead of 'OS'. Raw Output: {"message": "[OpenSearch.SubstitutionsWarningCaseSensitive] Use 'OpenSearch or operating system' instead of 'OS'.", "location": {"path": "_aggregations/bucket/terms.md", "range": {"start": {"line": 312, "column": 20}}}, "severity": "WARNING"}

```json
{
...
"aggregations" : {
"os" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 5639,
"buckets" : [
{
"key" : "win xp",
"doc_count" : 2885,
"successful" : {
"doc_count" : 2549,
"avg_bytes" : {
"value" : 6083.602196939976
}
}
},
{
"key" : "ios",
"doc_count" : 2737,
"successful" : {
"doc_count" : 2512,
"avg_bytes" : {
"value" : 5954.860270700637
}
}
},
{
"key" : "win 8",
"doc_count" : 2813,
"successful" : {
"doc_count" : 2585,
"avg_bytes" : {
"value" : 5910.602321083172
}
}
}
]
}
}
}
```

Pipeline aggregations cannot be used for sorting.
{: .note}

## Multi-field terms aggregation

The `terms` aggregation does not natively support multiple fields. To group by a combination of fields, use the [`multi_terms` aggregation]({{site.url}}{{site.baseurl}}/aggregations/bucket/multi-terms/) or a `script` that combines field values:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os_and_response": {
"terms": {
"script": {
"source": "doc['machine.os.keyword'].value + ' - ' + doc['response.keyword'].value"
},
"size": 5
}
}
}
}
```
{% include copy-curl.html %}

For better performance, consider using a [`multi_terms` aggregation]({{site.url}}{{site.baseurl}}/aggregations/bucket/multi-terms/) or creating a combined field at index time using [`copy_to`]({{site.url}}{{site.baseurl}}/mappings/mapping-parameters/copy-to/).

## Mixing field types across indexes

When the same field name has different numeric types across indexes (for example, `long` in one index and `double` in another), the `terms` aggregation promotes values to the broader type. Non-decimal values are cast to `double`, which can result in a loss of precision for values exceeding 2^53.
{: .warning}

## Missing values

The `missing` parameter assigns a value to documents that do not have the target field, placing them in the corresponding bucket. By default, documents without the field are excluded from the aggregation. The following example assigns `"unknown"` to any documents missing the `machine.os.keyword` field:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os": {
"terms": {
"field": "machine.os.keyword",
"missing": "unknown"
}
}
}
}
```
{% include copy-curl.html %}

## Script

You can use a `script` instead of a `field` to compute term values dynamically. The following example classifies log entries into size categories based on the `bytes` field:

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"byte_ranges": {
"terms": {
"script": {
"source": "if (doc['bytes'].value < 5000) return 'small'; if (doc['bytes'].value < 10000) return 'medium'; return 'large';"
}
}
}
}
}
```
{% include copy-curl.html %}

The response groups documents into computed categories:

```json
{
...
"aggregations" : {
"byte_ranges" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "medium",
"doc_count" : 6995
},
{
"key" : "small",
"doc_count" : 6377
},
{
"key" : "large",
"doc_count" : 702
}
]
}
}
}
```

### Value script

When both `field` and `script` are specified, the script acts as a value script and receives the field value as `_value`. The following example prefixes each response code with "HTTP ":

```json
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"responses_prefixed": {
"terms": {
"field": "response.keyword",
"script": {
"source": "'HTTP ' + _value"
}
}
}
}
}
```
{% include copy-curl.html %}

The response shows the transformed keys:

```json
{
...
"aggregations" : {
"responses_prefixed" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "HTTP 200",
"doc_count" : 12832
},
{
"key" : "HTTP 404",
"doc_count" : 801
},
{
"key" : "HTTP 503",
"doc_count" : 441
}
]
}
}
}
```

## Accounting for preaggregated data

While the `doc_count` field provides a representation of the number of individual documents aggregated in a bucket, `doc_count` by itself does not have a way to correctly increment documents that store pre-aggregated data. To account for pre-aggregated data and accurately calculate the number of documents in a bucket, you can use the `_doc_count` field to add the number of documents in a single summary field. When a document includes the `_doc_count` field, all bucket aggregations recognize its value and increase the bucket `doc_count` cumulatively. Keep these considerations in mind when using the `_doc_count` field:
While the `doc_count` field provides a representation of the number of individual documents aggregated in a bucket, `doc_count` by itself does not have a way to correctly increment documents that store preaggregated data. To account for preaggregated data and accurately calculate the number of documents in a bucket, you can use the `_doc_count` field to add the number of documents in a single summary field. When a document includes the `_doc_count` field, all bucket aggregations recognize its value and increase the bucket `doc_count` cumulatively. Keep these considerations in mind when using the `_doc_count` field:

* The field does not support nested arrays; only positive integers can be used.
* If a document does not contain the `_doc_count` field, aggregation uses the document to increase the count by 1.

OpenSearch features that rely on an accurate document count illustrate the importance of using the `_doc_count` field. To see how this field can be used to support other search tools, refer to [Index rollups]({{site.url}}{{site.baseurl}}/im-plugin/index-rollups/index/), an OpenSearch feature for the Index Management (IM) plugin that stores documents with pre-aggregated data in rollup indexes.
OpenSearch features that rely on an accurate document count illustrate the importance of using the `_doc_count` field. To see how this field can be used to support other search tools, refer to [Index rollups]({{site.url}}{{site.baseurl}}/im-plugin/index-rollups/index/), an OpenSearch feature for the Index Management (IM) plugin that stores documents with preaggregated data in rollup indexes.
{: .tip}

#### Example request
Expand Down
Loading