Generated banner for Elasticsearch Search Relevance
Generated logo for Elasticsearch Search Relevance

Elasticsearch Search Relevance

ElasticOfficialCreated by elastic

  • Skill
  • Tool integration
  • Software development
  • Apache-2.0
  • No issues found

From the author

Improve Elasticsearch search relevance for content and catalog indices: pin or promote results with query rules (correct rule type, criteria, and rule-query wiring) and tune organic ranking with multi_match, field boosts, and analysis grounded in the index mapping. Use when search results rank poorly, a specific document must appear first for a query, or the user asks to tune full-text matching — not for ES|QL analytics, index ingest, or cluster health.
— the description by elastic

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured process for improving full-text search relevance in Elasticsearch indices. It distinguishes between deterministic merchandising (pinning specific documents) and organic ranking improvements. Users are guided to diagnose index mappings, select the appropriate relevance lever—either query rules for pinning or multi-match queries with field boosts for organic ranking—and verify results through side-by-side testing. The skill emphasizes using the elastic CLI for all operations and strictly avoiding anti-patterns like sorting by price or keyword fields to manipulate search relevance.

What it does

  • Diagnoses search relevance issues by inspecting index mappings and current query structures.
  • Implements deterministic document pinning using Elasticsearch query rules.
  • Tunes organic search ranking via multi-match queries with strategic field boosting.
  • Provides a testing framework to compare baseline and candidate search results.
  • Maps HTTP-shorthand operations to the required elastic CLI commands.
  • Validates rule criteria and search query wiring to ensure relevance levers function as expected.

When to use it

  • When specific documents must appear at the top of search results for a given query.
  • When search results for content or catalog indices are generally weak or irrelevant.
  • When you need to improve recall for multi-word user queries.
  • When you need to transition from fragile sorting hacks to robust relevance tuning.

Advantages

  • Ensures predictable search behavior for merchandising through query rules.
  • Promotes evidence-based tuning by requiring side-by-side top-hit comparisons.
  • Prevents common anti-patterns like using keyword fields for full-text search.
  • Standardizes the use of the elastic CLI for consistent environment interaction.

Usage examples

“I need to make sure the product with ID SKU123 always appears first when users search for 'sale'.”
The agent will create a pinned query rule for the 'sale' query string and provide the search body using the rule query to ensure SKU123 is promoted.
“My search results for 'running shoes' are poor; they only match the description field.”
The agent will analyze the index mapping, propose a multi_match query that boosts the title field, and guide you through testing the new ranking.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/elastic/elasticsearch-search-relevance/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip elasticsearch-search-relevance.zip -d .agents/skills

Documentation by elastic

Elasticsearch Search Relevance

Improve full-text search results on content and catalog indices. Diagnose the mapping and current query, choose the right relevance lever (query rules for deterministic pinning vs multi_match and field boosts for organic ranking), apply the change, and verify top hits before reporting success.

<!-- begin-partial: preamble -->

Environment Configuration

This skill executes Elasticsearch operations through the elastic CLI. If the elastic CLI is not installed, tell the user what it is needed for. Do not guess credentials, call the HTTP API directly, or attempt other workarounds.

This skill references operations in HTTP-shorthand form (e.g., GET /, GET /_cat/indices, GET /{index}/_mapping, GET /{index}/_settings/index.mode, POST /_query). The Operations table at the end of this document maps each shorthand to the equivalent elastic CLI command — always use the CLI rather than calling the HTTP API directly.

<!-- end-partial: preamble -->

Scope

This skill covers Query DSL relevance on indices with text (and optional keyword) fields — product catalogs, documentation, knowledge bases. It uses POST /{index}/_search for evaluation and query-rules APIs for pinned or excluded documents.

Out of scope:

  • ES|QL search (POST /_query) — use the elasticsearch-esql skill.

  • Semantic / vector / hybrid retrieval — different field types and retrievers.

  • Sorting by price, date, or popularity instead of fixing text relevance unless the user explicitly wants non-relevance ordering.

Relevance levers

User intentLeverAPIs
Always show document X first for query QQuery rules — pinned rule + rule query in searchPUT /_query_rules/{ruleset_id}, POST /{index}/_search
Hide specific documents for query QQuery rules — exclude rule + rule querySame
Better ranking for open-ended text queriesmulti_match across mapped text fields with field boostsPOST /{index}/_search
Tokens not matching user languageOperator, minimum_should_match, or synonym analyzersPOST /{index}/_search, optionally POST /{index}/_analyze

Decision rule: If the user names a document that must rank first for a specific query, use query rules. If results are generally weak for a phrase, tune the organic query from the mapping. Do not simulate pinning with extreme boosts, function_score, or sort clauses.

