More like this query #
more_like_this finds documents similar to given text or documents.
The engine extracts the input’s terms, keeps the significant ones, and
runs the resulting top terms as a disjunctive query — a “find more of
this” query without any embedding model.
fields is required and limited to text fields.
How it works #
The query is rewritten at the search entry point — it is a client of
the analysis and term pipeline, not an index structure. Per field:
gather the like text (free text is
analyzed with the first field’s analyzer; a referenced document
contributes the text of every query field), tokenize it, drop terms
below min_term_freq, outside the word-length bounds, or listed in
stop_words, and remove every term the unlike inputs produced.
The surviving terms from all fields are pooled, ranked by source term
frequency (ties broken alphabetically for determinism), and the top
max_query_terms become a bool.should of term queries governed
by minimum_should_match. If nothing survives, the query matches
nothing. When include is false (the default) and the like input
named documents, their ids are added as a must_not — the seed
documents trivially contain every selected term.
Input: like and unlike
#
like accepts free text strings and document references, mixed in one
array; unlike takes the same forms and removes the terms it shares
with the selection:
POST /my-collection/_search
{
"query": {
"more_like_this": {
"fields": ["title", "body"],
"like": [
"lightweight running shoes",
{ "_id": "42" }
],
"unlike": "clearance sale",
"min_term_freq": 1,
"minimum_should_match": "30%"
}
}
}
- Free text is analyzed with the first field’s analyzer.
- A document reference is
{ "_id": "<id>" }("_key"is accepted as an alias). The referenced document’s text is read for term extraction; the document itself is excluded from the results unlessincludeistrue.
Parameters #
| Parameter | Default | Description |
|---|---|---|
fields | — (required) | Text fields to extract terms from and match against. |
like | — (required) | Free text and/or {_id} document references. |
unlike | — | Input whose shared terms are dropped from the selection. |
min_term_freq | 2 | Ignore terms appearing fewer than this many times in the input. |
max_query_terms | 25 | Cap on the number of selected terms. |
min_doc_freq | 5 | Parsed but not applied (see deviations below). |
max_doc_freq | 2147483647 | Parsed but not applied (see deviations below). |
min_word_length | 0 | Ignore shorter terms. |
max_word_length | 0 (unlimited) | Ignore longer terms. |
stop_words | — | Terms ignored during extraction. |
analyzer | field’s analyzer | Analyzer for free-text input. |
minimum_should_match | "30%" | How many selected terms a document must match; percentages truncate (30% of 5 terms → 1). |
boost_terms | 1.0 (off) | Boost repeated input terms; activates above 1 (see below). |
include | false | Whether the liked documents themselves can match. |
boost | 1.0 | Query-level boost. |
Terms are selected without document frequencies #
The engine expands the query at the search layer, where per-term
document frequencies are not yet available, so term selection ranks by
input term frequency alone (ties broken alphabetically) and keeps the
top max_query_terms.
What this means in practice:
min_doc_freq/max_doc_freqare accepted and validated but do not filter. Very common terms (stopword-like tokens that survive the analyzer) can reach the final query — usestop_wordsormax_word_lengthto control them instead.- Ranking quality relies on the term-frequency cap and
minimum_should_match; specificity comes from the analyzer (e.g. a stemming or stop-token analyzer) rather than df filtering.
boost_terms approximation
#
The engine multiplies the term’s boost by
boost_terms when the value is above one and the term occurred
more than once in the input — frequent input terms
matter more.
Use cases #
- “More like this article.” The playground’s free-text form — feed a title or abstract, get the neighbours ranked by shared terms, no embedding model or vector index required.
- Similar-by-example over stored docs.
like: [{ "_id": "A42" }]builds the query from the stored document itself — related-items panels, “variants of this record”, duplicate detection — with the seed document excluded from its own results by default. - Steering the neighborhood away.
unlike: "clearance sale"drops the boilerplate terms before selection, so similarity is measured on what makes the documents distinct, not their shared template text. - Zero-infrastructure similarity. Because selection is pure term statistics, it works on any indexed text field at query time — nothing to train, backfill or keep in sync.
Related #
match— what MLT compiles down to per term; thebool.shouldit expands into is ordinary boolean retrieval.bool— the shape of the expansion, including theminimum_should_matchknob the selection is governed by.significant_terms— the aggregational cousin: which terms distinguish a foreground set, rather than which documents share a seed’s terms.