Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.esql.optimizer;

import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;

import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.MapperServiceTestCase;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.enrich.EnrichPolicy;
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.analysis.Analyzer;
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
import org.elasticsearch.xpack.esql.analysis.EnrichResolution;
import org.elasticsearch.xpack.esql.analysis.Verifier;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.type.EsField;
import org.elasticsearch.xpack.esql.enrich.ResolvedEnrichPolicy;
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
import org.elasticsearch.xpack.esql.index.EsIndex;
import org.elasticsearch.xpack.esql.index.IndexResolution;
import org.elasticsearch.xpack.esql.plan.logical.Enrich;
import org.elasticsearch.xpack.esql.planner.FilterTests;
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
import org.elasticsearch.xpack.esql.session.Configuration;
import org.elasticsearch.xpack.esql.telemetry.Metrics;
import org.junit.Before;

import java.util.List;
import java.util.Map;

import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.configuration;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyInferenceResolution;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.defaultLookupResolution;

public class AbstractLocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
protected final Configuration config;
protected TestPlannerOptimizer plannerOptimizer;
protected TestPlannerOptimizer plannerOptimizerDateDateNanosUnionTypes;
protected TestPlannerOptimizer plannerOptimizerTimeSeries;
private Analyzer timeSeriesAnalyzer;

private static final String PARAM_FORMATTING = "%1$s";

@ParametersFactory(argumentFormatting = PARAM_FORMATTING)
public static List<Object[]> readScriptSpec() {
return settings().stream().map(t -> {
var settings = Settings.builder().loadFromMap(t.v2()).build();
return new Object[] { t.v1(), configuration(new QueryPragmas(settings)) };
}).toList();
}

private static List<Tuple<String, Map<String, Object>>> settings() {
return List.of(new Tuple<>("default", Map.of()));
}

protected static QueryBuilder wrapWithSingleQuery(String query, QueryBuilder inner, String fieldName, Source source) {
return FilterTests.singleValueQuery(query, inner, fieldName, source);
}

public AbstractLocalPhysicalPlanOptimizerTests(String name, Configuration config) {
this.config = config;
}

@Before
public void init() {
EnrichResolution enrichResolution = new EnrichResolution();
enrichResolution.addResolvedPolicy(
"foo",
Enrich.Mode.ANY,
new ResolvedEnrichPolicy(
"fld",
EnrichPolicy.MATCH_TYPE,
List.of("a", "b"),
Map.of("", "idx"),
Map.ofEntries(
Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)),
Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE))
)
)
);
plannerOptimizer = new TestPlannerOptimizer(config, makeAnalyzer("mapping-basic.json", enrichResolution));
var timeSeriesMapping = loadMapping("k8s-mappings.json");
var timeSeriesIndex = IndexResolution.valid(new EsIndex("k8s", timeSeriesMapping, Map.of("k8s", IndexMode.TIME_SERIES)));
timeSeriesAnalyzer = new Analyzer(
new AnalyzerContext(
EsqlTestUtils.TEST_CFG,
new EsqlFunctionRegistry(),
timeSeriesIndex,
enrichResolution,
emptyInferenceResolution()
),
TEST_VERIFIER
);
plannerOptimizerTimeSeries = new TestPlannerOptimizer(config, timeSeriesAnalyzer);
}

private Analyzer makeAnalyzer(String mappingFileName, EnrichResolution enrichResolution) {
var mapping = loadMapping(mappingFileName);
EsIndex test = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD));
IndexResolution getIndexResult = IndexResolution.valid(test);

return new Analyzer(
new AnalyzerContext(
config,
new EsqlFunctionRegistry(),
getIndexResult,
defaultLookupResolution(),
enrichResolution,
emptyInferenceResolution()
),
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
);
}

protected Analyzer makeAnalyzer(String mappingFileName) {
return makeAnalyzer(mappingFileName, new EnrichResolution());
}

protected Analyzer makeAnalyzer(IndexResolution indexResolution) {
return new Analyzer(
new AnalyzerContext(config, new EsqlFunctionRegistry(), indexResolution, new EnrichResolution(), emptyInferenceResolution()),
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
);
}

