# Facets and filters

Read the facets a page answers with, send the shopper's choices as filters, and count a facet when the shopper opens it.

A [facet](/docs/reference/glossary#facet) is a filter shoppers choose from, with the count each value would find. Every answer that lists products brings its page's facets. Here is the color facet of the sofa page `wohnen/sofas` (wohnen: living):

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?on=category_page&category=wohnen/sofas' \
  | jq -c '.sections[] | select(.type == "product") | .facets[] | select(.field == "color")
      | {field, kind, label}, (.values[] | {value, label, count})'
```

```json title="Answer: 200 · 9.2 ms · release b25a6aaa"
{"field":"color","kind":"swatch","label":"Farbe"}
{"value":"anthracite","label":"Anthrazit","count":2}
{"value":"beige","label":"Beige","count":2}
{"value":"grey","label":"Grau","count":2}
{"value":"green","label":"Grün","count":2}
```

Labels come in the request's [locale](/docs/reference/glossary#locale): Farbe is color, Grau grey. Each value carries the code that chooses it and how many products it would find. A `range`, such as the width, brings `min` and `max` instead of values.

**Reference:** [`facets`](/docs/reference/query-api#search.answer.sections.facets) · [`kind`](/docs/reference/query-api#search.answer.sections.facets.kind)

## Choose values

Send the shopper's choices as `filters` in a `POST`, one key per field. Values of one field are alternatives, and fields narrow each other:

```sh title="Terminal"
curl -s http://127.0.0.1:7800/v1/search -H 'Content-Type: application/json' \
  -d '{"on": "category_page", "category": "wohnen/sofas",
       "filters": {"color": ["grey", "green"], "width": {"gte": 160, "lte": 220}, "on_sale": true}}' \
  | jq -c '.sections[] | select(.type == "product") | .hits[] | {entity, variant}'
```

```json title="Answer: 200 · 12 ms · release b25a6aaa"
{"entity":"product:SOFA-ASKA-2","variant":"SOFA-ASKA-2-SALBEI"}
```

One sofa is grey or green, 160 to 220 cm wide and on sale. Its tile shows the [variant](/docs/reference/glossary#variant) that meets all three: the Aska in sage (Salbei), which counts as green and is on sale.

Each kind of facet is chosen its own way:

| Facet kind       | Chosen as                              | Example                                    |
| ---------------- | -------------------------------------- | ------------------------------------------ |
| `list`, `swatch` | Value or group codes                   | `"color": ["grey", "green"]`               |
| `buckets`        | A bucket's code, written as its bounds | `"delivery_days": ["-15"]`                 |
| `range`          | `gte`, `lte` or both                   | `"width": { "gte": 160, "lte": 220 }`      |
| `toggle`         | `true`                                 | `"on_sale": true`                          |
| `hierarchical`   | A category with everything below it    | `"category": { "within": "wohnen/sofas" }` |

`filters` is a [selector](/docs/reference/query-api#Selector) in these forms only. It takes any field some page of the [release](/docs/reference/glossary#release) counts, so `on_sale` works on the sofa page too; any other field is a `400`. The answer's `filters` labels each filter for its chip.

**Reference:** [`filters`](/docs/reference/query-api#search.filters) · [`Selector`](/docs/reference/query-api#Selector) · [`filters` in the answer](/docs/reference/query-api#search.answer.filters)

## A chosen value stays

Each facet is counted under every filter but its own, so choosing a value never makes its siblings vanish:

```sh title="Terminal"
curl -s http://127.0.0.1:7800/v1/search -H 'Content-Type: application/json' \
  -d '{"on": "category_page", "category": "wohnen/sofas", "filters": {"color": "grey", "on_sale": true}}' \
  | jq -c '.sections[] | select(.type == "product")
      | .total, (.facets[] | select(.field == "color") | .values[] | del(.label, .swatch))'
