Skip to content

Commit 1398524

Browse files
authored
Simplify index pattern (elastic#134215)
1 parent 8531284 commit 1398524

File tree

5 files changed

+27
-64
lines changed

5 files changed

+27
-64
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/PreAnalyzer.java

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,15 @@
1515
import org.elasticsearch.xpack.esql.plan.logical.UnresolvedRelation;
1616

1717
import java.util.ArrayList;
18-
import java.util.HashSet;
1918
import java.util.List;
20-
import java.util.Set;
21-
22-
import static java.util.Collections.emptyList;
2319

2420
/**
2521
* This class is part of the planner. Acts somewhat like a linker, to find the indices and enrich policies referenced by the query.
2622
*/
2723
public class PreAnalyzer {
2824

29-
public static class PreAnalysis {
30-
public static final PreAnalysis EMPTY = new PreAnalysis(null, emptyList(), emptyList(), emptyList());
31-
32-
public final IndexMode indexMode;
33-
public final List<IndexPattern> indices;
34-
public final List<Enrich> enriches;
35-
public final List<IndexPattern> lookupIndices;
36-
37-
public PreAnalysis(IndexMode indexMode, List<IndexPattern> indices, List<Enrich> enriches, List<IndexPattern> lookupIndices) {
38-
this.indexMode = indexMode;
39-
this.indices = indices;
40-
this.enriches = enriches;
41-
this.lookupIndices = lookupIndices;
42-
}
25+
public record PreAnalysis(IndexMode indexMode, IndexPattern index, List<Enrich> enriches, List<IndexPattern> lookupIndices) {
26+
public static final PreAnalysis EMPTY = new PreAnalysis(null, null, List.of(), List.of());
4327
}
4428

4529
public PreAnalysis preAnalyze(LogicalPlan plan) {
@@ -51,18 +35,19 @@ public PreAnalysis preAnalyze(LogicalPlan plan) {
5135
}
5236

5337
protected PreAnalysis doPreAnalyze(LogicalPlan plan) {
54-
Set<IndexPattern> indices = new HashSet<>();
38+
39+
Holder<IndexMode> indexMode = new Holder<>();
40+
Holder<IndexPattern> index = new Holder<>();
5541

5642
List<Enrich> unresolvedEnriches = new ArrayList<>();
5743
List<IndexPattern> lookupIndices = new ArrayList<>();
5844

59-
Holder<IndexMode> indexMode = new Holder<>();
6045
plan.forEachUp(UnresolvedRelation.class, p -> {
6146
if (p.indexMode() == IndexMode.LOOKUP) {
6247
lookupIndices.add(p.indexPattern());
6348
} else if (indexMode.get() == null || indexMode.get() == p.indexMode()) {
6449
indexMode.set(p.indexMode());
65-
indices.add(p.indexPattern());
50+
index.set(p.indexPattern());
6651
} else {
6752
throw new IllegalStateException("index mode is already set");
6853
}
@@ -73,7 +58,6 @@ protected PreAnalysis doPreAnalyze(LogicalPlan plan) {
7358
// mark plan as preAnalyzed (if it were marked, there would be no analysis)
7459
plan.forEachUp(LogicalPlan::setPreAnalyzed);
7560

76-
return new PreAnalysis(indexMode.get(), indices.stream().toList(), unresolvedEnriches, lookupIndices);
61+
return new PreAnalysis(indexMode.get(), index.get(), unresolvedEnriches, lookupIndices);
7762
}
78-
7963
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtils.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,20 +322,19 @@ static void updateExecutionInfoAtEndOfPlanning(EsqlExecutionInfo execInfo) {
322322
public static void initCrossClusterState(
323323
IndicesExpressionGrouper indicesGrouper,
324324
XPackLicenseState licenseState,
325-
List<IndexPattern> patterns,
325+
IndexPattern pattern,
326326
EsqlExecutionInfo executionInfo
327327
) throws ElasticsearchStatusException {
328-
if (patterns.isEmpty()) {
328+
if (pattern == null) {
329329
return;
330330
}
331-
assert patterns.size() == 1 : "Only single index pattern is supported";
332331
try {
333332
var groupedIndices = indicesGrouper.groupIndices(
334333
// indicesGrouper.getConfiguredClusters() might return mutable set that changes as clusters connect or disconnect.
335334
// it is copied here so that we have the same resolution when request contains multiple remote cluster patterns with *
336335
Set.copyOf(indicesGrouper.getConfiguredClusters()),
337336
IndicesOptions.DEFAULT,
338-
patterns.getFirst().indexPattern()
337+
pattern.indexPattern()
339338
);
340339

341340
executionInfo.clusterInfoInitializing(true);

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
5151
import org.elasticsearch.xpack.esql.index.EsIndex;
5252
import org.elasticsearch.xpack.esql.index.IndexResolution;
53-
import org.elasticsearch.xpack.esql.index.MappingException;
5453
import org.elasticsearch.xpack.esql.inference.InferenceResolution;
5554
import org.elasticsearch.xpack.esql.inference.InferenceService;
5655
import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer;
@@ -374,14 +373,14 @@ public void analyzedPlan(
374373
}
375374

376375
var preAnalysis = preAnalyzer.preAnalyze(parsed);
377-
EsqlCCSUtils.initCrossClusterState(indicesExpressionGrouper, verifier.licenseState(), preAnalysis.indices, executionInfo);
376+
EsqlCCSUtils.initCrossClusterState(indicesExpressionGrouper, verifier.licenseState(), preAnalysis.index(), executionInfo);
378377

379378
SubscribableListener. //
380-
<EnrichResolution>newForked(l -> enrichPolicyResolver.resolvePolicies(preAnalysis.enriches, executionInfo, l))
379+
<EnrichResolution>newForked(l -> enrichPolicyResolver.resolvePolicies(preAnalysis.enriches(), executionInfo, l))
381380
.<PreAnalysisResult>andThenApply(enrichResolution -> FieldNameUtils.resolveFieldNames(parsed, enrichResolution))
382381
.<PreAnalysisResult>andThen((l, r) -> resolveInferences(parsed, r, l))
383382
.<PreAnalysisResult>andThen((l, r) -> preAnalyzeMainIndices(preAnalysis, executionInfo, r, requestFilter, l))
384-
.<PreAnalysisResult>andThen((l, r) -> preAnalyzeLookupIndices(preAnalysis.lookupIndices.iterator(), r, executionInfo, l))
383+
.<PreAnalysisResult>andThen((l, r) -> preAnalyzeLookupIndices(preAnalysis.lookupIndices().iterator(), r, executionInfo, l))
385384
.<LogicalPlan>andThen((l, r) -> analyzeWithRetry(parsed, requestFilter, preAnalysis, executionInfo, r, l))
386385
.addListener(logicalPlanListener);
387386
}
@@ -633,26 +632,17 @@ private void preAnalyzeMainIndices(
633632
ThreadPool.Names.SEARCH_COORDINATION,
634633
ThreadPool.Names.SYSTEM_READ
635634
);
636-
// TODO we plan to support joins in the future when possible, but for now we'll just fail early if we see one
637-
List<IndexPattern> indices = preAnalysis.indices;
638-
if (indices.size() > 1) {
639-
// Note: JOINs are not supported but we detect them when
640-
listener.onFailure(new MappingException("Queries with multiple indices are not supported"));
641-
} else if (indices.size() == 1) {
642-
IndexPattern table = indices.getFirst();
643-
644-
// if the preceding call to the enrich policy API found unavailable clusters, recreate the index expression to search
645-
// based only on available clusters (which could now be an empty list)
635+
if (preAnalysis.index() != null) {
646636
String indexExpressionToResolve = EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo);
647637
if (indexExpressionToResolve.isEmpty()) {
648638
// if this was a pure remote CCS request (no local indices) and all remotes are offline, return an empty IndexResolution
649639
listener.onResponse(
650-
result.withIndexResolution(IndexResolution.valid(new EsIndex(table.indexPattern(), Map.of(), Map.of())))
640+
result.withIndexResolution(IndexResolution.valid(new EsIndex(preAnalysis.index().indexPattern(), Map.of(), Map.of())))
651641
);
652642
} else {
653643
boolean includeAllDimensions = false;
654644
// call the EsqlResolveFieldsAction (field-caps) to resolve indices and get field types
655-
if (preAnalysis.indexMode == IndexMode.TIME_SERIES) {
645+
if (preAnalysis.indexMode() == IndexMode.TIME_SERIES) {
656646
includeAllDimensions = true;
657647
// TODO: Maybe if no indices are returned, retry without index mode and provide a clearer error message.
658648
var indexModeFilter = new TermQueryBuilder(IndexModeFieldMapper.NAME, IndexMode.TIME_SERIES.getName());
@@ -673,12 +663,8 @@ private void preAnalyzeMainIndices(
673663
);
674664
}
675665
} else {
676-
try {
677-
// occurs when dealing with local relations (row a = 1)
678-
listener.onResponse(result.withIndexResolution(IndexResolution.invalid("[none specified]")));
679-
} catch (Exception ex) {
680-
listener.onFailure(ex);
681-
}
666+
// occurs when dealing with local relations (row a = 1)
667+
listener.onResponse(result.withIndexResolution(IndexResolution.invalid("[none specified]")));
682668
}
683669
}
684670

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -530,18 +530,12 @@ private LogicalPlan analyzedPlan(LogicalPlan parsed, CsvTestsDataLoader.MultiInd
530530

531531
private static CsvTestsDataLoader.MultiIndexTestDataset testDatasets(LogicalPlan parsed) {
532532
var preAnalysis = new PreAnalyzer().preAnalyze(parsed);
533-
var indices = preAnalysis.indices;
534-
if (indices.isEmpty()) {
535-
/*
536-
* If the data set doesn't matter we'll just grab one we know works.
537-
* Employees is fine.
538-
*/
533+
if (preAnalysis.index() == null) {
534+
// If the data set doesn't matter we'll just grab one we know works. Employees is fine.
539535
return CsvTestsDataLoader.MultiIndexTestDataset.of(CSV_DATASET_MAP.get("employees"));
540-
} else if (preAnalysis.indices.size() > 1) {
541-
throw new IllegalArgumentException("unexpected index resolution to multiple entries [" + preAnalysis.indices.size() + "]");
542536
}
543537

544-
String indexName = indices.getFirst().indexPattern();
538+
String indexName = preAnalysis.index().indexPattern();
545539
List<CsvTestsDataLoader.TestDataset> datasets = new ArrayList<>();
546540
if (indexName.endsWith("*")) {
547541
String indexPrefix = indexName.substring(0, indexName.length() - 1);

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlCCSUtilsTests.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ public void testInitCrossClusterState() {
699699

700700
// local only search works with any license state
701701
{
702-
var localOnly = List.of(new IndexPattern(EMPTY, randomFrom("idx", "idx1,idx2*")));
702+
var localOnly = new IndexPattern(EMPTY, randomFrom("idx", "idx1,idx2*"));
703703

704704
assertLicenseCheckPasses(indicesGrouper, null, localOnly, "");
705705
for (var mode : License.OperationMode.values()) {
@@ -710,7 +710,7 @@ public void testInitCrossClusterState() {
710710

711711
// cross-cluster search requires a valid (active, non-expired) enterprise license OR a valid trial license
712712
{
713-
var remote = List.of(new IndexPattern(EMPTY, randomFrom("idx,remote:idx", "idx1,remote:idx2*,remote:logs")));
713+
var remote = new IndexPattern(EMPTY, randomFrom("idx,remote:idx", "idx1,remote:idx2*,remote:logs"));
714714

715715
var supportedLicenses = EnumSet.of(License.OperationMode.TRIAL, License.OperationMode.ENTERPRISE);
716716
var unsupportedLicenses = EnumSet.complementOf(supportedLicenses);
@@ -738,26 +738,26 @@ private static XPackLicenseState createLicenseState(XPackLicenseStatus status) {
738738
private void assertLicenseCheckPasses(
739739
TestIndicesExpressionGrouper indicesGrouper,
740740
XPackLicenseStatus status,
741-
List<IndexPattern> patterns,
741+
IndexPattern pattern,
742742
String... expectedRemotes
743743
) {
744744
var executionInfo = new EsqlExecutionInfo(true);
745-
initCrossClusterState(indicesGrouper, createLicenseState(status), patterns, executionInfo);
745+
initCrossClusterState(indicesGrouper, createLicenseState(status), pattern, executionInfo);
746746
assertThat(executionInfo.clusterAliases(), containsInAnyOrder(expectedRemotes));
747747
}
748748

749749
private void assertLicenseCheckFails(
750750
TestIndicesExpressionGrouper indicesGrouper,
751751
XPackLicenseStatus licenseStatus,
752-
List<IndexPattern> patterns,
752+
IndexPattern pattern,
753753
String expectedErrorMessageSuffix
754754
) {
755755
ElasticsearchStatusException e = expectThrows(
756756
ElasticsearchStatusException.class,
757757
equalTo(
758758
"A valid Enterprise license is required to run ES|QL cross-cluster searches. License found: " + expectedErrorMessageSuffix
759759
),
760-
() -> initCrossClusterState(indicesGrouper, createLicenseState(licenseStatus), patterns, new EsqlExecutionInfo(true))
760+
() -> initCrossClusterState(indicesGrouper, createLicenseState(licenseStatus), pattern, new EsqlExecutionInfo(true))
761761
);
762762
assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST));
763763
}

0 commit comments

Comments
 (0)