<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Sergey Sidorov - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Sergey Sidorov - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/search-labs/author/sergey-sidorov</link>
    </image>
    <link>https://www.elastic.co/search-labs/author/sergey-sidorov</link>
    <atom:link href="https://www.elastic.co/search-labs/rss/author/sergey-sidorov.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Fri, 11 Sep 2026 19:08:02 GMT</lastBuildDate>
  <item>
    <title><![CDATA[How we built PromQL into Elasticsearch]]></title>
    <description><![CDATA[PromQL runs on the same Elasticsearch compute engine as ES|QL, with no plugin and no separate process to operate. Getting there meant changing how the engine evaluates time windows and builds grouping keys.]]></description>
    <content:encoded><![CDATA[<p>More than 80% of the Prometheus Query Language (PromQL) queries in our real-world corpus run on Elasticsearch without modification. Elasticsearch 9.5 makes the PromQL and the Prometheus-compatible API generally available (GA), so you can ingest Prometheus metrics with<a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write"> remote write</a> and query them through the<a href="https://www.elastic.co/docs/reference/query-languages/promql/promql-http-api"> Prometheus HTTP APIs</a> or the<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/promql"> PROMQL</a> command in Elasticsearch Query Language (ES|QL).</p><p>PromQL compiles to the same compute engine that runs ES|QL and inherits its planner and distributed execution, along with its release process. We didn’t build a second engine for this, and there’s no plugin to install.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9e310070ec244c5d/6aa3928e224d356c5e0dd18e/1.png" alt="PromQL compatibility in Elasticsearch rising from zero to 80% between 9.4 Tech Preview and 9.5 GA" /><p>This post is about how we built it.</p><p>Key takeaways:</p><ul><li><p><strong>One engine:</strong> The implementation combines Elasticsearch’s mature distributed planning, storage, and testing infrastructure with its newer compute engine, which provides a columnar execution runtime. This lets PromQL reuse proven Elasticsearch capabilities while executing through a modern, native vectorized pipeline rather than introducing a separate runtime.</p></li><li><p><strong>One server:</strong> Elasticsearch implements the Prometheus remote write and query APIs directly, so Prometheus-compatible ingest and queries run without any additional plugins.</p></li><li><p><strong>Engineered for efficiency:</strong> Supporting PromQL required new engine primitives for range-aligned evaluation grids, backward-looking windows, dynamic label grouping, pipeline result reshaping, and compact wide aggregation keys. These primitives allow PromQL queries to execute efficiently end to end, with the relevant semantics implemented directly in the compute engine rather than through external post-processing.</p></li><li><p><strong>Compatibility measured in real use:</strong> In addition to Prometheus compliance tests, we built a differential-testing and quality-control pipeline over 2,000 PromQL queries collected from public repositories. </p></li></ul><p>Read more:</p><ul><li><p><a href="https://www.elastic.co/observability-labs/blog/elasticsearch-supports-promql">Query Prometheus Metrics in Elasticsearch with PromQL</a></p></li><li><p><a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch">Ship Prometheus Metrics to Elasticsearch with Remote Write</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-native-prometheus-api">Bringing Fire to Elasticsearch: Adding Native Prometheus APIs</a></p></li></ul><h2><strong>Why run PromQL on Elasticsearch</strong></h2><p>Many teams already store logs and traces in Elasticsearch while running metrics in Prometheus or another dedicated metrics back end.</p><p>Prometheus and its ecosystem are strong and widely adopted, but large deployments can also bring operational sprawl and scaling challenges, along with limited retention. </p><p>So we set out to combine Elastic’s highly optimized time-series database (TSDB) with a best-in-class metrics ecosystem. The result is a smaller observability stack, with fewer systems to operate and metrics storage that scales horizontally and supports long-term retention.</p><h2><strong>One engine: PromQL and ES|QL share the same compute engine</strong></h2><p>We made an early architectural decision not to run a separate PromQL engine next to Elasticsearch.</p><p>PromQL is instead another front end to the Elasticsearch compute engine.</p><p>This puts PromQL in the normal Elasticsearch development lifecycle. It uses the same planner, distributed execution engine, testing infrastructure, and release process as Elasticsearch itself.</p><p>To learn more about Elasticsearch’s query engine, check <a href="https://www.elastic.co/search-labs/blog/elasticsearch-columnar-metrics-engine-30x-faster-prometheus">our blog</a>.</p><p>Like ES|QL’s time series queries that use the <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> source command, PromQL is translated into a highly optimized query plan and executed across the cluster. Nodes process columnar batches through vectorized operators, while partial results move through exchanges until the final result is assembled. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcf3a413e89209805/6aa392b38406d9813bcaa1d8/2.png" alt="PromQL and ES|QL frontends feed one shared Elasticsearch planner and columnar execution DAG across shards" /><p>This also means that PromQL and ES|QL operate over the same execution engine and time-series data. ES|QL can additionally extend a PromQL computation with post-processing that PromQL doesn’t support, such as lookup joins and inline aggregations.</p><p>For example, assume Prometheus request counters are stored in <code>metrics-*</code> and keyed by the <code>service</code> label. A lookup index, <code>service_registry</code>, maps each instance to its owning team and environment and to its service tier:</p><p></p><p></p><p>This architecture requires the execution engine to support PromQL semantics natively and efficiently rather than ES|QL syntax sugar. The following sections describe the changes and new execution primitives we introduced to achieve that.</p><h2><strong>One server: Prometheus remote write and HTTP API built into Elasticsearch</strong></h2><p>Query execution is only half of the story. The Prometheus ecosystem also expects familiar ingest and query APIs.</p><p>Prometheus protocols are the de facto standard for everything metrics in almost every team’s observability stack. So we built the HTTP API directly in the Elasticsearch server, which eliminated the need for a third component and tightened integration stability and performance.</p><p>On the ingest side, we <a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch-architecture">added</a> an endpoint for the <a href="https://prometheus.io/docs/specs/prw/remote_write_spec/">Prometheus remote write</a> protocol. It accepts Snappy-compressed Protocol Buffer messages, maps labels to TSDS dimensions, maps the metric name/value into metric fields, infers counter versus gauge mappings, and writes directly into TSDS. The built-in template is dynamic, so users don’t have to predeclare every Prometheus label or metric.</p><p>On the query side, Elasticsearch <a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch-architecture">exposes</a> Prometheus query APIs. A request enters through the Prometheus endpoint and is executed in the compute engine.</p><h2><strong>Running PromQL efficiently in a columnar engine</strong></h2><p>Sharing an execution engine doesn’t mean treating PromQL as syntax sugar over ES|QL. PromQL has different time, grouping, and response semantics, as well as workload characteristics that matter at scale. Supporting it efficiently required extending the compute engine rather than compensating in the API layer.</p><h3><strong>PromQL time grids: Aligning evaluation steps with TSTEP</strong></h3><p>Time-series query engines are optimized for grouping over time.</p><p>Elasticsearch normally groups timestamps with <code>TBUCKET(...)</code>, which truncates each timestamp to a fixed interval boundary. Truncation is cheap and produces deterministic bucket boundaries. It also makes intermediate results easier to reuse.</p><p>Prometheus defines evaluation points differently. For a range query, timestamps are laid out as fixed steps anchored to the query range, rather than derived by truncating each sample timestamp. Two queries with the same step but different range boundaries can therefore produce different evaluation grids.</p><p>To preserve these semantics, we introduced <code>TSTEP(...)</code>, which derives its grouping grid from the query range and step,  rather than truncating timestamps to globally aligned boundaries.</p><p>PromQL uses <code>TSTEP(...)</code> internally, preserving Prometheus timestamp semantics while still lowering the operation to a native Elasticsearch execution primitive.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt437155bca2029671/6aa3937927a5312436dcbf38/3.png" alt="TSTEP vs TBUCKET in Elasticsearch: PromQL step grid anchored to query start, TBUCKET to fixed boundaries" /><p>Some might see this as a simple problem, but the nuances matter. </p><p>Take, for example, a query that finds a 5m rolling average of a metric: </p><p></p><p></p><p>At evaluation time <code>T</code>, the result represents the average over the preceding five-minute range: </p><p><code>(T - 5m, T]</code></p><p>When the query is executed with a five-minute step, each output value is labeled with the upper end of its corresponding five-minute window.</p><p>Elasticsearch previously lacked this semantic and supported only forward-looking window aggregation functions:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97aac2fa95cf657d/6aa393b71ade64390142ee2e/4.png" alt="Forward-looking window aggregation where each bucket covers the interval from timestamp T to T plus W" /><p>We rewrote the window-evaluation path so that both ES|QL and PromQL use a common backward-looking windowing implementation:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a12ceb27d9ab95b/6aa393dcf08ee141fe856719/5.png" alt="Backward-looking PromQL window covering T minus W to T, the range used by rate and avg_over_time" /><p>Together, <code>TSTEP(...)</code> and backward-looking windows preserve the two time semantics that matter for PromQL range evaluation.</p><h3><strong>Dynamic label grouping: How PromQL </strong><strong><code>without()</code></strong><strong> resolves at runtime</strong></h3><p>Time grids determine when a PromQL expression is evaluated. Aggregation determines which input series are combined and which labels identify each output series.</p><p>For most analytical query engines, that identity is known when the query is planned. The planner can allocate grouping columns and choose an aggregation strategy. It also carries a fixed key through the execution pipeline.</p><p>That’s how ES|QL works:</p><p></p><p></p><p>The output series are grouped by an explicit key <code>(cluster, namespace)</code>.</p><p>PromQL can express the same operation in the opposite direction:</p><p></p><p></p><p>Now we know which dimensions <em>not</em> to use. We don’t necessarily know the full grouping key until the query is executed. This is a small language difference with significant execution consequences. </p><p>One possible implementation is to discover every label used by the metric, subtract <code>instance</code> and <code>pod</code>, and rewrite the expression into an ordinary <code>by(...)</code> aggregation. That adds a <a href="https://www.elastic.co/search-labs/blog/esql-metrics-info-ts-info-time-series-catalog">discovery phase</a> before planning. It also becomes inefficient for high-dimensional metrics where only a subset of all possible dimensions may have useful value in a particular series. Most queries need only a small subset of the available dimensions, so carrying the entire dimension universe as an aggregation key wastes memory and adds bookkeeping overhead.</p><p>We instead extended the time-series execution path with dynamic grouping columns. The engine loads dimensions as the series are read and applies the exclusions per time series. This avoids making the grouping schema a prerequisite for planning and avoids carrying a large sparse set of grouping columns through aggregation.</p><h3><strong>Dimension packing: Keeping wide PromQL grouping keys cheap</strong></h3><p>The <a href="https://prometheus.io/docs/practices/rules/#aggregation">idiomatic</a> way of writing PromQL aggregations involves heavy use of <code>without(...)</code> over <code>by(...)</code>:</p><p></p><p></p><p>Excluding labels rather than explicitly listing them makes dashboards and alerts resilient to schema evolution. If a new label is added to the metric, the query continues to preserve it unless it’s explicitly excluded.</p><p>The consequence for the engine is that effective grouping keys can be wide. Many of those labels often have low cardinality, yet each still participates in every aggregation stage.</p><p>In a columnar engine, each grouping column is normally represented as a separate vector. Ten grouping labels therefore mean 10 vectors flowing through every aggregation operator:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2f491d0d9fdb82f2/6aa3940027a53105ccdcbf3c/6.png" alt="Elasticsearch columnar page: rows split into typed blocks with delta and ordinal dictionary compression" /><p>Dimension fields are declared in the index mapping; the planner knows the schema up front, and grouping keys stay narrow and predictable.</p><p>In the columnar engine, each grouping label is carried as a separate vector or block. A key with 10 labels therefore requires 10 vectors to be read, hashed, compared, and retained by aggregation operators. As key width grows, so does the amount of data and bookkeeping that must move through the pipeline:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13b25c686bbb46e0/6aa39429de2395f2e2e86d1d/7.png" alt="PromQL aggregation without packing: each grouping label hashed as a separate block into 64-byte keys" /><p>To avoid paying that per-column cost for every additional label, we introduced dimension packing. Before aggregation begins, the engine encodes the full grouping key into a single compact representation. Hash and comparison operations run on the packed key rather than on each block independently:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a75855ef51c017b/6aa394468406d95667caa1de/8.png" alt="Dimension packing in Elasticsearch encodes PromQL grouping labels into one 16-byte key before hashing" /><p>Packing lets hashing and comparison operate on a single compact key rather than an increasing number of grouping blocks, making aggregation overhead less sensitive to key width. Because the engine is shared, ES|QL time-series queries will benefit from this optimization as well.</p><h3><strong>Building the Prometheus HTTP API response inside the pipeline</strong></h3><p>Unlike Elasticsearch's ES|QL column-oriented response format, the Prometheus response is row-oriented.  The Prometheus API returns one result row per time series, with its samples represented as timestamp-value pairs. </p><p>To support a compatible API layer, we had to regroup in the HTTP layer converting the columnar results into boxed row objects and accumulate them in map- and list-based structures until the complete Prometheus response could be produced. </p><p>We replaced this with the <code>TimeSeriesCollapse</code> compute operator. It groups rows by series and aligns samples to the query’s fixed step grid. It emits the reshaped result as ordinary columnar pages containing one row per series, with aligned multi-valued timestamp and value blocks. And it preserves compact, vectorized block representation throughout the pipeline: </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99e2ee3137bc2fa6/6aa39496d29b4e02e81da7b6/9.png" alt="TimeSeriesCollapse operator reshapes five columnar rows into two Prometheus time series per output page" /><p>The HTTP layer can now serialize those blocks directly, avoiding the maps, lists, boxed objects, and associated allocations required by the earlier implementation.</p><h2><strong>Testing PromQL compatibility against 2,000 real queries</strong></h2><p>Prometheus <a href="https://github.com/prometheus/compliance">compliance tests</a> were our starting point.</p><p>Even though they gave us a strong baseline, they didn’t tell us how frequently individual PromQL features appear in real workloads. To complement that baseline, we built a second test corpus from over 2,000 PromQL queries collected from public repositories. </p><p>We then classified those queries by the language features and expression patterns they exercise:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf008821c47e46040/6aa394b68406d90c5bcaa1e2/10.png" alt="PromQL feature use across 2,000 real queries: aggregations 59.67%, selectors 57.55%, rate functions 45.21%" /><p>For each compatible query shape, we run the same query against Elasticsearch and Prometheus and compare the results. </p><p>In addition to that, we actively rely on <a href="https://en.wikipedia.org/wiki/Fuzzing">fuzz testing</a>, which catches issues that unit tests alone are unlikely to expose, including differences in timestamp alignment, label retention, aggregation behavior, range-vector evaluation, and response encoding.</p><h2><strong>Which PromQL functions and APIs are supported in 9.5</strong></h2><p>Since 9.4 (technical preview), PromQL support in Elasticsearch has expanded substantially. In Elasticsearch 9.5, both the<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/promql"> <code>PROMQL</code></a> command in ES|QL and the<a href="https://www.elastic.co/docs/reference/query-languages/promql/promql-http-api"> Prometheus HTTP APIs</a> are generally available (GA), with more than 80% of the PromQL workflows in our real-world corpus now running without modification:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6706c16409dccfd7/6aa394d327a5312acfdcbf41/1.png" alt="PromQL compatibility in Elasticsearch rising from zero to 80% between 9.4 Tech Preview and 9.5 GA" /><p>The main additions since technical preview are:</p><p><strong>Feature</strong></p><p><strong>Example</strong></p><p><strong>Status</strong></p><p>Prometheus remote write ingest</p><p><code>POST /_prometheus/api/v1/write</code></p><p>GA in 9.5</p><p>Range queries</p><p><code>/api/v1/query_range</code></p><p>GA in 9.5</p><p>Instant queries</p><p><code>/api/v1/query</code></p><p>GA in 9.5</p><p>Metric metadata and build info</p><p><code>/api/v1/metadata</code>, <code>/api/v1/status/buildinfo</code></p><p>GA in 9.5</p><p>Native histogram functions</p><p><code>histogram_quantile</code>, <code>histogram_count</code>, <code>histogram_sum</code></p><p>GA in 9.5</p><p>Per-selector offset modifiers</p><p><code>[5m] offset 1h</code></p><p>GA in 9.5</p><p>Top-level <code>or</code> operator</p><p><code>rate(a[5m])</code> or <code>rate(b[5m])</code></p><p>GA in 9.5, up to eight operands</p><h3><strong>Prometheus remote write ingest</strong></h3><p>Elasticsearch <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write">accepts</a> Prometheus remote write (v1) messages directly:</p><p></p><p></p><p>Snappy-compressed Protocol Buffer messages are decoded, and labels are mapped to TSDS dimensions. Metric names and values are written into the time-series index. The built-in template is dynamic, so users don’t have to predeclare every Prometheus label or metric.</p><h3><strong>Range and instant queries through the Prometheus HTTP API</strong></h3><p>Both range and instant query endpoints are <a href="https://www.elastic.co/search-labs/blog/elasticsearch-native-prometheus-api">available</a>:</p><p></p><p></p><p></p><p></p><p>Range queries return matrices evaluated over a time window, and instant queries return vectors evaluated at a single timestamp. These endpoints can be used by Kibana, Grafana, or Prometheus-compatible alerting tools, and custom dashboards.</p><h3><strong>Metric metadata and build info endpoints</strong></h3><p>Elasticsearch <a href="https://www.elastic.co/docs/reference/query-languages/promql/promql-http-api#promql-http-api-metadata">exposes</a> metadata about available metrics and a build-info endpoint:</p><p></p><p></p><p></p><p></p><p>The metadata endpoint returns metric types and help text, and the build-info endpoint returns the Prometheus-compatible server version. Grafana and other tools use these endpoints for feature detection and UI behavior.</p><h3><strong>Native histogram functions: histogram_quantile, count, and sum</strong></h3><p>Elasticsearch supports the <a href="https://www.elastic.co/docs/reference/query-languages/promql/functions/histogram">main PromQL operations</a> over native histograms:</p><p></p><p></p><p></p><p></p><p>Native histograms adapt their bucket layout to the data, providing useful precision across a wide value range without requiring users to configure every bucket boundary in advance. Classic histograms continue to work alongside native histograms.</p><h3><strong>Per-selector offset modifiers in PromQL</strong></h3><p>Offset modifiers shift a selector’s time window backward:</p><p></p><p></p><p>This returns the request rate from one hour earlier. Per-selector offsets are commonly used to compare current traffic, latency, or resource usage with an earlier baseline, such as the same period one week ago.</p><h3><strong>Top-level </strong><strong><code>or</code></strong><strong> operator in PromQL</strong></h3><p>Elasticsearch supports the top-level PromQL <code>or</code> operator:</p><p></p><p></p><p>In PromQL, <code>or</code> isn’t a Boolean operation. It performs a union between two sets of time series. Results from the left side are retained; a series from the right side is added only when its label set doesn’t match a series already returned by the left side. This is useful during migrations where the same logical metric may exist under an old and a new name.</p><p>The implementation follows Prometheus’s left-side precedence rules and preserves the <code>__name__</code> label. Top-level chains of up to eight operands are supported.</p><h2><strong>PromQL features not yet supported in Elasticsearch</strong></h2><p>GA doesn’t mean complete PromQL compatibility. Some less common and more complex parts of PromQL remain unsupported. These gaps now define the next phase of the work: </p><p><strong>Feature</strong></p><p><strong>Example</strong></p><p><strong>Status</strong></p><p>Advanced vector matching</p><p><code>on(instance) group_left</code></p><p>Planned</p><p>Sorting and ranking</p><p><code>topk</code>, <code>bottomk</code>, <code>limitk</code>, <code>sort</code>, <code>sort_desc</code></p><p>Planned</p><p>Label manipulation</p><p><code>label_replace</code>, <code>label_join</code></p><p>Planned</p><p>Absolute time modifier</p><p><code>@ 1710000000</code></p><p>Planned</p><p>Mixed-offset compound expressions</p><p><code>rate(...) - rate(... offset 1h)</code></p><p>Planned</p><p>Alerting and target endpoints</p><p><code>/api/v1/alerts</code>, <code>/api/v1/targets</code></p><p>Out of scope</p><h3><strong>Advanced </strong><a href="https://prometheus.io/docs/prometheus/latest/querying/operators/#group-modifiers"><strong>vector matching</strong></a><strong> with </strong><strong><code>on()</code></strong><strong> and </strong><strong><code>group_left</code></strong></h3><p>Some binary operations that require Prometheus vector matching aren’t yet part of GA.</p><p>For example, this query divides per-instance request rates by a per-instance capacity metric:</p><p></p><p></p><p>The <code>on(instance)</code> clause specifies which labels identify matching series. <code>group_left</code> permits many request-rate series to match a single per-instance capacity series, while retaining the labels from the higher-cardinality left-hand side.</p><p>These expressions are common when joining a detailed metric with metadata or a lower-cardinality capacity metric. Basic binary expressions are supported where applicable, while the remaining vector-matching forms are planned work.</p><h3><strong>Sorting and ranking: </strong><strong><code>topk</code></strong><strong>, </strong><strong><code>bottomk</code></strong><strong>, and </strong><strong><code>sort</code></strong></h3><p>Prometheus sorting and ranking functions are also not yet part of GA:</p><p></p><p></p><p>This returns the 10 services with the highest request rate. Similar queries are widely used in “top offenders” dashboards for traffic, latency, errors, and resource consumption.</p><p>The remaining functions include:</p><p></p><p></p><p></p><p></p><p></p><p></p><h3><strong>Label manipulation with label_replace and label_join</strong></h3><p>PromQL can construct or rewrite labels during query evaluation. These functions are particularly useful when dashboard variables, naming conventions, or label schemas don’t match exactly:</p><p></p><p></p><p>This creates an <code>environment</code> label from the <code>cluster</code> label.</p><p>Another common example combines existing labels into a display-oriented label:</p><p></p><p></p><p>This produces a <code>target</code> label, such as <code>payments/api-7f6d9</code>. <code>label_replace(...)</code> and <code>label_join(...)</code> aren’t yet included in GA.</p><h3><strong>Advanced time modifiers: The </strong><strong><code>@</code></strong><strong> modifier and mixed offsets</strong></h3><p>Several advanced time modifiers and expression forms remain outside the GA scope.</p><p>For example, an absolute <code>@</code> modifier evaluates a selector at a fixed Unix timestamp rather than at the query’s normal evaluation time:</p><p></p><p></p><p>This is useful for comparisons against a fixed historical point.</p><p>PromQL also permits expressions in which the two sides use different offsets:</p><p></p><p></p><p>This compares current traffic with traffic one hour earlier. Per-selector <code>offset</code> is available in GA, but not every combination of offsets and compound expressions is part of GA yet.</p><h3><strong>Prometheus API endpoints not yet implemented</strong></h3><p>In addition, the Prometheus HTTP API surface isn’t yet fully complete. Notably:</p><p>Alerting metadata through:</p><p></p><p></p><p>used by tools that inspect active alert state.</p><p>Target discovery through:</p><p></p><p></p><p>used to inspect scrape targets, health, and labels.</p><p>These endpoints concern Prometheus server and scrape-target state rather than querying metrics stored in Elasticsearch.</p><p>For the full list of limitations, see the <a href="https://www.elastic.co/docs/reference/query-languages/promql/promql-limitations#promql-limitations-form-post">PromQL limitations</a> page. </p><h2><strong>Try PromQL in Elasticsearch 9.5</strong></h2><p>To query Prometheus metrics in Elasticsearch 9.5 or Serverless, see the <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/promql">PromQL documentation</a> and the <a href="https://www.elastic.co/docs/reference/query-languages/promql/promql-http-api">Prometheus HTTP API reference</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/promql-elasticsearch-compute-engine</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/promql-elasticsearch-compute-engine</guid>
    <category><![CDATA[Query Languages]]></category>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Inside Elastic]]></category>
    <dc:creator><![CDATA[Sergey Sidorov,Felix Barnsteiner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf690e82ea51bbeec/6aa37da41ade6445cc42eded/unnamed.png" length="0" type="image/png"/>
    <pubDate>Fri, 11 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>