Span queries

Span queries #

Span queries are low-level positional queries that expose and control the positions of terms — the building blocks for phrase-adjacent logic that phrase does not express directly. Most use cases are better served by match_phrase (or its slop); the span family exists for the remaining ones.

Inside a nested query: not supported yet — span clauses are rejected with an HTTP 400 naming the offending clause. Use phrase with slop for positional logic.

How it works #

The engine rewrites each span query to its fastest equivalent before execution. An in_order span_near whose clauses are all span_term becomes the engine’s native phrase query — reusing the block-max-WAND phrase producer, the fastest position pipeline available; on that path slop carries phrase-slop semantics (swapping two adjacent terms costs 2 slop, so a reversed occurrence matches from slop: 2). A root span_multi becomes its wrapped matcher and a span_or becomes a bool.should. The residual shapes — multi-term leaves inside span_near, unordered span_near — run a native two-phase evaluator: phase one takes the clause AND (each clause’s terms expanded from the dictionary) as the candidate set, phase two decodes each candidate’s positions and counts alignments with a per-pair gap budget (next.start − prev.end, ordered) or a minimal covering window (unordered, the window-minus-widest-span rule). Frequencies cap at 1024 alignments — match-ness is exact, only the BM25 input is capped; the clause idf sum uses each clause’s rarest term. Without a position index the family degrades to the clause AND, exactly like phrase. span_term itself accepts three wire shapes: { "field": "value" }, { "field": { "value": "v" } } and { "field": …, "value": … }.

span_term #

Matches documents containing an exact term at any position — the positional unit the other span queries compose.

POST /my-collection/_search
{
  "query": {
    "span_term": {
      "field": "message",
      "value": "search"
    }
  }
}

Parameters: field (required, string), value (required, string), boost (optional, default 1.0).

span_near #

Matches documents where the given span clauses occur within slop positions of each other, optionally in order — the span form of a proximity match — get within three positions of search, in order (25 log lines in the dataset):

POST /my-collection/_search
{
  "query": {
    "span_near": {
      "clauses": [
        { "span_term": { "field": "message", "value": "get" } },
        { "span_term": { "field": "message", "value": "search" } }
      ],
      "slop": 3,
      "in_order": true
    }
  }
}
  • clauses — (Required, array of span queries) The spans to relate.
  • slop — (Optional, integer, default: 0) Maximum positions between the clauses.
  • in_order — (Optional, boolean, default: true) Whether the clauses must appear in the given order.

span_or #

Matches documents matching any of the given span clauses — the union of spans.

{
  "span_or": {
    "clauses": [
      { "span_term": { "field": "message", "value": "get" } },
      { "span_term": { "field": "message", "value": "post" } }
    ]
  }
}
  • clauses — (Required, array of span queries).

span_multi #

Wraps a multi-term query (such as prefix, wildcard, fuzzy, regexp, term) so it can be used as a span clause inside span_near / span_or. The wrapped query sits under the match key:

{
  "span_multi": {
    "match": {
      "prefix": { "field": "user", "value": "ki" }
    }
  }
}
  • match — (Required, query) The multi-term query to wrap.

Use cases #

  • Proximity with a budget. The playground’s get…search within slop: 3 keeps all 25 GET /search lines while admitting lines with a word between — the direct sibling of phrase + slop.
  • Positional unions. span_or of get and post matches 125 of the 150 log lines (100 + 25, no overlap) — an OR that stays a span, so it can feed a span_near afterwards.
  • Pattern leaves in proximity. span_multi wrapping prefix: cluster within slop: 2 of left (in order) matches the 25 cluster node left … lines — a wildcard family next to an anchor word, which phrase cannot express at all.
  • Wire compatibility. Span pipelines arriving from existing clients run unchanged — all four clauses take the standard wire forms.
  • intervals — the other positional grammar: a declarative rule tree (all_of/any_of + filter predicates) instead of clause composition. Prefer it for anything richer than near/or; prefer spans for the shapes above.
  • phrase — what an all-span_term span_near becomes internally; its slop documents the shared slop metric.
  • prefix, wildcard, fuzzy, regexp — the queries span_multi wraps.
Calendar October 6, 2026
Edit Edit this page