Process

  1. Inspect the mapping and current query. Call GET / to confirm connectivity. When the index is unknown, narrow candidates with GET /_cat/indices, then call GET /{index}/_mapping.

    From the mapping, list every text field (e.g., title, description) and every keyword field used for filters (brand, category). Note which fields are short (precision) vs long (recall). Read the user's current search body if provided — identify which fields it queries and whether it already uses rule, multi_match, or single-field match.

    Decision: Is the problem deterministic promotion (one doc must win for one query) or organic ranking (several docs should score better)? Data needed: index name, mapping properties, current query JSON, example query strings, and target document ID(s) when pinning.

  2. Choose the relevance lever. Apply the decision from step 1:

    • Pinning / promotion → Create a query-rules ruleset with a rule of type pinned (never exclude for promotion). Set criteria so the rule fires only for the intended query text — e.g., contains or exact on a metadata key such as query_string with value "sale". Set actions to pin the correct document via ids (e.g., ["SKU123"]) or docs (e.g., [{"_index":"catalog","_id":"SKU123"}]). Use docs when _id may not be unique across indices. Read Query Rules Reference for full structure.

    • Organic ranking → Replace single-field match on a long field with multi_match across the mapped text fields. Boost short fields (typically title^2 with description unboosted). Consider operator, minimum_should_match, or synonym-aware analyzers when multi-word recall is still poor — but do not sort by price, date, or keyword fields to fake better text relevance, and do not query .keyword sub-fields with term for analyzed user phrases. Read Multi-Match Tuning.

    Decision: Pick exactly one primary lever per request. Data needed: chosen fields and boosts, ruleset ID and rule ID names, criteria metadata keys, and pinned document identifiers.

  3. Apply the change. Execute the APIs for the chosen lever:

    Query rules path

    • Create or replace the ruleset with PUT /_query_rules/{ruleset_id} (or add one rule with PUT /_query_rules/{ruleset_id}/_rule/{rule_id}).

    • Confirm structure with GET /_query_rules/{ruleset_id}.

    • Validate criteria with POST /_query_rules/{ruleset_id}/_test using the same match_criteria you will pass at search time.

    • Wire the search: POST /{index}/_search must use a rule query whose ruleset_id references the ruleset and whose match_criteria supplies values for every criteria metadata key (e.g., "query_string": "sale"). Place the normal relevance clause inside organic. Creating the ruleset alone does not pin anything — the pin applies only when search includes the rule query.

    Organic tuning path

    • Build a candidate multi_match (or equivalent bool/should) query from the mapping.

    • Optionally inspect analysis with POST /{index}/_analyze on sample query text when tokenization explains misses.

    Decision: Stop after one coherent change set; avoid stacking unrelated edits before testing.

  4. Test and compare top hits. Before and after each candidate, call POST /{index}/_search with the same size (≥ 10), the user's query string, and "track_scores": true. For pinning, the search body must include the rule query from step 3.

    Compare for each run:

    • Top _id values and order

    • _score where relevant

    • Key _source fields (title, description, product id)

    For pinning, confirm the target document (e.g., SKU123) is first when match_criteria matches the query and that organic matches still appear below. For organic tuning, confirm titles and intent-aligned documents rise without relying on sort or keyword exact-match hacks.

    Decision: Ship the candidate that wins on evidence; if none improve results, report what was tried and propose the next lever (e.g., synonyms or additional fields). Data needed: side-by-side top-hit lists from baseline and candidate queries.

Examples

Pin SKU123 for query "sale" on catalog

Wrong: Boost SKU123, sort by _id, or create a ruleset without a rule search query.

Right:

  1. PUT /_query_rules/catalog-sale-pin with a pinned rule, criteria matching query text "sale", actions pinning SKU123.

  2. POST /catalog/_search with:

{
  "query": {
    "rule": {
      "ruleset_id": "catalog-sale-pin",
      "match_criteria": { "query_string": "sale" },
      "organic": {
        "multi_match": {
          "query": "sale",
          "fields": ["title^2", "description"]
        }
      }
    }
  },
  "size": 10
}

Verify SKU123 is hit #1 and remaining hits are organic matches below the pin.

Improve "running shoes" when only description is searched

Mapping provides title and description as text, plus brand and category as keyword.

Wrong: Keep match on description only; sort by price; term query on title.keyword.

Right:

  1. Baseline: POST /catalog/_search with the user's current match on description; record top hits.

  2. Candidate: POST /catalog/_search with:

{
  "query": {
    "multi_match": {
      "query": "running shoes",
      "fields": ["title^2", "description"],
      "type": "best_fields",
      "operator": "or",
      "minimum_should_match": "75%"
    }
  },
  "size": 10
}
  1. Compare top hits — documents with "running shoes" in title should rank above description-only matches. If recall is still thin, consider synonym expansion in a follow-up iteration (not sort-by-price).

