# Aggregations

### Overview

Aggregations compute metrics from your filtered data. Use them to answer questions like "How many errors per service?".

### Syntax

Use the pipe operator `|` to chain a `stats` command after your filters:

```
<filters> | stats <function>(<field>) as <alias>
```

**Single aggregation:**

```
level:error | stats count()
```

**Multiple aggregations:**

```
level:error | stats count() total, count_uniq(workload) services
```

**With grouping:**

```
level:error | stats by (workload) count() errors
```

### Counting Functions

#### count()

Count the number of records.

```
level:error | stats count()
```

#### count\_empty(field)

Count records where a field is empty.

```
* | stats count_empty(error)
```

*Logs with no error field*

#### count\_uniq(field)

Count unique values in a field.

```
* | stats count_uniq(user_id)
```

*Number of unique users*

### Numeric Aggregations

Numeric functions attempt to convert field values to numbers. Non-numeric values are treated as NULL and ignored.

#### avg(field)

Calculate the average (mean) value.

```
* | stats avg(duration_seconds)
```

#### sum(field)

Calculate the total sum.

```
* | stats sum(bytes_sent)
```

#### min(field) and max(field)

Find minimum and maximum values.

```
* | stats min(duration_seconds), max(duration_seconds)
```

#### median(field)

Calculate the median value (50th percentile).

```
* | stats median(bytes)
```

#### quantile(p, field)

Calculate percentiles. Use values between 0 and 1 (0.5 = 50th percentile, 0.95 = 95th percentile).

```
* | stats quantile(0.95, bytes_sent) p95
```

```
* | stats quantile(0.5, bytes_sent) p50, quantile(0.95, bytes_sent) p95, quantile(0.99, bytes_sent) p99
```

#### sum\_len(field)

Sum the length of string values.

```
* | stats sum_len(message)
```

*Total characters in all messages*

### Value Aggregations

#### values(field)

Get all values (with duplicates).

```
level:error | stats values(error_message)
```

*All error messages*

#### uniq\_values(field)

Get unique values (no duplicates).

```
* | stats uniq_values(status_code)
```

*List of all status codes seen*

### Grouping with 'by'

Group results by one or more fields.

#### Single Field Grouping

```
* | stats by (workload) count()
```

```
* | stats by (level) count() as log_count
```

#### Multiple Field Grouping

```
* | stats by (workload, level) count()
```

#### Multiple Functions

```
* | stats by (resource) count(), min(duration_seconds), max(duration_seconds), median(latency)
```

### Post-Aggregation Filtering

Filter results after aggregation.

#### Using filter pipe

```
* | stats by (workload) count() as request_count | filter request_count:>1000
```

*Workloads with more than 1000 requests*

#### Implicit Filtering (Without filter keyword)

```
* | stats by (workload) count() as request_count | request_count:>1000
```

*Same as above, shorter syntax*

### Time-Series Aggregations

**Note:** In groundcover, time bucketing is configured externally through the UI time range selector, not in the query itself.

```
* | stats by (workload) count() as logs_per_minute
```

*Count logs per workload (time interval set in UI)*

```
* | stats by (status_code) count()
```

*Requests per status code over time*
