OriginChainDB docs
examples · fts · 8 / 9

8. Autocomplete and did-you-mean

← FTS examples

what 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.

POST /v1/tenants/:t/fts/:schema/_suggest
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
  }'

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" completes cance, not the whole string. The analyzed prefix is echoed back so you can see what was actually used.
  • Defaults. size 5, max_edits 2, mode missing, min_word_length 4; phrase adds candidates_per_term 4 and confidence 1.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. source says whether frequencies came from the segmented dictionary or a legacy presence set; frequency_ranked says whether the list is ranked at all; df_exact is true only when no segment holds deletions; candidates_truncated warns that a fuzzy expansion was cut before the right answer could be considered.
  • Caps refuse rather than clamp. A size above MAX_SUGGEST_SIZE (100) or a max_scan above MAX_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 mode is missing: a term already in the dictionary is not second-guessed, because a suggester that always has something to say trains people to ignore it. Use popular or always if 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 df as a live count. It counts postings, including postings whose document has since been deleted, so it is an upper bound. df_exact tells 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 score yourself. The list already arrives best-first - nearest edit distance, then frequency. Re-sorting on a blended score usually just reproduces it.