
## Aggregate search results

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/search/references/aggregation

> Aggregate search results programmatically using the Atlan Python SDK (pyatlan) with bucket and metric aggregation techniques.

# Aggregate search results

You can aggregate information about your search results in a few ways.

Currently only the following are implemented through the SDKs, though Elasticsearch itself supports many additional scenarios[^1].

## Bucket aggregation

You can group results together based on a field using bucket aggregation. With this, you can answer questions like:

> Which kinds of assets most frequently match my search criteria?

For example:

### Java

```java showLineNumbers title="Build a bucket aggregation"
IndexSearchRequest request = client.assets.select() // (1)
 .aggregate("type", Asset.TYPE_NAME.bucketBy()) // (2)
 .sort(Asset.CREATE_TIME.order(SortOrder.Desc))
 .toRequest(); // (3)
IndexSearchResponse response = request.search(client); // (4)
```

1. Start building a query from a client, using its 'assets' member's `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will bucket the results based on the distinct types of assets (tables, columns, etc).

3. You can then turn these criteria into a search request using the `toRequest()` helper.
4. And once you have a request, you can then run the search. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```java title="Do something with the results"
Map aggregates = response.getAggregations(); // (1)
AggregationBucketResult result = (AggregationBucketResult) aggregates.get("type"); // (2)
List buckets = result.getBuckets(); // (3)
for (AggregationBucketDetails detail : buckets)
```

1. From the search response, not only can you retrieve the results (as in previous examples), but when an aggregation is requested you can also retrieve the aggregation result.
2. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name. (You would probably want to type-check this before the explicit cast.)
3. If the result is to a request that produces aggregation buckets, there will be bucket-specific details within it.
4. You can iterate through these details...
5. ...to retrieve the key of the bucket (in the example this would be the type of asset: table, column, etc).
6. ...to retrieve the number of results that match that bucket key (in the example, how many tables, columns, etc there are in the results).

### Python

```python showLineNumbers title="Build a bucket aggregation"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Asset
from pyatlan.model.fluent_search import FluentSearch

client = AtlanClient()
request = (
 FluentSearch.select() # (1)
 .aggregate("type", Asset.TYPE_NAME.bucket_by()) # (2)
 .sort(Asset.CREATE_TIME.order())
).to_request() # (3)
results = client.asset.search(criteria=request) # (4)
```

1. Start building a query from a FluentSearch, using its `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will bucket the results based on the distinct types of assets (tables, columns, etc).

3. You can then turn these criteria into a search request using the `to_request()` helper.
4. And once you have a request, you can then run the search.

```python title="Do something with the results"
result = results.aggregations["type"] # (1)
buckets = result.buckets: # (2)
for detail in buckets: # (3)
 detail.key # (4)
 detail.doc_count # (5)
```

1. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name.
2. If the result is to a request that produces aggregation buckets, there will be bucket-specific details within it.
3. You can iterate through these details...
4. ...to retrieve the key of the bucket (in the example this would be the type of asset: table, column, etc).
5. ...to retrieve the number of results that match that bucket key (in the example, how many tables, columns, etc there are in the results).

### Kotlin

```kotlin showLineNumbers title="Build a bucket aggregation"
val request = client.assets.select() // (1)
 .aggregate("type", Asset.TYPE_NAME.bucketBy()) // (2)
 .sort(Asset.CREATE_TIME.order(SortOrder.Desc))
 .toRequest() // (3)
val response = request.search(client) // (4)
```

1. Start building a query from a client, using its 'assets' member's `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will bucket the results based on the distinct types of assets (tables, columns, etc).

3. You can then turn these criteria into a search request using the `toRequest()` helper.
4. And once you have a request, you can then run the search. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```kotlin title="Do something with the results"
val aggregates = response.aggregations // (1)
val result = aggregates["type"] as AggregationBucketResult // (2)
val buckets = result.buckets // (3)
for (detail in buckets)
```

1. From the search response, not only can you retrieve the results (as in previous examples), but when an aggregation is requested you can also retrieve the aggregation result.
2. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name. (You would probably want to type-check this before the explicit cast.)
3. If the result is to a request that produces aggregation buckets, there will be bucket-specific details within it.
4. You can iterate through these details...
5. ...to retrieve the key of the bucket (in the example this would be the type of asset: table, column, etc).
6. ...to retrieve the number of results that match that bucket key (in the example, how many tables, columns, etc there are in the results).

### Raw REST API

```json showLineNumbers title="POST /api/meta/search/indexsearch"
{
 "dsl": {
 "aggregations": { // (1)
 "type": {
 "terms": {
 "field": "__typeName.keyword"
 }
 }
 }
 "query": {
 "bool": {
 "filter": [
 { "term": { "__typeName.keyword": "Table" }}
 ]
 }
 },
 "sort": [
 { "__modificationTimestamp": { "order": "desc" }}
 ]
 }
}
```