/**
* This exempts the warning about adding the automatic limit from the warnings check in
* {@link ESTestCase#ensureNoWarnings()}
*/
@Override
protected List<String> filteredWarnings() {
return withDefaultLimitWarning(super.filteredWarnings());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* Tests for the {@link org.elasticsearch.xpack.esql.optimizer.rules.logical.local.IgnoreNullMetrics} planner rule, to
* verify that the filters are being pushed to Lucene.
*/
public class IgnoreNullMetricsPhysicalPlannerTests extends LocalPhysicalPlanOptimizerTests {
public class IgnoreNullMetricsPhysicalPlannerTests extends AbstractLocalPhysicalPlanOptimizerTests {
public IgnoreNullMetricsPhysicalPlannerTests(String name, Configuration config) {
super(name, config);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@

package org.elasticsearch.xpack.esql.optimizer;

import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;

import org.apache.lucene.search.IndexSearcher;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.MapperServiceTestCase;
import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
Expand All @@ -26,19 +21,14 @@
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.search.vectors.KnnVectorQueryBuilder;
import org.elasticsearch.search.vectors.RescoreVectorBuilder;
import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.xpack.core.enrich.EnrichPolicy;
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.EsqlTestUtils.TestSearchStats;
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
import org.elasticsearch.xpack.esql.analysis.Analyzer;
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
import org.elasticsearch.xpack.esql.analysis.EnrichResolution;
import org.elasticsearch.xpack.esql.analysis.Verifier;
import org.elasticsearch.xpack.esql.core.expression.Alias;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
import org.elasticsearch.xpack.esql.core.expression.Expression;
Expand All @@ -50,12 +40,9 @@
import org.elasticsearch.xpack.esql.core.expression.ReferenceAttribute;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.core.type.EsField;
import org.elasticsearch.xpack.esql.core.type.MultiTypeEsField;
import org.elasticsearch.xpack.esql.core.util.Holder;
import org.elasticsearch.xpack.esql.enrich.ResolvedEnrichPolicy;
import org.elasticsearch.xpack.esql.expression.Order;
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
import org.elasticsearch.xpack.esql.expression.function.UnsupportedAttribute;
import org.elasticsearch.xpack.esql.expression.function.aggregate.Count;
import org.elasticsearch.xpack.esql.expression.function.aggregate.Min;
Expand All @@ -69,11 +56,9 @@
import org.elasticsearch.xpack.esql.expression.predicate.logical.Or;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual;
import org.elasticsearch.xpack.esql.index.EsIndex;
import org.elasticsearch.xpack.esql.index.IndexResolution;
import org.elasticsearch.xpack.esql.optimizer.rules.logical.ExtractAggregateCommonFilter;
import org.elasticsearch.xpack.esql.parser.ParsingException;
import org.elasticsearch.xpack.esql.plan.logical.Enrich;
import org.elasticsearch.xpack.esql.plan.logical.EsRelation;
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.esql.plan.physical.AggregateExec;
Expand All @@ -95,19 +80,15 @@
import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan;
import org.elasticsearch.xpack.esql.plan.physical.ProjectExec;
import org.elasticsearch.xpack.esql.plan.physical.TopNExec;
import org.elasticsearch.xpack.esql.planner.FilterTests;
import org.elasticsearch.xpack.esql.plugin.EsqlFlags;
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery;
import org.elasticsearch.xpack.esql.rule.Rule;
import org.elasticsearch.xpack.esql.rule.RuleExecutor;
import org.elasticsearch.xpack.esql.session.Configuration;
import org.elasticsearch.xpack.esql.stats.SearchContextStats;
import org.elasticsearch.xpack.esql.stats.SearchStats;
import org.elasticsearch.xpack.esql.telemetry.Metrics;
import org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter;
import org.elasticsearch.xpack.kql.query.KqlQueryBuilder;
import org.junit.Before;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -120,7 +101,6 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import static java.util.Arrays.asList;
import static org.elasticsearch.compute.aggregation.AggregatorMode.FINAL;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
Expand All @@ -129,14 +109,8 @@
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_PLANNER_SETTINGS;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.as;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.configuration;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyInferenceResolution;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.unboundLogicalOptimizerContext;
import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.defaultLookupResolution;
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.indexWithDateDateNanosUnionType;
import static org.elasticsearch.xpack.esql.core.querydsl.query.Query.unscore;
import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS;
Expand All @@ -153,7 +127,7 @@
import static org.hamcrest.Matchers.nullValue;

//@TestLogging(value = "org.elasticsearch.xpack.esql:TRACE,org.elasticsearch.compute:TRACE", reason = "debug")
public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
public class LocalPhysicalPlanOptimizerTests extends AbstractLocalPhysicalPlanOptimizerTests {

public static final List<DataType> UNNECESSARY_CASTING_DATA_TYPES = List.of(
DataType.BOOLEAN,
Expand All @@ -163,7 +137,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
DataType.KEYWORD,
DataType.TEXT
);
private static final String PARAM_FORMATTING = "%1$s";

/**
* Estimated size of a keyword field in bytes.
Expand All @@ -172,11 +145,6 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
public static final String MATCH_OPERATOR_QUERY = "from test | where %s:%s";
public static final String MATCH_FUNCTION_QUERY = "from test | where match(%s, %s)";

protected TestPlannerOptimizer plannerOptimizer;
private TestPlannerOptimizer plannerOptimizerDateDateNanosUnionTypes;
private Analyzer timeSeriesAnalyzer;
protected TestPlannerOptimizer plannerOptimizerTimeSeries;
private final Configuration config;
private final SearchStats IS_SV_STATS = new TestSearchStats() {
@Override
public boolean isSingleValue(FieldAttribute.FieldName field) {
Expand All @@ -196,82 +164,8 @@ public String constantValue(FieldAttribute.FieldName name) {
}
};

@ParametersFactory(argumentFormatting = PARAM_FORMATTING)
public static List<Object[]> readScriptSpec() {
return settings().stream().map(t -> {
var settings = Settings.builder().loadFromMap(t.v2()).build();
return new Object[] { t.v1(), configuration(new QueryPragmas(settings)) };
}).toList();
}

private static List<Tuple<String, Map<String, Object>>> settings() {
return asList(new Tuple<>("default", Map.of()));
}

public LocalPhysicalPlanOptimizerTests(String name, Configuration config) {
this.config = config;
}

@Before
public void init() {
EnrichResolution enrichResolution = new EnrichResolution();
enrichResolution.addResolvedPolicy(
"foo",
Enrich.Mode.ANY,
new ResolvedEnrichPolicy(
"fld",
EnrichPolicy.MATCH_TYPE,
List.of("a", "b"),
Map.of("", "idx"),
Map.ofEntries(
Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)),
Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE))
)
)
);
plannerOptimizer = new TestPlannerOptimizer(config, makeAnalyzer("mapping-basic.json", enrichResolution));
var timeSeriesMapping = loadMapping("k8s-mappings.json");
var timeSeriesIndex = IndexResolution.valid(new EsIndex("k8s", timeSeriesMapping, Map.of("k8s", IndexMode.TIME_SERIES)));
timeSeriesAnalyzer = new Analyzer(
new AnalyzerContext(
EsqlTestUtils.TEST_CFG,
new EsqlFunctionRegistry(),
timeSeriesIndex,
enrichResolution,
emptyInferenceResolution()
),
TEST_VERIFIER
);
plannerOptimizerTimeSeries = new TestPlannerOptimizer(config, timeSeriesAnalyzer);
}

private Analyzer makeAnalyzer(String mappingFileName, EnrichResolution enrichResolution) {
var mapping = loadMapping(mappingFileName);
EsIndex test = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD));
IndexResolution getIndexResult = IndexResolution.valid(test);

return new Analyzer(
new AnalyzerContext(
config,
new EsqlFunctionRegistry(),
getIndexResult,
defaultLookupResolution(),
enrichResolution,
emptyInferenceResolution()
),
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
);
}

protected Analyzer makeAnalyzer(String mappingFileName) {
return makeAnalyzer(mappingFileName, new EnrichResolution());
}

private Analyzer makeAnalyzer(IndexResolution indexResolution) {
return new Analyzer(
new AnalyzerContext(config, new EsqlFunctionRegistry(), indexResolution, new EnrichResolution(), emptyInferenceResolution()),
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
);
super(name, config);
}

/**
Expand Down Expand Up @@ -2560,10 +2454,6 @@ private boolean isMultiTypeEsField(Expression e) {
return e instanceof FieldAttribute fa && fa.field() instanceof MultiTypeEsField;
}

protected static QueryBuilder wrapWithSingleQuery(String query, QueryBuilder inner, String fieldName, Source source) {
return FilterTests.singleValueQuery(query, inner, fieldName, source);
}

private Stat queryStatsFor(PhysicalPlan plan) {
var limit = as(plan, LimitExec.class);
var agg = as(limit.child(), AggregateExec.class);
Expand All @@ -2575,11 +2465,6 @@ private Stat queryStatsFor(PhysicalPlan plan) {
return stat;
}

@Override
protected List<String> filteredWarnings() {
return withDefaultLimitWarning(super.filteredWarnings());
}

private static KqlQueryBuilder kqlQueryBuilder(String query) {
return new KqlQueryBuilder(query);
}
Expand Down
Loading