All Products
Search
Document Center

OpenSearch:layer clause

Last Updated:Apr 01, 2026

The layer clause lets you perform hierarchical queries — a retrieval strategy that divides your index into prioritized scan ranges, each with its own quota and optional query logic. Instead of scanning all documents with a single query, you define a sequence of layers that OpenSearch Retrieval Engine Edition evaluates in order, stopping each layer when its quota is met and carrying any shortfall forward to the next.

Use the layer clause when:

  • Your index is sorted by an attribute field and you want to skip full scans by targeting a specific range of documents.

  • You need a guaranteed minimum result count before falling back to a broader query.

  • You need real-time data to appear in results before sorted historical data.

  • A single query mode (AND, OR, or RANK) cannot balance result count and query performance for your use case.

Key concepts

TermDescription
docidThe internal ID that OpenSearch Retrieval Engine Edition assigns to each document. During a query, documents are scanned by docid in ascending order.
rangeA contiguous slice of the document space, defined by attribute field values or extension keywords. Each layer scans one or more ranges.
quotaThe maximum number of documents to retrieve from a layer. If a layer falls short of its quota, the shortfall is added to the next layer's quota.
seekThe operation that locates a document during a query.
layerA layer can contain one or more ranges. The layer that contains a range determines the query priority of the range. You can specify a layer in the following formats: [query] layer, layer [query] range, and range docid seek name.

Syntax

{
  "layer": [
    {
      "range": { ... },
      "quota": <number>
    }
  ]
}

Layers are evaluated in array order — index 0 has the highest priority. The layer clause is optional.

quota

quota sets the maximum number of documents a layer can contribute to the final result set.

Key behaviors:

  • The total quota across all layers must not exceed rank_size. If quotas sum to more than rank_size, later layers are capped. For example, with rank_size=10 and quota:5;quota:7, only 5 documents are retrieved from the second layer.

  • If a layer retrieves fewer documents than its quota, the remaining capacity rolls over to the next layer automatically.

  • OpenSearch Retrieval Engine Edition supports two quota check modes: In the first method (per-document), it checks the remaining quota each time it retrieves a document. In the second method (post-scan), it does not check the quota during retrieval; after scanning all documents in the layer, it checks the remaining quota and adds it to the next layer's quota. In both modes, the total retrieved documents never exceed rank_size.

  • Default quota: 0. Maximum quota: the maximum value of uint32_t.

range

range defines which documents OpenSearch Retrieval Engine Edition scans in a layer. If omitted, the full document space [0, docCount) is scanned.

Specify a range using attribute fields:

"range": {
  "fields": [
    { "field": "<attribute-field>", "values": [<value1>, <value2>] }
  ]
}

Requirements for attribute field ranges:

  • Use attribute fields only. Calculation expressions are not supported.

  • The attribute fields must be sorted using the same method as the queried documents. If the sort method differs, the range is invalid and a full scan runs instead.

  • List attribute fields continuously. Do not insert extension keywords between fields.

Extension keywords

Use extension keywords in index_type or alongside fields to target predefined data slices:

KeywordTargets
%sortedSorted full data and incremental data
%unsortedUnsorted data, including real-time data
%otherDocuments not covered by any specified layer
%docidA specific range of document IDs
%segmentidA specific range of segments
%percentA percentage slice of a range, in [value1, value2) format

If neither %sorted nor %unsorted is specified, OpenSearch Retrieval Engine Edition includes both automatically. In default mode, sorted documents are scanned first; if the quota is not met, real-time data from the next layer is scanned.

Multiple query clauses per layer

Assign a different query to each layer by separating queries with semicolons (;):

{
  "query": "A OR B;A RANK B;A AND B",
  "layer": [
    { "quota": 1000 },
    { "quota": 1000 },
    { "quota": 1000 }
  ]
}

If the number of layers exceeds the number of clauses, the remaining layers use the last clause.

Examples

Target a sorted range by attribute field

If offline sorting is enabled and your documents are sorted by an attribute field such as site_id, documents from the same site occupy a contiguous range in the index. Targeting that range avoids a full scan.

Search for iphone in sites 1 and 7:

{
  "layer": [
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [1, 7] }
        ]
      },
      "quota": 5000
    }
  ]
}

Add a fallback layer for sites 5 and 10, scanned only when the first layer falls short of its quota:

{
  "layer": [
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [1, 7] }
        ]
      },
      "quota": 5000
    },
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [5, 10] }
        ]
      },
      "quota": 0
    }
  ]
}

Setting quota to 0 on the second layer means it receives only the quota rolled over from the first layer.

Distribute the quota explicitly across both layers:

{
  "layer": [
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [1, 7] }
        ]
      },
      "quota": 4000
    },
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [5, 10] }
        ]
      },
      "quota": 1000
    }
  ]
}

Target a multi-dimensional range

When documents are sorted by multiple fields — for example, by site_id and then by static_score within each site — define a range across both dimensions.

Retrieve pages with a static score above 100 from sites 1 and 7:

{
  "layer": [
    {
      "range": {
        "fields": [
          { "field": "site_id", "values": [1, 7] },
          { "field": "static_score", "values": "[100,]" }
        ]
      },
      "quota": 4000
    }
  ]
}
Enclose range values for numeric fields in double quotation marks when using open-ended intervals, such as "[100,]".

Balance result count and query performance

A single query mode trades off result volume against performance:

Query modeResult volumePerformance
A AND BLowHigh
A OR BHighLow
A RANK BMediumMedium

No single mode covers all cases. To obtain sufficient results without compromising query performance, you can specify multiple query modes in one query statement. A layered query chains all three modes: A OR B fills the quota first with the broadest results, A RANK B adds medium-precision results if needed, and A AND B fills any remaining gap with the highest-precision matches.

{
  "query": "A OR B;A RANK B;A AND B",
  "layer": [
    { "quota": 1000 },
    { "quota": 1000 },
    { "quota": 1000 }
  ]
}
Layers after the first are only scanned when earlier layers fall short of their quota. If the first-layer query (A OR B) returns sufficient results, later layers are not scanned. Set quotas based on the expected result distribution for your data.

Prioritize real-time data

When result freshness matters, query %unsorted (real-time) data first, then fall back to sorted data. Use %percent to further control which portion of the sorted range is scanned.

Query real-time data first, then the lower-ranked 50% of sorted documents in services 1 and 3, then the top 50%:

{
  "layer": [
    {
      "range": {
        "index_type": "%unsorted"
      },
      "quota": 5000
    },
    {
      "range": {
        "index_type": "%sorted",
        "fields": [
          { "field": "service_id", "values": [1, 3] }
        ],
        "percent": "[50,100)"
      },
      "quota": 0
    },
    {
      "range": {
        "index_type": "%sorted",
        "fields": [
          { "field": "service_id", "values": [1, 3] }
        ],
        "percent": "[0,50)"
      },
      "quota": 0
    }
  ]
}

%percent accepts a half-open interval in [value1, value2) format, where values represent percentages of the range.