Guidelines

  • Ground every field name in the mapping — never invent name, content, or body without checking GET /{index}/_mapping.

  • Query rules for pins, boosts for ranking — merchandising belongs in query rules; field boosts belong in organic queries.

  • Match criteria wiring is mandatory — metadata keys in rule criteria must appear in the search rule.match_criteria object with the runtime values (typically the user's query string).

  • Test before claiming success — run baseline and candidate searches; cite top-hit changes.

  • Keyword fields filter; text fields search — use keyword fields in filter context, not as the primary full-text target for natural language.

  • Always deliver the concrete artifact — even when you cannot connect to a cluster to verify, produce the full ruleset JSON (for pinning) or the candidate query body (for organic tuning), then explain how to verify once the connection is available. Never stop at a high-level outline.

References

Operations

HTTP API (shorthand)elastic CLI command
GET /elastic es info
GET /_cat/indiceselastic es cat indices --index '<pattern>'
GET /{index}/_mappingelastic es indices get-mapping --index '<index>'
PUT /_query_rules/{ruleset_id}elastic es query-rules put-ruleset --ruleset-id '<id>' --rules '<json>'
PUT /_query_rules/{ruleset_id}/_rule/{rule_id}elastic es query-rules put-rule --ruleset-id '<id>' --rule-id '<id>' --type pinned --criteria '<json>' --actions '<json>'
GET /_query_rules/{ruleset_id}elastic es query-rules get-ruleset --ruleset-id '<id>'
POST /_query_rules/{ruleset_id}/_testelastic es query-rules test --ruleset-id '<id>' --match-criteria '<json>'
POST /{index}/_searchelastic es search --index '<index>' --query '<json>'
POST /{index}/_analyzeelastic es indices analyze --index '<index>' --field '<field>' --text '<text>'

Files

references/query-rules-reference.md

Query Rules Reference

Query rules let you pin or exclude documents when search-time match_criteria satisfy rule criteria. They replace ad-hoc sort scripts or extreme boosts for merchandising and curated results.

Rule types

TypeEffect
pinnedRewrites the organic query into a pinned query; listed documents appear first in order
excludeRemoves matching documents from the result set

Use pinned when the user wants a document at the top. Use exclude only when the user explicitly asks to hide documents. Never use exclude for promotion.

Criteria

Each criterion has type, metadata, and values. All criteria on a rule must match.

Criterion typeTypical use
exactMetadata value must equal one of values
containsMetadata value must contain one of values (substring)
fuzzyMetadata value must fuzzy-match one of values

The metadata key is a label you define — it is not an index field. At search time, the rule query passes match_criteria whose keys must match these metadata names.

Common pattern for query-text rules:

{
  "type": "contains",
  "metadata": "query_string",
  "values": ["sale"]
}

Narrow rules with additional criteria (locale, category, channel) only when the user requires them.

Actions for pinned / exclude

Provide either ids or docs, never both in one rule.

By _id only (works when IDs are unique within the searched indices):

"actions": { "ids": ["SKU123"] }

By index + _id (preferred when IDs may collide across indices):

"actions": {
  "docs": [{ "_index": "catalog", "_id": "SKU123" }]
}

Pinned queries cap at 100 documents total across matching rules.

Create a ruleset

PUT /_query_rules/{ruleset_id} replaces the entire ruleset body:

{
  "rules": [
    {
      "rule_id": "pin-sku123-on-sale",
      "type": "pinned",
      "criteria": [
        {
          "type": "contains",
          "metadata": "query_string",
          "values": ["sale"]
        }
      ],
      "actions": {
        "docs": [{ "_index": "catalog", "_id": "SKU123" }]
      }
    }
  ]
}

Add or update a single rule with PUT /_query_rules/{ruleset_id}/_rule/{rule_id} when merging into an existing ruleset without rewriting every rule.

Creating the ruleset does nothing until a search uses a rule query. The match_criteria object must supply values for every metadata key your criteria reference.

{
  "query": {
    "rule": {
      "ruleset_id": "catalog-sale-pin",
      "match_criteria": {
        "query_string": "sale"
      },
      "organic": {
        "multi_match": {
          "query": "sale",
          "fields": ["title", "description"]
        }
      }
    }
  }
}
  • ruleset_id — the ruleset you created.

  • match_criteria — binds search context to rule criteria (here, the user's query text).

  • organic — the normal relevance query; pinned hits prepend, then organic matches follow.

Validate criteria matching before searching with POST /_query_rules/{ruleset_id}/_test and the same match_criteria payload.

Anti-patterns for pinning

  • Boosting or sorting by product ID to fake a pin — fragile and breaks when inventory changes.

  • Using exclude when the user asked to promote a result.

  • Creating a ruleset but searching with only multi_match or match — the pin never applies.

  • Mismatching metadata names between rule criteria and search match_criteria.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

The author says: Elasticsearch 8.10 or later (query rules), self-managed, Elastic Cloud Hosted, or Elastic Cloud Serverless. Requires the `elastic` CLI ≥ 0.2 with `stack es` support.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
elastic
Publisher
Elastic (Official)
Source
elastic/agent-skills · skills/elasticsearch/elasticsearch-search-relevance · commit 09d879d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
elastic/agent-skills

AutoGineer is not affiliated with or endorsed by elastic.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download elasticsearch-search-relevance.zip