Run ES|QL across multiple indices and remote clusters, then decide explicitly where Query DSL/aggregations remain the better search contract.

ES|QL Across Multiple Indices/Clusters and When It Complements vs Replaces Query DSL/Aggregations

Teach ES|QL and OpenSearch PPL/SQL as distinct analytical interfaces with explicit boundaries relative to Query DSL, aggregations, dashboards, security, and resource cost.

Intermediate → Advanced115–150 minutesMulti-index / cross-cluster ES|QL lab · Chapter 23 · Lesson 02Elasticsearch/Kibana 9.5.3 · OpenSearch/Dashboards 3.8.0 · bundled JVMsLast reviewed: September 2026

Learning outcomes

AtlasMart now stores production telemetry in several daily indices and has remote regional clusters from Chapter 22. A single analytical question may span local indices, remote indices, or both. ES|QL can query those sources, but “works across clusters” does not mean identical latency, security, enrichment placement, or failure behavior to cross-cluster Search API.

01

Use ES|QL source patterns across multiple indices and remote clusters with explicit failure/security expectations.

02

Explain schema/type reconciliation when a table is built from heterogeneous index patterns.

03

Decide whether a requirement belongs in ES|QL or Query DSL/aggregations based on semantics rather than syntax preference.

04

Reason about cross-cluster enrichment/lookup placement and WAN transfer cost.

05

Validate completeness, latency, and authorization before promoting multi-source analytics.

Pinned analytics-language baseline

Examples are reviewed against Elasticsearch/Kibana 9.5.3 and OpenSearch/OpenSearch Dashboards 3.8.0, using their bundled JVMs and the course's existing local TLS/auth conventions. Elasticsearch examples use ES|QL through POST /_query. OpenSearch examples use the bundled SQL plugin through /_plugins/_ppl and /_plugins/_sql; a minimal OpenSearch distribution may require that plugin to be installed. Syntax, commands, functions, cross-cluster support, result limits, and preview/experimental status are version-sensitive—verify them against the exact deployment before promoting a query. No live Elasticsearch/OpenSearch cluster is available in this generation environment; the requests are reproducible, deterministic fixture invariants are stated explicitly, and runtime/benchmark values are left as MEASURED rather than fabricated.

Execution and safety note

Run mutating, destructive, security, lifecycle, snapshot, failure-injection, and load-test commands only in the disposable AtlasMart lab or an equivalently isolated environment. Verify the target cluster, index, tenant, credentials, and rollback path before execution; treat shown output as an expected invariant unless the lesson explicitly labels it as captured evidence.

1. Multi-index source patterns

FROM can target index patterns, aliases, data streams, and—in supported cross-cluster configurations—remote-cluster-qualified sources. The result table is typed. If the same field has incompatible mappings across sources, the analytical surface can expose type conflicts; that is a schema-governance problem, not a reason to cast blindly.

Local multi-index ES|QL
POST /_query?format=txt
{
  "query": """
    FROM atlasmart-telemetry-2026.09.10, atlasmart-telemetry-2026.09.11
    | WHERE environment == "prod"
    | STATS requests = SUM(request_count), errors = SUM(error_count) BY service
    | EVAL error_rate = TO_DOUBLE(errors) / TO_DOUBLE(requests)
    | SORT error_rate DESC
  """
}

Use aliases or data streams when they express a stable application contract. A wildcard that accidentally captures an old index with a different type for request_count can turn an otherwise valid analysis into a type-resolution failure or a semantic mismatch.

2. Cross-cluster ES|QL is distributed analytics

With remote clusters configured, ES|QL can reference sources such as region_b:atlasmart-telemetry-*. The local cluster coordinates a distributed analytical plan. WAN latency, remote availability, credentials, and version compatibility therefore enter the query's correctness envelope.

Cross-cluster source
POST /_query?format=txt
{
  "query": """
    FROM atlasmart-telemetry-*, region_b:atlasmart-telemetry-*
    | WHERE environment == "prod"
    | STATS requests = SUM(request_count), errors = SUM(error_count) BY service
    | EVAL error_rate = TO_DOUBLE(errors) / TO_DOUBLE(requests)
    | SORT errors DESC
  """
}
Completeness is part of the result contract.

