Chapter 45 · Amazon Opensearch Service
Subchapter 45.10
references/assessment-shape-translation.mdMarkdown12 KBView on GitHub
The user has a working query, request body, or DSL fragment in another search engine (Solr, Elasticsearch ≥ 7.11 syntax that needs OS-side adjustments, raw Lucene syntax, or vendor-specific dialect) and wants the OpenSearch equivalent. The deliverable is drop-in JSON or code that the user can paste into _search, _msearch, a client SDK call, or an OpenSearch Dashboards Dev Tools tab.
This shape is purely a syntactic + semantic mapping exercise. It is NOT a migration assessment, NOT a sizing exercise, and NOT a relevance-tuning engagement.
Detect TRANSLATION_TASK when the user prompt contains any of:
q=…&fq=…&qf=…, give me OpenSearch JSON”/select?q=…&qf=…&pf=…&mm=…&tie=…)runtime_mappings in 7.12+, ES 8.x knn top-level field) and the user is targeting AOS managedq= string with field-prefix syntax (title:headphones AND brand:sony)Do NOT dispatch here when:
schema.xml or an ES mapping → that’s SCHEMA_CONVERSION.references/search-recipes.md directly.Produce these sections in order, nothing more:
“Translating Solr 8.11 eDisMax
q=wireless headphones&qf=title^3 description&pf=title^5&mm=2<-25%&q.op=AND&tie=0.3to OpenSearch 2.x_search.”
State source engine + version (if known) + the specific query type (eDisMax, dismax, standard, function query, JSON Facet API, …) + target endpoint.
A single fenced code block, valid JSON, ready to paste into Dev Tools. Preserve field names exactly. Include query, from/size, sort, aggs, highlight blocks as the source request had them.
For every non-trivial Solr/ES parameter in the source, one row showing source param → OpenSearch param → fidelity (verbatim / mapped / approximation). This is the heart of the shape — every syntactic element must be either preserved or explicitly mapped, with no silent drops.
A short bullet list of behavior drift the user must be aware of. Examples:
pf (phrase boost): modeled as a should clause with multi_match type: phrase. Scoring shape differs — Solr’s pf boosts the whole phrase score additively; OpenSearch’s should adds a separately-scored phrase match. Re-tune boost values against your judgment list.tie_breaker default: OpenSearch multi_match best_fields defaults tie_breaker: 0.0 (winner-takes-all); Solr eDisMax defaults tie=0.0 as well, but if the source omitted tie, set it explicitly to avoid surprises if you later upgrade.q.op=AND: OpenSearch query_string defaults to OR. Set default_operator: AND explicitly or results will diverge.If the translation is non-trivial, give the user a 1-line _validate/query?explain=true or a 2-doc sanity check they can run to confirm the rewrite parses and scores reasonably.
Do NOT produce these sections in TRANSLATION_TASK:
This is the canonical lookup. Use it; do NOT re-derive per request.
| Solr (or ES 7.x dialect) | OpenSearch | Fidelity | Notes |
|---|---|---|---|
q=headphones | {"multi_match": {"query": "headphones", "fields": ["title", "description"]}} | mapped | Solr searches df (default field); OS has no _all — name fields explicitly. |
q=title:headphones | {"match": {"title": "headphones"}} | verbatim | Field-scoped match. |
q.op=AND | "default_operator": "AND" (on query_string) or "operator": "AND" (on match / multi_match) | verbatim | OpenSearch defaults to OR. #1 cause of result divergence. Always set explicitly. |
defType=edismax | multi_match type: best_fields | mapped | Closest semantic equivalent; not byte-identical scoring. |
qf=title^3 description^1 tags^2 | "fields": ["title^3", "description^1", "tags^2"] | verbatim | Boosts pass through unchanged. |
pf=title^5 (phrase boost) | should: [{"multi_match": {"query": "<q>", "type": "phrase", "fields": ["title^5"]}}] | approximation | Scoring shape differs — see caveats. |
pf2=title^3 / pf3=title^2 | Two should clauses with match_phrase and slop adjustment | approximation | Solr’s bigram/trigram phrase boost has no exact OS equivalent. |
tie=0.3 | "tie_breaker": 0.3 (on multi_match best_fields) | verbatim | Same semantics. |
mm=2<-25% | "minimum_should_match": "2<-25%" | verbatim | Syntax passes UNCHANGED — same parser. |
mm=100% | "minimum_should_match": "100%" | verbatim | All clauses must match. |
fq=in_stock:true | bool.filter: [{"term": {"in_stock": true}}] | verbatim | Filter context — no scoring, cacheable. |
bq=category:electronics^2 (boost query) | should: [{"term": {"category": {"value": "electronics", "boost": 2}}}] | verbatim | Additive scoring boost. |
bf=recip(ms(NOW,timestamp),3.16e-11,1,1) (boost function) | function_score with gauss or exp decay on timestamp | mapped | Solr recip is a hyperbolic decay; OS gauss/exp give equivalent shape — re-tune scale. |
sort=score desc, price asc | "sort": [{"_score": "desc"}, {"price": "asc"}] | verbatim | score → _score. |
start=20&rows=20 | "from": 20, "size": 20 | verbatim | |
fl=title,price,_score | "_source": ["title", "price"] (_score always returned) | verbatim | |
hl=true&hl.fl=title | "highlight": {"fields": {"title": {}}} | verbatim | |
facet=true&facet.field=category | "aggs": {"by_category": {"terms": {"field": "category.keyword"}}} | mapped | Use .keyword sub-field for terms agg. |
facet.range=price&facet.range.start=0&facet.range.end=100&facet.range.gap=20 | "aggs": {"price_ranges": {"range": {"field": "price", "ranges": [{"to":20},{"from":20,"to":40},…]}}} | mapped | OS uses explicit range buckets. |
mlt=true | more_like_this query | mapped | Same semantics, different request shape. |
wt=json | (default) Accept: application/json | verbatim | OS speaks JSON natively. |
group=true&group.field=brand | "collapse": {"field": "brand.keyword"} | mapped | OS collapse ≈ Solr field collapsing for top-1 per group. |
cursorMark=* | search_after + tiebreaker sort | mapped | OS deep pagination via PIT + search_after. |
User: Translate this Solr query:
q=wireless headphones&defType=edismax&qf=title^3 description^1 tags^2&pf=title^5&mm=2<-25%&q.op=AND&tie=0.3&fq=in_stock:true&start=0&rows=20&sort=score desc, price asc&hl=true&hl.fl=titleAssistant response (this is the shape):
Translating Solr 8.x eDisMax q=wireless headphones (qf/pf/mm/tie/q.op/fq/sort/highlight) to OpenSearch 2.x _search.
GET products/_search
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "wireless headphones",
"type": "best_fields",
"fields": ["title^3", "description^1", "tags^2"],
"operator": "AND",
"minimum_should_match": "2<-25%",
"tie_breaker": 0.3
}
}
],
"should": [
{
"multi_match": {
"query": "wireless headphones",
"type": "phrase",
"fields": ["title^5"]
}
}
],
"filter": [
{"term": {"in_stock": true}}
]
}
},
"from": 0,
"size": 20,
"sort": [{"_score": "desc"}, {"price": "asc"}],
"highlight": {"fields": {"title": {}}}
}| Solr param | OpenSearch | Fidelity |
|---|---|---|
defType=edismax | multi_match type: best_fields | mapped |
qf=title^3 description^1 tags^2 | fields: ["title^3","description^1","tags^2"] | verbatim |
pf=title^5 | should + multi_match type: phrase | approximation |
mm=2<-25% | minimum_should_match: "2<-25%" | verbatim |
q.op=AND | operator: "AND" | verbatim |
tie=0.3 | tie_breaker: 0.3 | verbatim |
fq=in_stock:true | filter term clause | verbatim |
sort=score desc, price asc | sort: [{"_score":"desc"},{"price":"asc"}] | verbatim |
hl=true&hl.fl=title | highlight.fields.title | verbatim |
Approximation caveats:
pf phrase boost: modeled as a separate should clause; Solr eDisMax adds the pf score to the eDisMax sum, OpenSearch adds the should score to the bool sum. Top-N order should be similar but not identical — re-tune pf boost (using the example value ^5) against your judgment list.q.op=AND: set as operator: "AND" on multi_match (cleaner than query_string for this case). OpenSearch defaults to OR — without this, recall would explode.To sanity-check parsing: POST products/_validate/query?explain=true with the same body — confirms the BoolQuery / DisjunctionMaxQuery structure matches expectation.
Before sending the response, tick every box:
_search (or the right endpoint).q.op / default_operator is handled explicitly: if the source had AND (Solr q.op=AND OR an explicit AND/&& boolean operator inside a Lucene query_string query), it MUST appear in the JSON itself — operator: "AND" on match/multi_match, or default_operator: "AND" on query_string. Discussing it only in prose or in the approximation caveats does NOT satisfy this rule (the customer is told to drop the JSON in directly, so the JSON has to be correct on its own). Apply this to every query in a multi-query translation, not only the first.mm / minimum_should_match preserved verbatim (same parser; do not “simplify” 2<-25%).qf boosts preserved verbatim in multi_match.fields.pf / pf2 / pf3 is in a separate should clause AND flagged in caveats as approximation.tie_breaker is set explicitly when the source had tie (do not rely on defaults)..keyword sub-field for term / terms agg / sort are using it.Timeline & Resourcing section. NO Executive Summary. NO Sizing. NO migration tooling pivot.