Skip to content

Commit 202f444

Browse files
not-napoleonelasticsearchmachine
andauthored
Esql - Add base class for Physical Plan Optimizer Tests (elastic#135104)
This extracts an abstract base class from LocalPhysicalPlanOptimizerTests to hold the initialization of the planners and other utility methods. This way, classes like ReplaceRoundToWithQueryAndTagsTests can extend from the base class instead of from LocalPhysicalPlanOptimizerTests. Extending from the actual test class results in re-running the tests in that class for each subclass, with no changes. This PR should result in removing about 160 redundant test runs, and make it a little easier to write physical planner tests in separate files. --------- Co-authored-by: elasticsearchmachine <[email protected]>
1 parent 4c1b987 commit 202f444

File tree

4 files changed

+154
-120
lines changed

4 files changed

+154
-120
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.optimizer;
9+
10+
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
11+
12+
import org.elasticsearch.common.settings.Settings;
13+
import org.elasticsearch.core.Tuple;
14+
import org.elasticsearch.index.IndexMode;
15+
import org.elasticsearch.index.mapper.MapperServiceTestCase;
16+
import org.elasticsearch.index.query.QueryBuilder;
17+
import org.elasticsearch.license.XPackLicenseState;
18+
import org.elasticsearch.test.ESTestCase;
19+
import org.elasticsearch.xpack.core.enrich.EnrichPolicy;
20+
import org.elasticsearch.xpack.esql.EsqlTestUtils;
21+
import org.elasticsearch.xpack.esql.analysis.Analyzer;
22+
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
23+
import org.elasticsearch.xpack.esql.analysis.EnrichResolution;
24+
import org.elasticsearch.xpack.esql.analysis.Verifier;
25+
import org.elasticsearch.xpack.esql.core.tree.Source;
26+
import org.elasticsearch.xpack.esql.core.type.DataType;
27+
import org.elasticsearch.xpack.esql.core.type.EsField;
28+
import org.elasticsearch.xpack.esql.enrich.ResolvedEnrichPolicy;
29+
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
30+
import org.elasticsearch.xpack.esql.index.EsIndex;
31+
import org.elasticsearch.xpack.esql.index.IndexResolution;
32+
import org.elasticsearch.xpack.esql.plan.logical.Enrich;
33+
import org.elasticsearch.xpack.esql.planner.FilterTests;
34+
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
35+
import org.elasticsearch.xpack.esql.session.Configuration;
36+
import org.elasticsearch.xpack.esql.telemetry.Metrics;
37+
import org.junit.Before;
38+
39+
import java.util.List;
40+
import java.util.Map;
41+
42+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER;
43+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.configuration;
44+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyInferenceResolution;
45+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping;
46+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
47+
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.defaultLookupResolution;
48+
49+
public class AbstractLocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
50+
protected final Configuration config;
51+
protected TestPlannerOptimizer plannerOptimizer;
52+
protected TestPlannerOptimizer plannerOptimizerDateDateNanosUnionTypes;
53+
protected TestPlannerOptimizer plannerOptimizerTimeSeries;
54+
private Analyzer timeSeriesAnalyzer;
55+
56+
private static final String PARAM_FORMATTING = "%1$s";
57+
58+
@ParametersFactory(argumentFormatting = PARAM_FORMATTING)
59+
public static List<Object[]> readScriptSpec() {
60+
return settings().stream().map(t -> {
61+
var settings = Settings.builder().loadFromMap(t.v2()).build();
62+
return new Object[] { t.v1(), configuration(new QueryPragmas(settings)) };
63+
}).toList();
64+
}
65+
66+
private static List<Tuple<String, Map<String, Object>>> settings() {
67+
return List.of(new Tuple<>("default", Map.of()));
68+
}
69+
70+
protected static QueryBuilder wrapWithSingleQuery(String query, QueryBuilder inner, String fieldName, Source source) {
71+
return FilterTests.singleValueQuery(query, inner, fieldName, source);
72+
}
73+
74+
public AbstractLocalPhysicalPlanOptimizerTests(String name, Configuration config) {
75+
this.config = config;
76+
}
77+
78+
@Before
79+
public void init() {
80+
EnrichResolution enrichResolution = new EnrichResolution();
81+
enrichResolution.addResolvedPolicy(
82+
"foo",
83+
Enrich.Mode.ANY,
84+
new ResolvedEnrichPolicy(
85+
"fld",
86+
EnrichPolicy.MATCH_TYPE,
87+
List.of("a", "b"),
88+
Map.of("", "idx"),
89+
Map.ofEntries(
90+
Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)),
91+
Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE))
92+
)
93+
)
94+
);
95+
plannerOptimizer = new TestPlannerOptimizer(config, makeAnalyzer("mapping-basic.json", enrichResolution));
96+
var timeSeriesMapping = loadMapping("k8s-mappings.json");
97+
var timeSeriesIndex = IndexResolution.valid(new EsIndex("k8s", timeSeriesMapping, Map.of("k8s", IndexMode.TIME_SERIES)));
98+
timeSeriesAnalyzer = new Analyzer(
99+
new AnalyzerContext(
100+
EsqlTestUtils.TEST_CFG,
101+
new EsqlFunctionRegistry(),
102+
timeSeriesIndex,
103+
enrichResolution,
104+
emptyInferenceResolution()
105+
),
106+
TEST_VERIFIER
107+
);
108+
plannerOptimizerTimeSeries = new TestPlannerOptimizer(config, timeSeriesAnalyzer);
109+
}
110+
111+
private Analyzer makeAnalyzer(String mappingFileName, EnrichResolution enrichResolution) {
112+
var mapping = loadMapping(mappingFileName);
113+
EsIndex test = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD));
114+
IndexResolution getIndexResult = IndexResolution.valid(test);
115+
116+
return new Analyzer(
117+
new AnalyzerContext(
118+
config,
119+
new EsqlFunctionRegistry(),
120+
getIndexResult,
121+
defaultLookupResolution(),
122+
enrichResolution,
123+
emptyInferenceResolution()
124+
),
125+
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
126+
);
127+
}
128+
129+
protected Analyzer makeAnalyzer(String mappingFileName) {
130+
return makeAnalyzer(mappingFileName, new EnrichResolution());
131+
}
132+
133+
protected Analyzer makeAnalyzer(IndexResolution indexResolution) {
134+
return new Analyzer(
135+
new AnalyzerContext(config, new EsqlFunctionRegistry(), indexResolution, new EnrichResolution(), emptyInferenceResolution()),
136+
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
137+
);
138+
}
139+
140+
/**
141+
* This exempts the warning about adding the automatic limit from the warnings check in
142+
* {@link ESTestCase#ensureNoWarnings()}
143+
*/
144+
@Override
145+
protected List<String> filteredWarnings() {
146+
return withDefaultLimitWarning(super.filteredWarnings());
147+
}
148+
149+
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/IgnoreNullMetricsPhysicalPlannerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* Tests for the {@link org.elasticsearch.xpack.esql.optimizer.rules.logical.local.IgnoreNullMetrics} planner rule, to
2323
* verify that the filters are being pushed to Lucene.
2424
*/
25-
public class IgnoreNullMetricsPhysicalPlannerTests extends LocalPhysicalPlanOptimizerTests {
25+
public class IgnoreNullMetricsPhysicalPlannerTests extends AbstractLocalPhysicalPlanOptimizerTests {
2626
public IgnoreNullMetricsPhysicalPlannerTests(String name, Configuration config) {
2727
super(name, config);
2828
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java

Lines changed: 2 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,11 @@
77

88
package org.elasticsearch.xpack.esql.optimizer;
99

10-
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
11-
1210
import org.apache.lucene.search.IndexSearcher;
1311
import org.elasticsearch.common.network.NetworkAddress;
14-
import org.elasticsearch.common.settings.Settings;
1512
import org.elasticsearch.common.unit.Fuzziness;
16-
import org.elasticsearch.core.Tuple;
1713
import org.elasticsearch.index.IndexMode;
1814
import org.elasticsearch.index.mapper.MapperService;
19-
import org.elasticsearch.index.mapper.MapperServiceTestCase;
2015
import org.elasticsearch.index.mapper.ParsedDocument;
2116
import org.elasticsearch.index.query.BoolQueryBuilder;
2217
import org.elasticsearch.index.query.MatchQueryBuilder;
@@ -26,19 +21,14 @@
2621
import org.elasticsearch.index.query.QueryStringQueryBuilder;
2722
import org.elasticsearch.index.query.RangeQueryBuilder;
2823
import org.elasticsearch.index.query.SearchExecutionContext;
29-
import org.elasticsearch.license.XPackLicenseState;
3024
import org.elasticsearch.search.vectors.KnnVectorQueryBuilder;
3125
import org.elasticsearch.search.vectors.RescoreVectorBuilder;
3226
import org.elasticsearch.test.VersionUtils;
33-
import org.elasticsearch.xpack.core.enrich.EnrichPolicy;
3427
import org.elasticsearch.xpack.esql.EsqlTestUtils;
3528
import org.elasticsearch.xpack.esql.EsqlTestUtils.TestSearchStats;
3629
import org.elasticsearch.xpack.esql.VerificationException;
3730
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
3831
import org.elasticsearch.xpack.esql.analysis.Analyzer;
39-
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
40-
import org.elasticsearch.xpack.esql.analysis.EnrichResolution;
41-
import org.elasticsearch.xpack.esql.analysis.Verifier;
4232
import org.elasticsearch.xpack.esql.core.expression.Alias;
4333
import org.elasticsearch.xpack.esql.core.expression.Attribute;
4434
import org.elasticsearch.xpack.esql.core.expression.Expression;
@@ -50,12 +40,9 @@
5040
import org.elasticsearch.xpack.esql.core.expression.ReferenceAttribute;
5141
import org.elasticsearch.xpack.esql.core.tree.Source;
5242
import org.elasticsearch.xpack.esql.core.type.DataType;
53-
import org.elasticsearch.xpack.esql.core.type.EsField;
5443
import org.elasticsearch.xpack.esql.core.type.MultiTypeEsField;
5544
import org.elasticsearch.xpack.esql.core.util.Holder;
56-
import org.elasticsearch.xpack.esql.enrich.ResolvedEnrichPolicy;
5745
import org.elasticsearch.xpack.esql.expression.Order;
58-
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
5946
import org.elasticsearch.xpack.esql.expression.function.UnsupportedAttribute;
6047
import org.elasticsearch.xpack.esql.expression.function.aggregate.Count;
6148
import org.elasticsearch.xpack.esql.expression.function.aggregate.Min;
@@ -69,11 +56,9 @@
6956
import org.elasticsearch.xpack.esql.expression.predicate.logical.Or;
7057
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan;
7158
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual;
72-
import org.elasticsearch.xpack.esql.index.EsIndex;
7359
import org.elasticsearch.xpack.esql.index.IndexResolution;
7460
import org.elasticsearch.xpack.esql.optimizer.rules.logical.ExtractAggregateCommonFilter;
7561
import org.elasticsearch.xpack.esql.parser.ParsingException;
76-
import org.elasticsearch.xpack.esql.plan.logical.Enrich;
7762
import org.elasticsearch.xpack.esql.plan.logical.EsRelation;
7863
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
7964
import org.elasticsearch.xpack.esql.plan.physical.AggregateExec;
@@ -95,19 +80,15 @@
9580
import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan;
9681
import org.elasticsearch.xpack.esql.plan.physical.ProjectExec;
9782
import org.elasticsearch.xpack.esql.plan.physical.TopNExec;
98-
import org.elasticsearch.xpack.esql.planner.FilterTests;
9983
import org.elasticsearch.xpack.esql.plugin.EsqlFlags;
100-
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
10184
import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery;
10285
import org.elasticsearch.xpack.esql.rule.Rule;
10386
import org.elasticsearch.xpack.esql.rule.RuleExecutor;
10487
import org.elasticsearch.xpack.esql.session.Configuration;
10588
import org.elasticsearch.xpack.esql.stats.SearchContextStats;
10689
import org.elasticsearch.xpack.esql.stats.SearchStats;
107-
import org.elasticsearch.xpack.esql.telemetry.Metrics;
10890
import org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter;
10991
import org.elasticsearch.xpack.kql.query.KqlQueryBuilder;
110-
import org.junit.Before;
11192

11293
import java.io.IOException;
11394
import java.util.ArrayList;
@@ -120,7 +101,6 @@
120101
import java.util.function.BiFunction;
121102
import java.util.function.Function;
122103

123-
import static java.util.Arrays.asList;
124104
import static org.elasticsearch.compute.aggregation.AggregatorMode.FINAL;
125105
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
126106
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
@@ -129,14 +109,8 @@
129109
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
130110
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
131111
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_PLANNER_SETTINGS;
132-
import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER;
133112
import static org.elasticsearch.xpack.esql.EsqlTestUtils.as;
134-
import static org.elasticsearch.xpack.esql.EsqlTestUtils.configuration;
135-
import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyInferenceResolution;
136-
import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping;
137113
import static org.elasticsearch.xpack.esql.EsqlTestUtils.unboundLogicalOptimizerContext;
138-
import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
139-
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.defaultLookupResolution;
140114
import static org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils.indexWithDateDateNanosUnionType;
141115
import static org.elasticsearch.xpack.esql.core.querydsl.query.Query.unscore;
142116
import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS;
@@ -153,7 +127,7 @@
153127
import static org.hamcrest.Matchers.nullValue;
154128

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

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

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

175-
protected TestPlannerOptimizer plannerOptimizer;
176-
private TestPlannerOptimizer plannerOptimizerDateDateNanosUnionTypes;
177-
private Analyzer timeSeriesAnalyzer;
178-
protected TestPlannerOptimizer plannerOptimizerTimeSeries;
179-
private final Configuration config;
180148
private final SearchStats IS_SV_STATS = new TestSearchStats() {
181149
@Override
182150
public boolean isSingleValue(FieldAttribute.FieldName field) {
@@ -196,82 +164,8 @@ public String constantValue(FieldAttribute.FieldName name) {
196164
}
197165
};
198166

199-
@ParametersFactory(argumentFormatting = PARAM_FORMATTING)
200-
public static List<Object[]> readScriptSpec() {
201-
return settings().stream().map(t -> {
202-
var settings = Settings.builder().loadFromMap(t.v2()).build();
203-
return new Object[] { t.v1(), configuration(new QueryPragmas(settings)) };
204-
}).toList();
205-
}
206-
207-
private static List<Tuple<String, Map<String, Object>>> settings() {
208-
return asList(new Tuple<>("default", Map.of()));
209-
}
210-
211167
public LocalPhysicalPlanOptimizerTests(String name, Configuration config) {
212-
this.config = config;
213-
}
214-
215-
@Before
216-
public void init() {
217-
EnrichResolution enrichResolution = new EnrichResolution();
218-
enrichResolution.addResolvedPolicy(
219-
"foo",
220-
Enrich.Mode.ANY,
221-
new ResolvedEnrichPolicy(
222-
"fld",
223-
EnrichPolicy.MATCH_TYPE,
224-
List.of("a", "b"),
225-
Map.of("", "idx"),
226-
Map.ofEntries(
227-
Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)),
228-
Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE))
229-
)
230-
)
231-
);
232-
plannerOptimizer = new TestPlannerOptimizer(config, makeAnalyzer("mapping-basic.json", enrichResolution));
233-
var timeSeriesMapping = loadMapping("k8s-mappings.json");
234-
var timeSeriesIndex = IndexResolution.valid(new EsIndex("k8s", timeSeriesMapping, Map.of("k8s", IndexMode.TIME_SERIES)));
235-
timeSeriesAnalyzer = new Analyzer(
236-
new AnalyzerContext(
237-
EsqlTestUtils.TEST_CFG,
238-
new EsqlFunctionRegistry(),
239-
timeSeriesIndex,
240-
enrichResolution,
241-
emptyInferenceResolution()
242-
),
243-
TEST_VERIFIER
244-
);
245-
plannerOptimizerTimeSeries = new TestPlannerOptimizer(config, timeSeriesAnalyzer);
246-
}
247-
248-
private Analyzer makeAnalyzer(String mappingFileName, EnrichResolution enrichResolution) {
249-
var mapping = loadMapping(mappingFileName);
250-
EsIndex test = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD));
251-
IndexResolution getIndexResult = IndexResolution.valid(test);
252-
253-
return new Analyzer(
254-
new AnalyzerContext(
255-
config,
256-
new EsqlFunctionRegistry(),
257-
getIndexResult,
258-
defaultLookupResolution(),
259-
enrichResolution,
260-
emptyInferenceResolution()
261-
),
262-
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
263-
);
264-
}
265-
266-
protected Analyzer makeAnalyzer(String mappingFileName) {
267-
return makeAnalyzer(mappingFileName, new EnrichResolution());
268-
}
269-
270-
private Analyzer makeAnalyzer(IndexResolution indexResolution) {
271-
return new Analyzer(
272-
new AnalyzerContext(config, new EsqlFunctionRegistry(), indexResolution, new EnrichResolution(), emptyInferenceResolution()),
273-
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
274-
);
168+
super(name, config);
275169
}
276170

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

2563-
protected static QueryBuilder wrapWithSingleQuery(String query, QueryBuilder inner, String fieldName, Source source) {
2564-
return FilterTests.singleValueQuery(query, inner, fieldName, source);
2565-
}
2566-
25672457
private Stat queryStatsFor(PhysicalPlan plan) {
25682458
var limit = as(plan, LimitExec.class);
25692459
var agg = as(limit.child(), AggregateExec.class);
@@ -2575,11 +2465,6 @@ private Stat queryStatsFor(PhysicalPlan plan) {
25752465
return stat;
25762466
}
25772467

2578-
@Override
2579-
protected List<String> filteredWarnings() {
2580-
return withDefaultLimitWarning(super.filteredWarnings());
2581-
}
2582-
25832468
private static KqlQueryBuilder kqlQueryBuilder(String query) {
25842469
return new KqlQueryBuilder(query);
25852470
}

0 commit comments

Comments
 (0)