If a remote cluster is optional/skipped or unavailable, a syntactically successful query may represent a degraded regional view. Preserve the Chapter 22 rule: verify which clusters participated before interpreting a “global” table.

3. ENRICH/LOOKUP placement across clusters

Current ES|QL has explicit cross-cluster rules for ENRICH and LOOKUP JOIN. Default/remote execution can require consistent reference data on involved clusters. Coordinator modes can centralize enrichment/lookup but increase data movement and coordinator work. Some command ordering is restricted because pipeline-breaking commands move execution to the coordinating side.

Pattern Data locality Tradeoff
Local/default lookup Lookup data present where operation executes Lower transfer when reference data is consistently deployed.
Remote enrichment Each remote enriches regional rows Good for region-specific reference data; requires policy/data on remotes.
Coordinator lookup/enrich Rows cross WAN, reference data remains local Simplifies reference ownership but can increase transfer/coordinator load.

Do not solve missing lookup data by silently copying a stale snapshot to every region. Version the reference dataset and include its version in analytical evidence.

4. Query DSL and ES|QL overlap—but do not collapse

Both interfaces can filter and aggregate. Query DSL remains the native search contract for scored document retrieval, highlighting, detailed search options, and the full aggregation tree. ES|QL is often clearer for chained transformations, derived columns, tabular analysis, and analysts who think in pipelines. “Can express” is different from “should use.”

Requirement ES|QL Query DSL + aggs
Tabular derived metrics Strong fit Possible but client reshaping often needed
Scored hits + highlights Not the primary contract Strong fit
Nested aggregation tree / specialized agg May not expose every search feature Native fit
Pipeline-style transformation Native mental model Often verbose JSON/client work
PIT/search_after pagination Not a substitute Native Search API path
Analytical cross-cluster table Supported with constraints CCS Search API remains alternative

5. Same requirement, two interfaces

Use the shared telemetry fixture (or equivalent daily indices) and execute the service-level rollup via both APIs. Compare exact requests/errors/average semantics first. Then measure the client/runtime cost separately.

Shared AtlasMart telemetry fixture
PUT atlasmart-telemetry-v23
{
  "settings": {"number_of_shards": 1, "number_of_replicas": 0},
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "@timestamp": {"type": "date"},
      "service": {"type": "keyword"},
      "environment": {"type": "keyword"},
      "tenant_id": {"type": "keyword"},
      "request_count": {"type": "long"},
      "error_count": {"type": "long"},
      "duration_ms": {"type": "double"}
    }
  }
}

