> For the complete documentation index, see [llms.txt](https://docs.groundcover.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.groundcover.com/investigate/querying-your-groundcover-data/search-performance.md).

# Search Performance

How groundcover speeds up log and trace searches, and what you can do to make a slow query fast.

## Overview

Every search has to scan some amount of data. The larger the time range and the broader the filters, the more data a query touches — and the longer it takes to return.

groundcover uses several mechanisms to shrink how much data a search scans. They are **not alternatives to each other** — they stack, and each one fits a different situation. Some are always on, some turn on automatically based on how you query, and some you configure yourself.

## Standardized attributes

Some fields are optimized out of the box, with no setup. The only requirement is that your queries actually use them.

### Scope keys

`env`, `cluster`, `namespace`, and `workload` are first-class indexed fields. Including them in a query dramatically narrows what the search scans, and it's the easiest speedup available.

```
cluster:prod-us namespace:checkout level:error
```

{% hint style="success" %}
**Rule of thumb:** start every query with the scope keys you can (`env`, `cluster`, `namespace`, `workload`), then add more specific filters.
{% endhint %}

### Good fields to filter by

Beyond the scope keys, each data type has its own first-class fields. Reaching for these rather than a custom attribute or free text gives the search something selective to narrow on straight away.

| Data type         | Useful fields                                                                                                                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Logs              | `level` for severity — `level:error`, or `level:in(error, fatal)` for several at once. Also `pod_name` and `trace_id`.                                                                                       |
| Traces            | `status_code` for HTTP errors — `status_code>=500`, or `status_code>=400` for 4xx and 5xx together. Also `status:error` for span-level errors, plus `span_type`, `resource`, `duration_seconds`, `trace_id`. |
| Kubernetes events | `type` for severity — `type:Warning`. Also `reason`, such as `reason:OOMKilling`, and `entity_workload`.                                                                                                     |

{% hint style="info" %}
These error fields are not interchangeable. Use `level` for log severity, `status_code` for HTTP errors on traces, and `status:error` for span status — each applies only to its own data type.
{% endhint %}

### Trace IDs

Trace ID lookups are optimized by default, in both logs and traces, so pivoting from a log line to its trace is fast with no configuration.

groundcover also recognizes the common spellings of trace ID fields at ingestion, so the optimization works across the usual variants. See [Log and Trace Correlation](/investigate/log-and-trace-correlation.md#valid-trace_id-fields-in-logs) for the full list of recognized field names.

If your services emit trace IDs under a custom field name that isn't recognized, the best fix is to map it onto a standard name using a [log pipeline](/collect-data/data-pipelines/log-pipelines.md) rather than querying the custom field directly. That way your trace ID lookups ride the default optimization.

## Favorite attributes

Out of all the string attributes you send, only a handful tend to be searched regularly. groundcover automatically identifies that frequently-used subset and keeps it in a dedicated, smaller place. Searches on those keys then run against a much smaller set of attributes instead of every attribute in your data, which makes them noticeably faster.

**How it turns on.** Automatically, based on usage. When the same attribute shows up in your queries repeatedly over a recent window of time, groundcover promotes it on its own. There is nothing to configure and no request to make.

{% hint style="warning" %}
**The speedup applies going forward.** Promotion accelerates searches over data ingested *after* an attribute becomes a favorite. Historical data is not re-indexed, so the benefit builds up over time rather than appearing at once.
{% endhint %}

## Inverted index

An inverted index is a targeted index on one specific field that makes **needle-in-a-haystack** searches fast — finding the small number of records that carry one particular value.

**How it works.** Many high-cardinality identifiers only appear within short windows of time. An inverted index maps values of a field to the time windows they appear in. Looking a value up means first consulting the index to find where it occurs, then scanning only those windows instead of the whole time range.

Searching for a correlation ID over the last 7 days, where the ID only appears on two of those days:

```
Day    1     2     3     4     5     6     7
             X                 X

Without an inverted index:  scan all 7 days
With an inverted index:     look up the ID, then scan only days 2 and 5
```

**When it's a good fit.** The value you search for is rare across the dataset and concentrated in time rather than scattered everywhere — an order ID, a request or correlation ID, a job or upload ID, a user ID. These are the fields you reach for when stitching together the story of a single request across services.

**When it's the wrong tool.** Values that recur constantly. If a value matches a large or steady share of your records over time, the index can't narrow anything down and there's little to gain. Indexing something like `level` would not help, because every record has one.

**How to enable it.** Inverted indexes are configured by groundcover rather than self-served. If you have a field that fits the pattern above, [contact groundcover support](https://www.groundcover.com/contact-us) with the field name and the data type it applies to. Trace IDs already benefit from this by default.

{% hint style="info" %}
A field that's worth an inverted index is usually worth having as a favorite attribute too. Since favorite attributes are promoted automatically from real query usage, this generally takes care of itself once you start searching the field regularly.
{% endhint %}

## Index tiers

Index tiers let you carve out a defined slice of your data — a critical cluster, production errors, a key tenant — so that searches within that slice are consistently fast. You define the slice with a gcQL filter, and matching records are grouped together as they arrive. Querying a tier by name then touches only that group rather than the full dataset.

Reach for a tier when you repeatedly search the same well-defined subset of your data. Keep the number of tiers small: a handful of genuinely high-value slices stays easy to reason about, while carving up everything dilutes the benefit.

A record belongs to exactly one tier, so a tier holds only what tiers defined before it didn't already claim. If you expect a tier to contain every record matching its filter, check that no broader tier sits above it — see [How overlap is resolved](/operate/storage-management.md#how-overlap-is-resolved).

See [Storage Management](/operate/storage-management.md) for access requirements, how to define a tier, and how to filter on one with `index_tier`.

## Making a slow query faster

When a search is slower than you'd like, work through these in order:

1. **Add scope keys.** Can you add `env`, `cluster`, `namespace`, or `workload`? This is the most common and most effective fix.
2. **Narrow the time range.** Scanning 14 days costs roughly fourteen times what scanning one day costs. Start narrow and widen only if you need to.
3. **Prefer `key:value` over searching everything.** A filter on a specific attribute is far cheaper than `*:value`, which has to look at every attribute. On logs, the same applies to bare free-text terms. Bare free text isn't supported on traces — reach for `*:value` or a field-qualified filter there instead. See [Search & Filter](/investigate/search-and-filter.md) for which syntax each section supports.
4. **Go easy on wildcards.** `*` at the start or end of a term prevents an exact-match lookup and forces a broader scan. An exact term is significantly faster than the same term with a wildcard attached.
5. **Filter, don't exclude.** Where you can, say what you want rather than what you don't. `cluster:prod-us` narrows the search to one cluster; `-cluster:staging` still has to consider everything else.
6. **Ask about an inverted index.** If you're routinely looking up rare identifiers, this is the mechanism built for it.
7. **Define an index tier.** If you keep coming back to the same well-defined subset of your data, make it a tier and query it with `index_tier`.

### Example

A phrase searched across two weeks of data, scoped only by excluding one cluster:

```
-cluster:staging "order-processing-failed"
```

The exclusion still leaves every other cluster in scope, and nothing narrows the search before the phrase is matched. Naming the cluster and namespace instead:

```
cluster:prod-us namespace:orders "order-processing-failed"
```

The phrase is unchanged — what changed is how much data it runs against. This is deliberately a narrower search: it asks whether this happened in `prod-us` and the `orders` namespace, rather than anywhere outside staging. That narrowing is the point. If you genuinely need the broader scope, keep the exclusion and shorten the time range instead.

## Related

* [Filters](/investigate/querying-your-groundcover-data/groundcover-query-language/filters.md) — full filtering syntax
* [Search & Filter](/investigate/search-and-filter.md) — search syntax across the native pages
* [Storage Management](/operate/storage-management.md) — retention, cold storage, and index tiers
* [Log Pipelines](/collect-data/data-pipelines/log-pipelines.md) — reshape attributes at ingestion


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.groundcover.com/investigate/querying-your-groundcover-data/search-performance.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