1. Add an aggregation to your search. You can add multiple aggregations to a single search, but each must have a unique name (`type` in this example is such a name).

## Metric aggregation

You can also calculate metrics about your search results. With this, you can answer questions like:

> What's the average number of columns I have in tables and views in a particular schema?

For example:

### Java

```java showLineNumbers title="Build a metric aggregation"
IndexSearchRequest request = client.assets.select() // (1)
 .aggregate("avg_columns", Table.COLUMN_COUNT.avg()) // (2)
 .sort(Asset.CREATE_TIME.order(SortOrder.Desc))
 .toRequest(); // (3)
IndexSearchResponse response = request.search(client); // (4)
```

1. Start building a query from a client, using its 'assets' member's `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will calculate an average of numeric values across the results (in this case, column counts on tables).

3. You can then turn these criteria into a search request using the `toRequest()` helper.
4. And once you have a request, you can then run the search. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```java title="Do something with the results"
Map aggregates = response.getAggregations(); // (1)
AggregationMetricResult result = (AggregationMetricResult) aggregates.get("avg_columns"); // (2)
result.getValue(); // (3)
```

1. From the search response, not only can you retrieve the results (as in previous examples), but when an aggregation is requested you can also retrieve the aggregation result.
2. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name. (You would probably want to type-check this before the explicit cast.)
3. If the result is to a request that produces an aggregation metric, you can retrieve the value of that calculated metric directly.

### Python

```python showLineNumbers title="Build a metric aggregation"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Asset, Table
from pyatlan.model.fluent_search import FluentSearch

client = AtlanClient()
request = (
 FluentSearch
 .select() # (1)
 .aggregate("avg_columns", Table.COLUMN_COUNT.avg()) # (2)
 .sort(Asset.CREATE_TIME.order())
).to_request() # (3)
results = client.asset.search(criteria=request)
```

1. Start building a query from the FluentSearch, using its `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will calculate an average of numeric values across the results (in this case, column counts on tables).
3. You can then turn these criteria into a search request using the `to_request()` helper.

```python title="Do something with the results"
result = results.aggregations['avg_columns'] # (1)
result.value # (2)
```

1. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name.
2. If the result is to a request that produces an aggregation metric, you can retrieve the value of that calculated metric directly.

### Kotlin

```kotlin showLineNumbers title="Build a metric aggregation"
val request = client.assets.select() // (1)
 .aggregate("avg_columns", Table.COLUMN_COUNT.avg()) // (2)
 .sort(Asset.CREATE_TIME.order(SortOrder.Desc))
 .toRequest() // (3)
val response = request.search(client) // (4)
```

1. Start building a query from a client, using its 'assets' member's `select()` method.
2. Add an aggregation by chaining one or more `aggregate()` methods, and passing:

 - Any arbitrary key you want, which you'll use to look up the results of the aggregation in the response. You can add as many aggregations as you want, but each must have a unique key to look up its unique results.
 - The field you want to aggregate, along with the kind of aggregation you want to do on that field. This example will calculate an average of numeric values across the results (in this case, column counts on tables).

3. You can then turn these criteria into a search request using the `toRequest()` helper.
4. And once you have a request, you can then run the search. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```kotlin title="Do something with the results"
val aggregates = response.aggregations // (1)
val result = aggregates["avg_columns"] as AggregationMetricResult // (2)
result.value // (3)
```

1. From the search response, not only can you retrieve the results (as in previous examples), but when an aggregation is requested you can also retrieve the aggregation result.
2. Since multiple aggregations can be requested, you can retrieve a specific aggregation result by name. (You would probably want to type-check this before the explicit cast.)
3. If the result is to a request that produces an aggregation metric, you can retrieve the value of that calculated metric directly.

### Raw REST API

```json showLineNumbers title="POST /api/meta/search/indexsearch"
{
 "dsl": {
 "aggregations": { // (1)
 "avg_columns": {
 "avg": {
 "field": "columnCount"
 }
 }
 }
 "query": {
 "bool": {
 "filter": [
 { "term": { "__typeName.keyword": "Table" }}
 ]
 }
 },
 "sort": [
 { "__modificationTimestamp": { "order": "desc" }}
 ]
 }
}
```

1. Add an aggregation to your search. You can add multiple aggregations to a single search, but each must have a unique name (`avg_columns` in this example is such a name).

[^1]: This page is a summary of the details in the Elasticsearch Guide's [aggregation guide](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html)

---
