8. Autocomplete and did-you-mean
← FTS exampleswhat this does
One endpoint carries three suggesters, chosen by the kind field. completion finishes the word a user is still typing, term offers a correction for a single misspelled word, and phrase rewrites a whole query using the corrections that actually occur together in your corpus.
when to use it
completion- the dropdown under a search box, fired on each keystroke. It reads the term dictionary, never a posting block.term- the "did you mean" line above a thin result set, when one word is clearly wrong.phrase- a multi-word query where every word is plausible on its own but the combination is not.
the request
Autocomplete against an indexed field. Like _aggs and _search, _suggest hangs off the schema and takes the field in the body.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/fts/shop.products/_suggest" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "completion",
"field": "description",
"prefix": "head",
"size": 5
}'import requests
# One endpoint, three suggesters. "kind" selects which.
resp = requests.post(
f"https://{OC_HOST}/v1/tenants/{OC_TENANT}/fts/shop.products/_suggest",
headers={"Authorization": f"Bearer {OC_TOKEN}"},
json={
"kind": "completion",
"field": "description",
"prefix": "head",
"size": 5,
},
)
suggest = resp.json()
for c in suggest["completions"]:
print(c["text"], c["df"])// One endpoint, three suggesters. "kind" selects which.
const res = await fetch(
`https://${OC_HOST}/v1/tenants/${OC_TENANT}/fts/shop.products/_suggest`,
{
method: "POST",
headers: {
Authorization: `Bearer ${OC_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
kind: "completion",
field: "description",
prefix: "head",
size: 5,
}),
},
);
const suggest = await res.json();
for (const c of suggest.completions) {
console.log(c.text, c.df);
}// One endpoint, three suggesters. "kind" selects which.
body, _ := json.Marshal(map[string]any{
"kind": "completion",
"field": "description",
"prefix": "head",
"size": 5,
})
req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
"https://"+ocHost+"/v1/tenants/"+ocTenant+"/fts/shop.products/_suggest",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+ocToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)what you get back
{
"kind": "completion",
"prefix": "head",
"completions": [
{ "text": "headphones", "df": 3, "total_tf": 4, "score": 3.0 },
{ "text": "headset", "df": 1, "total_tf": 1, "score": 1.0 }
],
"source": "segmented",
"frequency_ranked": true,
"df_exact": true,
"scanned": 2
}
The response echoes the kind it was asked for, so all three fit one client type. df is the number of documents containing the term and score is that same count as a float - the library scores a completion by its document frequency rather than inventing a formula.
the other two suggesters
Same URL, same auth, different kind. A term suggestion reports the typed word, its own document frequency, and the candidates within the edit budget:
{ "kind": "term", "field": "description", "text": "headphnes", "max_edits": 1 } {
"kind": "term",
"entries": [
{
"text": "headphnes",
"df": 0,
"options": [
{ "text": "headphones", "distance": 1, "df": 3, "score": 0.82 }
],
"candidates_truncated": false
}
],
"source": "segmented",
"frequency_ranked": true,
"df_exact": true
} A phrase suggestion corrects every term at once and ranks the whole rewrite, so it can prefer a candidate that is a worse individual match but a far better neighbour:
{ "kind": "phrase", "field": "description", "text": "wireles headphnes", "max_edits": 1 } {
"kind": "phrase",
"input": ["wireles", "headphnes"],
"input_score": 0.0,
"options": [
{
"text": "wireless headphones",
"terms": ["wireless", "headphones"],
"score": 0.61,
"term_score": 0.74,
"cooccurrence_score": 0.48,
"corrected_terms": 2
}
],
"source": "segmented",
"cooccurrence_available": true,
"df_exact": true,
"cooccurrence_probes": 4
} how it works
- The prefix is analyzed, and the last token wins.
"noise cance"completescance, not the whole string. The analyzed prefix is echoed back so you can see what was actually used. - Defaults.
size5,max_edits2,modemissing,min_word_length4; phrase addscandidates_per_term4 andconfidence1.0. - Ordering is stable. Completions sort by document frequency, then total term frequency, then the term itself - the trailing lexicographic key means equal-frequency completions come back in the same sequence every run.
"order": "lexicographic"turns ranking off when you do your own. - Every response carries honesty flags.
sourcesays whether frequencies came from the segmented dictionary or a legacy presence set;frequency_rankedsays whether the list is ranked at all;df_exactis true only when no segment holds deletions;candidates_truncatedwarns that a fuzzy expansion was cut before the right answer could be considered. - Caps refuse rather than clamp. A
sizeaboveMAX_SUGGEST_SIZE(100) or amax_scanaboveMAX_PREFIX_SCAN(100,000) is a 400 - a clamped scan would return a top-k computed over an arbitrary slice of the dictionary.
common mistakes
- Expecting a correction for a correctly-spelled word. The default
modeismissing: a term already in the dictionary is not second-guessed, because a suggester that always has something to say trains people to ignore it. Usepopularoralwaysif you want otherwise. - Treating an empty list as an error. A prefix nothing starts with is 200 with
"completions": []. The field is indexed, so "no completions" is a true answer - only an unindexed field is a 404. - Trusting
dfas a live count. It counts postings, including postings whose document has since been deleted, so it is an upper bound.df_exacttells you when that gap is closed; re-check with a real query if it matters. - Ignoring
frequency_ranked. When it is false the list is alphabetical rather than best-first, and showing the first entry as "did you mean" is then arbitrary. - Sorting term options by
scoreyourself. The list already arrives best-first - nearest edit distance, then frequency. Re-sorting on a blended score usually just reproduces it.