```

```json title="Answer: 200 · 16 ms · release b25a6aaa"
0
{"value":"green","count":1}
{"value":"grey","count":0,"selected":true}
```

No grey sofa is on sale, so the page lists nothing. Green still counts one sofa, since the color facet ignores its own choice. Grey stays at 0, `selected`, so the shopper can take it back. Other empty values are left out, and so is an empty facet nobody chose.

## Upper bounds

Where a product's variants differ, some counts can only be [upper bounds](/docs/reference/glossary#upper-bound). Choose two sizes and two colors of bed linen, and one variant may meet the size while another meets the color. Such a facet carries `upper_bound: true`, as does a [section's](/docs/reference/glossary#section) total where a free range over such a field meets another choice, and the [trace](/docs/reference/glossary#trace) says why.

**Reference:** [`upper_bound`](/docs/reference/query-api#search.answer.sections.facets.upper_bound)

## The price histogram

A price shown as a histogram brings the bars behind its slider, as `histogram` bins:

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?on=category_page&category=wohnen/sofas' \
  | jq -c '.sections[] | select(.type == "product") | .facets[] | select(.field == "price")
      | {min, max}, (.histogram[] | select(.count > 0))'
```

```json title="Answer: 200 · 9.5 ms · release b25a6aaa"
{"min":649.0,"max":1899.0}
{"gte":620,"lt":680,"count":1}
{"gte":680,"lt":750,"count":1}
{"gte":750,"lt":820,"count":1}
{"gte":1200,"lt":1300,"count":1}
{"gte":1800,"lt":2000,"count":1}
```

The bins are a fixed ladder of 24 steps to the decade, the same under every filter, and empty ones come too, so the bars keep their places. Four sofas fill five bins: a tile counts in every bin one of its prices falls in, and the Aska costs 649 € in sage, on sale, and 749 € in grey.

**Reference:** [`histogram`](/docs/reference/query-api#search.answer.sections.facets.histogram)

## Facets counted later

A page counts its first 30 facets and those a filter chooses; the others come `deferred`, without values. A list or swatch carries 50 values, the chosen and the most frequent, and `more` says how many others it has.

To fill a facet when the shopper opens it, name it in `facets`. With `count_only`, the answer leaves out the hits and the other facets:

```sh title="Terminal"
curl -s http://127.0.0.1:7800/v1/search -H 'Content-Type: application/json' \
  -d '{"on": "category_page", "category": "wohnen/sofas", "filters": {"color": "grey"},
       "facets": ["brand"], "count_only": true}' \
  | jq -c '.sections[] | {total, hits, facets}'
```

```json title="Answer: 200 · 7.7 ms · release b25a6aaa"
{"total":2,"hits":[],"facets":[{"field":"brand","kind":"list","label":"Marke","values":[{"value":"Halvard","label":"Halvard","count":2}],"search":true}]}
```

Both grey sofas are Halvard's. The home store's pages show ten facets at most, so none waits; the [React components](/docs/react) ask this way for every facet a shopper opens.

**Reference:** [`facets`](/docs/reference/query-api#search.facets) · [`count_only`](/docs/reference/query-api#search.count_only) · [`deferred`](/docs/reference/query-api#search.answer.sections.facets.deferred) · [`counted_facets`](/docs/reference/limits#counted_facets)

## Which facets a page shows

The merchant lays out each page's facets in `categories.json`: `default` for every page, `nodes` for the categories that differ.

```json title="tests/fixtures/home/categories.json (trimmed)"
{
    "default": {
        "facets": ["category", { "field": "price", "display": "histogram" }, "style", "on_sale", "availability"]
    },
    "nodes": [
        { "category": "wohnen/sofas", "facets": ["width", "color", "upholstery", "seat_height", "price"] },
        { "category": "leuchten", "facets": ["room", "light_source", "material", "color", "price"] }
    ]
}
```

A category page shows the facets of the nearest [node](/docs/reference/glossary#node) up the tree that lists its own, else `default`: the floor lamps (`leuchten/stehleuchten`) show those of `leuchten` (lamps). A search page shows `default`, or the facets of the category its query names, so "sofa" shows the sofa page's. [Pages and their filters](/docs/pages) shows how a merchant lays them out.

**Reference:** [`categories.json` nodes](/docs/reference/release-files/categories#nodes.facets) · [`default`](/docs/reference/release-files/categories#default.facets)

## Next

- [Category pages](/docs/category-pages): browse a category with its facets and banners.
- [Sorting](/docs/sorting): the orders a page offers and the one it starts in.
- [URLs and redirects](/docs/urls): keep the shopper's choices in the page's URL.