POST atlasmart-telemetry-v23/_bulk?refresh=true
{"index":{"_id":"e01"}}
{"@timestamp":"2026-09-11T10:00:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":100,"error_count":2,"duration_ms":120}
{"index":{"_id":"e02"}}
{"@timestamp":"2026-09-11T10:01:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":120,"error_count":3,"duration_ms":140}
{"index":{"_id":"e03"}}
{"@timestamp":"2026-09-11T10:02:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":80,"error_count":4,"duration_ms":220}
{"index":{"_id":"e04"}}
{"@timestamp":"2026-09-11T10:03:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":90,"error_count":5,"duration_ms":260}
{"index":{"_id":"e05"}}
{"@timestamp":"2026-09-11T10:05:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":130,"error_count":1,"duration_ms":110}
{"index":{"_id":"e06"}}
{"@timestamp":"2026-09-11T10:06:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":140,"error_count":2,"duration_ms":130}
{"index":{"_id":"e07"}}
{"@timestamp":"2026-09-11T10:07:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":100,"error_count":2,"duration_ms":210}
{"index":{"_id":"e08"}}
{"@timestamp":"2026-09-11T10:08:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":110,"error_count":3,"duration_ms":250}
{"index":{"_id":"e09"}}
{"@timestamp":"2026-09-11T10:10:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":150,"error_count":1,"duration_ms":100}
{"index":{"_id":"e10"}}
{"@timestamp":"2026-09-11T10:11:00Z","service":"catalog-api","environment":"prod","tenant_id":"tenant-a","request_count":160,"error_count":1}
{"index":{"_id":"e11"}}
{"@timestamp":"2026-09-11T10:12:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":120,"error_count":6,"duration_ms":280}
{"index":{"_id":"e12"}}
{"@timestamp":"2026-09-11T10:13:00Z","service":"checkout-api","environment":"prod","tenant_id":"tenant-a","request_count":130,"error_count":7,"duration_ms":300}
Query DSL + aggregations
POST atlasmart-telemetry-v23/_search
{
  "size": 0,
  "query": {"bool": {"filter": [
    {"term": {"environment": "prod"}},
    {"range": {"@timestamp": {"gte": "2026-09-11T10:00:00Z", "lt": "2026-09-11T10:15:00Z"}}}
  ]}},
  "aggs": {
    "services": {
      "terms": {"field": "service", "size": 20},
      "aggs": {
        "requests": {"sum": {"field": "request_count"}},
        "errors": {"sum": {"field": "error_count"}},
        "avg_ms": {"avg": {"field": "duration_ms"}},
        "p95_ms": {"percentiles": {"field": "duration_ms", "percents": [95]}}
      }
    }
  }
}
ES|QL equivalent intent
POST /_query?format=txt
{
  "query": """
    FROM atlasmart-telemetry-v23
    | WHERE environment == "prod"
      AND @timestamp >= "2026-09-11T10:00:00Z"
      AND @timestamp <  "2026-09-11T10:15:00Z"
    | STATS requests=SUM(request_count), errors=SUM(error_count),
            avg_ms=AVG(duration_ms), p95_ms=PERCENTILE(duration_ms,95)
      BY service
    | EVAL error_rate=TO_DOUBLE(errors)/TO_DOUBLE(requests)
    | SORT service
  """
}

The response shapes differ: Query DSL returns aggregation objects/buckets, while ES|QL returns typed columns and row values. A test should normalize both into the same domain records before comparing.

6. Boundary case: analytical language for latency-critical lookup

Wrong approach: replace every product lookup with ES|QL because the query is shorter.

A product page that needs one document by ID or keyword with strict latency is a different workload from a telemetry rollup. Use the simplest search/get API that carries the needed semantics, then measure. ES|QL readability does not erase parse/planning/distributed execution costs or result-model differences.

Latency experiment contract
workload=A: GET/product lookup via Search API
workload=B: equivalent ES|QL filtered row query
same_dataset=true
same_auth=true
same_concurrency=true
same_warmup=true
same_network=true
p50_A=MEASURED
p95_A=MEASURED
p99_A=MEASURED
p50_B=MEASURED
p95_B=MEASURED
p99_B=MEASURED
cpu_A=MEASURED
cpu_B=MEASURED
choice=BASED_ON_REQUIREMENT_AND_EVIDENCE

7. Production gate

Promote cross-index/cross-cluster ES|QL only after schema compatibility, remote-cluster participation, authorization, reference-data versioning, query result semantics, and p95/p99 under representative WAN conditions are verified. Security scopes should name only the required sources; a pipeline language is not a reason to grant wildcard read access.

Check your understanding

  1. What can make a multi-index ES|QL query fail before execution?
  2. Why can a successful global query still be incomplete?
  3. When does coordinator-side enrichment cost more?
  4. Why keep Query DSL in the architecture?
  5. What should an interface comparison normalize first?
Review the answers

1. Incompatible field types or other schema conflicts across matched sources.

2. An optional remote cluster can be unavailable/skipped, so cluster participation must be inspected.

3. When source rows must cross the WAN before being enriched/ joined locally.

4. It remains the native surface for many hit/search/aggregation controls that ES|QL does not replace.

5. Domain semantics and result shape before comparing latency or developer ergonomics.

Cleanup
DELETE atlasmart-telemetry-v23

Next, move to OpenSearch and learn PPL and SQL on their own terms.

Summary and next step

Preserve the evidence, assumptions, version boundaries, and safety checks established in this lesson. Carry them into the next lesson—or, at the end of the capstone, into the production runbook—rather than treating this lesson as an isolated recipe.

References

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.