From 74294fcc8cc25e3945031a5be102eb8e2c69dcf1 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 13 Nov 2025 16:17:57 +0100 Subject: [PATCH] Translate start/end to filter on @timestamp --- .../TranslatePromqlToTimeSeriesAggregate.java | 26 ++++++++++++++++--- .../PromqlLogicalPlanOptimizerTests.java | 24 ++--------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/promql/TranslatePromqlToTimeSeriesAggregate.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/promql/TranslatePromqlToTimeSeriesAggregate.java index ba5c45b557971..9ef59b194c693 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/promql/TranslatePromqlToTimeSeriesAggregate.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/promql/TranslatePromqlToTimeSeriesAggregate.java @@ -25,7 +25,9 @@ import org.elasticsearch.xpack.esql.expression.predicate.logical.And; import org.elasticsearch.xpack.esql.expression.predicate.nulls.IsNotNull; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals; +import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In; +import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThanOrEqual; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.NotEquals; import org.elasticsearch.xpack.esql.expression.promql.function.PromqlFunctionRegistry; import org.elasticsearch.xpack.esql.optimizer.rules.logical.OptimizerRules; @@ -93,7 +95,7 @@ protected LogicalPlan rule(PromqlCommand promqlCommand) { LogicalPlan promqlPlan = promqlCommand.promqlPlan(); // first replace the Placeholder relation with the child plan - promqlPlan = promqlPlan.transformUp(PlaceholderRelation.class, pr -> promqlCommand.child()); + promqlPlan = promqlPlan.transformUp(PlaceholderRelation.class, pr -> withTimestampFilter(promqlCommand, promqlCommand.child())); // Translate based on plan type return translate(promqlCommand, promqlPlan); @@ -101,8 +103,26 @@ protected LogicalPlan rule(PromqlCommand promqlCommand) { private LogicalPlan translate(PromqlCommand promqlCommand, LogicalPlan promqlPlan) { // convert the plan bottom-up - MapResult result = map(promqlCommand, promqlPlan); - return result.plan(); + promqlPlan = map(promqlCommand, promqlPlan).plan(); + + return promqlPlan; + } + + private static LogicalPlan withTimestampFilter(PromqlCommand promqlCommand, LogicalPlan plan) { + if (promqlCommand.start().value() != null && promqlCommand.end().value() != null) { + Source promqlSource = promqlCommand.source(); + Expression timestamp = promqlCommand.timestamp(); + plan = new Filter( + promqlSource, + plan, + new And( + promqlSource, + new GreaterThanOrEqual(promqlSource, timestamp, promqlCommand.start()), + new LessThanOrEqual(promqlSource, timestamp, promqlCommand.end()) + ) + ); + } + return plan; } private record MapResult(LogicalPlan plan, Map extras) {} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/promql/PromqlLogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/promql/PromqlLogicalPlanOptimizerTests.java index a8956a324e2fd..fd7f68ffae223 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/promql/PromqlLogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/promql/PromqlLogicalPlanOptimizerTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.xpack.esql.core.expression.predicate.regex.RegexMatch; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; -import org.elasticsearch.xpack.esql.expression.function.grouping.Bucket; import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.NotEquals; @@ -30,7 +29,6 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.List; import java.util.Map; import static java.util.Collections.emptyMap; @@ -39,13 +37,10 @@ import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -import static org.junit.Assume.assumeTrue; // @TestLogging(value = "org.elasticsearch.xpack.esql:TRACE", reason = "debug tests") public class PromqlLogicalPlanOptimizerTests extends AbstractLogicalPlanOptimizerTests { - private static final String PARAM_FORMATTING = "%1$s"; - private static Analyzer tsAnalyzer; @BeforeClass @@ -80,7 +75,6 @@ public void testExplainPromql() { ) """); - logger.info(plan); } public void testExplainPromqlSimple() { @@ -93,7 +87,6 @@ public void testExplainPromqlSimple() { | STATS AVG(AVG_OVER_TIME(network.bytes_in)) BY TBUCKET(1h) """); - logger.info(plan); } public void testAvgAvgOverTimeOutput() { @@ -106,7 +99,6 @@ public void testAvgAvgOverTimeOutput() { | LIMIT 1000 """); - logger.info(plan); } public void testTSAvgAvgOverTimeOutput() { @@ -118,7 +110,6 @@ public void testTSAvgAvgOverTimeOutput() { | LIMIT 1000 """); - logger.info(plan); } public void testTSAvgWithoutByDimension() { @@ -129,8 +120,6 @@ public void testTSAvgWithoutByDimension() { | STATS avg(avg_over_time(network.bytes_in)) BY TBUCKET(1h) | LIMIT 1000 """); - - logger.info(plan); } public void testPromqlAvgWithoutByDimension() { @@ -144,7 +133,6 @@ public void testPromqlAvgWithoutByDimension() { | LIMIT 1000 """); - logger.info(plan); } public void testRangeSelector() { @@ -156,7 +144,6 @@ public void testRangeSelector() { | promql step 1h ( max by (pod) (avg_over_time(network.bytes_in[1h])) ) """); - logger.info(plan); } @AwaitsFix(bugUrl = "Invalid call to dataType on an unresolved object ?RATE_$1") @@ -172,7 +159,6 @@ avg by (pod) (rate(network.bytes_in[1h])) """; var plan = planPromql(testQuery); - logger.info(plan); } public void testStartEndStep() { @@ -184,8 +170,6 @@ public void testStartEndStep() { """; var plan = planPromql(testQuery); - List collect = plan.collect(Bucket.class::isInstance); - logger.info(plan); } public void testLabelSelector() { @@ -205,7 +189,6 @@ max by (pod) (avg_over_time(network.bytes_in{pod=~"host-0|host-1|host-2"}[5m])) assertThat(filters, hasSize(1)); var filter = (Filter) filters.getFirst(); assertThat(filter.condition().anyMatch(In.class::isInstance), equalTo(true)); - logger.info(plan); } public void testLabelSelectorPrefix() { @@ -226,7 +209,6 @@ avg by (pod) (avg_over_time(network.bytes_in{pod=~"host-.*"}[5m])) var filter = (Filter) filters.getFirst(); assertThat(filter.condition().anyMatch(StartsWith.class::isInstance), equalTo(true)); assertThat(filter.condition().anyMatch(NotEquals.class::isInstance), equalTo(false)); - logger.info(plan); } public void testLabelSelectorProperPrefix() { @@ -276,7 +258,6 @@ sum by (host.name, mountpoint) (last_over_time(system.filesystem.usage{state=~"u """; var plan = planPromql(testQuery); - logger.info(plan); } @AwaitsFix(bugUrl = "only aggregations across timeseries are supported at this time (found [foo or bar])") @@ -297,7 +278,6 @@ public void testGrammar() { """; var plan = planPromql(testQuery); - logger.info(plan); } // public void testPromqlArithmetricOperators() { @@ -325,9 +305,9 @@ protected LogicalPlan planPromql(String query) { query = query.replace("$now-1h", '"' + Instant.now().minus(1, ChronoUnit.HOURS).toString() + '"'); query = query.replace("$now", '"' + Instant.now().toString() + '"'); var analyzed = tsAnalyzer.analyze(parser.createStatement(query)); - logger.info(analyzed); + logger.info("analyzed plan:\n{}", analyzed); var optimized = logicalOptimizer.optimize(analyzed); - logger.info(optimized); + logger.info("optimized plan:\n{}", optimized); return optimized; } }