Search by term proximity and order with the Query DSL intervals query

The Query DSL intervals query finds documents based on the order and proximity of matching terms. Unlike the match_phrase query, it lets you define flexible rules about how terms relate to each other. You can allow gaps between terms, enforce term order, and combine multiple term patterns.

This tutorial is for developers who know basic Query DSL queries and want finer control over how matching terms relate to each other.

By the end of this tutorial, you can:

  • Run a basic ordered proximity search using all_of with match
  • Combine all_of and any_of rules for flexible matching
  • Use the prefix rule to match partial words
  • Apply a filter rule to exclude unwanted terms from proximity matches
Tip

The code examples use Console syntax. You can convert examples into other programming languages in the Console UI.

To follow this tutorial, you need:

  • Access to an Elasticsearch project or deployment. Refer to Choosing your deployment type for deployment options.
  • A way to send requests, such as Console.
  • Index privileges to create, write to, read, and delete the books index, such as the all index privilege.
  • Familiarity with basic Query DSL queries. It also helps to know how text analysis breaks text into terms at specific positions, because intervals rules match on those positions.

Create a books index and add several sample documents:

				PUT /books
		
				POST /books/_bulk?refresh=wait_for
					{"index":{"_id":"1"}}
{"title":"Elasticsearch in Action","synopsis":"A practical guide to full text search with Elasticsearch. Covers queries, filters, and performance tuning."}
{"index":{"_id":"2"}}
{"title":"Search Fundamentals","synopsis":"Learn the basics of full text search including tokenization, analyzers, and relevance scoring."}
{"index":{"_id":"3"}}
{"title":"Faster Elasticsearch","synopsis":"Tips for fast and efficient search at scale. Covers caching, faster queries, and cluster optimization."}
{"index":{"_id":"4"}}
{"title":"Machine Learning for Search","synopsis":"Apply machine learning to improve search relevance. Covers learning to rank and query classification."}
{"index":{"_id":"5"}}
{"title":"Advanced Text Processing","synopsis":"Deep dive into text analysis and search pipelines. Full coverage of custom analyzers and token filters."}
		

The response shows "errors": false when all five documents are indexed.

By default, text fields use the standard analyzer. It lowercases and tokenizes the text, so each word becomes a searchable term at a specific position.

To learn more, refer to Text analysis.

Use all_of with ordered: true to find books where full text appears immediately before search, with no gaps allowed:

				GET /books/_search
					{
  "query": {
    "intervals": {
      "synopsis": {
        "all_of": {
          "ordered": true,
          "max_gaps": 0,
          "intervals": [
            {
              "match": {
                "query": "full text",
                "max_gaps": 0,
                "ordered": true
              }
            },
            {
              "match": {
                "query": "search"
              }
            }
          ]
        }
      }
    }
  }
}
		
  1. ordered: true requires the first interval (full text) to appear before the second (search).
  2. max_gaps: 0 on all_of requires both intervals to be adjacent, with no words between text and search.

Documents 1 and 2 match because both contain full text search as three contiguous, ordered terms. The other documents don't contain the phrase full text search.

Use any_of inside all_of to match alternative terms. This query finds books where search appears within two positions of either machine learning or text, in any order:

				GET /books/_search
					{
  "query": {
    "intervals": {
      "synopsis": {
        "all_of": {
          "ordered": false,
          "max_gaps": 2,
          "intervals": [
            {
              "match": {
                "query": "search"
              }
            },
            {
              "any_of": {
                "intervals": [
                  { "match": { "query": "machine learning" } },
                  { "match": { "query": "text" } }
                ]
              }
            }
          ]
        }
      }
    }
  }
}
		
  1. ordered: false allows the intervals to appear in any order.
  2. max_gaps: 2 allows up to two unmatched positions between the intervals.
  3. any_of matches either machine learning or text.

Documents 1 and 2 match because text appears immediately before search (zero gaps). Document 5 matches because text and search are two positions apart (analysis and and are between them). Document 4 matches because search appears two positions after machine learning (to and improve are between them). Document 3 doesn't match because its synopsis contains neither text nor machine learning near search.

The scores drop as the matching terms get farther apart. Documents 1 and 2 score highest because their matching terms are adjacent, and documents 5 and 4 score lower because other terms sit between the matches. The intervals query gives shorter matches a higher score.

The prefix rule matches any term that starts with a given set of characters. This is useful when the input contains a partial word, such as when a user has typed only the start of a word in a search box.

Find books where search is immediately followed by a term that starts with relev:

				GET /books/_search
					{
  "query": {
    "intervals": {
      "synopsis": {
        "all_of": {
          "ordered": true,
          "max_gaps": 0,
          "intervals": [
            {
              "match": {
                "query": "search"
              }
            },
            {
              "prefix": {
                "prefix": "relev"
              }
            }
          ]
        }
      }
    }
  }
}
		
  1. The prefix rule matches any indexed term that starts with relev, such as relevance and relevant.

Document 4 matches because its synopsis contains search relevance. If you replace the prefix rule with a match rule for relev, the query returns no results, because relev isn't a complete term in the index. Document 2 also contains relevance, but four other terms sit between it and search, so max_gaps: 0 excludes it.

Warning

The prefix rule can expand to match many terms depending on your data. If a prefix matches more terms than the indices.query.bool.max_clause_count search setting allows, Elasticsearch returns an error. To avoid this error, use the index_prefixes mapping option or keep prefixes specific enough to limit expansion.

Use the filter rule to exclude unwanted terms from a proximity match. This query finds books where search appears near text, but only when analysis doesn't appear between them:

				GET /books/_search
					{
  "query": {
    "intervals": {
      "synopsis": {
        "match": {
          "query": "text search",
          "max_gaps": 5,
          "ordered": false,
          "filter": {
            "not_containing": {
              "match": {
                "query": "analysis"
              }
            }
          }
        }
      }
    }
  }
}
		
  1. The not_containing filter excludes intervals where analysis appears between text and search.

Documents 1 and 2 match because both contain text near search without analysis between them. Document 5 also contains text near search and matched in Step 3. However, its synopsis contains text analysis and search, so analysis appears between the two terms and the filter excludes the document.

Delete the books index:

				DELETE /books
		

In this tutorial, you learned how to:

  • Search for ordered, contiguous phrases with all_of and max_gaps
  • Match alternative terms using any_of
  • Match partial words using the prefix rule
  • Exclude unwanted terms from proximity matches using a filter rule