Skip to content

Commit a9da1a9

Browse files
committed
Address review suggestions
1 parent f9e182b commit a9da1a9

File tree

1 file changed

+61
-5
lines changed

1 file changed

+61
-5
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/promql/TranslatePromqlToEsqlPlan.java

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,67 @@ private TranslationResult translateNode(LogicalPlan node, LogicalPlan currentPla
259259
}
260260

261261
/**
262-
* Translates an AcrossSeriesAggregate (sum, avg, max, min, count, etc.).
263-
* Creates TimeSeriesAggregate if child has no aggregation or Aggregate if child already aggregated.
264-
* NOTE: Only AcrossSeriesAggregate nodes create plan-level aggregation nodes.
265-
* WithinSeriesAggregate and other PromqlFunctionCall nodes produce
266-
* expressions embedded inside the aggregation, but do not create Aggregate plan nodes themselves.
262+
* Translates {@code AcrossSeriesAggregate} to an ESQL {@code Aggregate}.
263+
* <p>
264+
* PromQL aggregation shape is dynamic and can't be expressed in ESQL directly without enumerating the full label set.
265+
* We avoid that at plan time (for performance), so the translator carries aggregation shape as a triplet
266+
* {@code (G, O, X)} where G = grouping labels, O = output labels, X = excluded labels.
267+
* G is either a concrete set of label names or an opaque runtime representation {@code T...} (backed by _timeseries).
268+
* Labels in O but not in G are null-filled in the output.
269+
* <p>
270+
* <ul>
271+
* <li>WITHOUT(E): (G, O, X) -> (G\E, G\E, X u E)</li>
272+
* <li>BY(W): (G, O, X) -> (W\X, W, X)</li>
273+
* </ul>
274+
* <p>
275+
* Leaf state is {@code [G=T..., O=T..., X={}]} (all labels present but unknown by name).
276+
* <p>
277+
* Examples:
278+
*
279+
* <pre>
280+
* sum without(pod) (
281+
* avg without(region) (
282+
* cpu_util
283+
* ) [G=T...\{region}, O=T...\{region}, X={region}]
284+
* ) [G=T...\{region,pod}, O=T...\{region,pod}, X={region,pod}]
285+
* </pre>
286+
*
287+
* <pre>
288+
* sum by(cluster,region) (
289+
* avg without(region) (
290+
* cpu_util
291+
* ) [G=T...\{region}, O=T...\{region}, X={region}]
292+
* ) [G={cluster}, O={cluster,region}, X={region}] // region is null-filled
293+
* </pre>
294+
*
295+
* <pre>
296+
* max without(cluster) (
297+
* sum by(cluster,region) (
298+
* avg without(region) (
299+
* cpu_util
300+
* ) [G=T...\{region}, O=T...\{region}, X={region}]
301+
* ) [G={cluster}, O={cluster,region}, X={region}] // region is null-filled
302+
* ) [G={}, O={}, X={region,cluster}]
303+
* </pre>
304+
*
305+
* <pre>
306+
* sum without(pod) (
307+
* avg by(cluster,pod) (
308+
* cpu_util
309+
* ) [G={cluster,pod}, O={cluster,pod}, X={}]
310+
* ) [G={cluster}, O={cluster}, X={pod}]
311+
* </pre>
312+
* <pre>
313+
* sum by(cluster) (
314+
* avg by(cluster,pod) (
315+
* cpu_util
316+
* ) [G={cluster,pod}, O={cluster,pod}, X={}]
317+
* ) [G={cluster}, O={cluster}, X={}]
318+
* </pre>
319+
*
320+
* <p>Only {@code AcrossSeriesAggregate} creates plan-level aggregation nodes.
321+
* {@code WithinSeriesAggregate} and other {@code PromqlFunctionCall} nodes are
322+
* lowered to expressions and folded into the aggregate.
267323
*/
268324
private TranslationResult translateAcrossSeriesAggregate(AcrossSeriesAggregate agg, LogicalPlan currentPlan, TranslationContext ctx) {
269325
List<Attribute> groupingLabels = normalizeLabels(agg.groupings());

0 commit comments

Comments
 (0)