# Autocomplete

Suggest searches, categories and products while the shopper types, from one request after each pause in typing.

A shopper who has typed "ecks" wants to see where it leads before typing on. A request on the `autocomplete` surface completes it with the catalog's own words:

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?on=autocomplete&query=ecks' | jq -c '.suggestions[]'
```

```json title="Answer: 200 · 10 ms · release b25a6aaa"
{"text":"Ecksofa"}
{"text":"Ecksofa","scope":{"category":"wohnen/sofas","title":"Sofas"}}
```

"Ecksofa" is a corner sofa. The first suggestion searches the word, the second searches it among the sofas.

## Text and scoped suggestions

The trace's `suggest` step says where each suggestion comes from:

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?on=autocomplete&query=ecks&debug=1' \
  | jq -r '.trace.steps[] | select(.step == "suggest") | .suggestions[].source'
```

```text title="Answer: 200 · 13 ms · release b25a6aaa"
the word "Ecksofa" of 1 product titles, a compound of the head "sofa"
the word "Ecksofa" of 1 product titles, a compound of the head "sofa", in the category wohnen/sofas, where 1 of them sit
```

- **Text suggestions** complete what was typed with category titles, brand names, the labels of the values a query is read for, and the words of product titles. Up to five come first, the one most products hold first.
- **Scoped suggestions** search the first text suggestion that sits in categories within up to two of them, the category most of its products sit in first.

A scoped suggestion searches its text with its category chosen:

```json title="POST /v1/search"
{ "query": "Ecksofa", "filters": { "category": { "within": "wohnen/sofas" } } }
```

**Reference:** [`suggestions`](/docs/reference/query-api#search.answer.suggestions) · [`suggest`](/docs/reference/trace#suggest)

## Products and categories at once

The answer carries a search's sections too, so one request fills a search box with suggestions, products, categories and guides:

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?on=autocomplete&query=sof&per_page=3' \
  | jq -c '.suggestions, (.sections[] | select(.total > 0) | {type, hits: [.hits[].entity]})'
```

```json title="Answer: 200 · 15 ms · release b25a6aaa"
[{"text":"Sofas"}]
{"type":"category","hits":["category:wohnen/sofas"]}
{"type":"product","hits":["product:SOFA-ASKA-2","product:SOFA-LUND-3","product:SOFA-NIKO-SCHLAF"]}
{"type":"content","hits":["content:guide-sofa-finden","content:guide-teppichgroesse"]}
```

`per_page` sets how many products come along. The sections beside them show up to 6 hits each, in the order a search shows them.

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

## No redirect while typing

A search for "sofa" lands on the sofa page. The same text in the search box stays where it is:

```sh title="Terminal"
curl -s 'http://127.0.0.1:7800/v1/search?query=sofa' | jq -c '.redirect'
curl -s 'http://127.0.0.1:7800/v1/search?on=autocomplete&query=sofa' | jq -c '.redirect'
```

```json title="Answer: 200 · 4.5 ms · release b25a6aaa"
{"url":"/wohnen/sofas?from=sofa","entity":"category:wohnen/sofas"}
null
```

A shopper reaches the page a query names when they submit it, never while typing. A rule's redirect follows the rule's own `when`: one without `on` answers in the search box too, so a rule meant for submitted searches names `"on": "search"`.

**Reference:** [`redirect`](/docs/reference/query-api#search.answer.redirect) · [`when.on`](/docs/reference/release-files/rules#when.on)

## In a storefront

The client's `suggestions` store asks after a 100 ms pause in typing and drops an answer for text the shopper has already typed past:

```ts twoslash title="app/search-box.ts"
import { createSearch } from "@orbsearch/client";

const search = createSearch({ endpoint: "http://127.0.0.1:7800" });
const box = search.suggestions({ locale: "de", per_page: 3 });

box.subscribe(() => {
    const { response } = box.current();
    for (const { text, scope } of response?.suggestions ?? []) {
        console.log(scope ? `${text} in ${scope.title}` : text);
    }
});
box.type("ecks");
```

`current()` and `subscribe()` have the shape React's `useSyncExternalStore` reads. `SearchBox` from the [React components](/docs/react) runs on this store and lists the suggestions, products and links in one list a keyboard moves through.

A search box has a tighter budget than a search: 15 ms at p95, measured at the server, against a search's 30 ms.

<Callout type="info">
    A suggestion ranks by the products that hold it in every channel, so a channel may be offered a brand it does not
    sell.
</Callout>

**Reference:** [`Timings`](/docs/reference/trace#Timings)

## Next

- [Search and browse](/docs/search): what the search a suggestion leads to answers.
- [React components](/docs/react): the search box ready to use.
- [How search understands a query](/docs/query-understanding): what a submitted query names.
