Skip to content

Commit b9f878e

Browse files
authored
Cross-project search index expression rewriting (#135346)
This PR implements cross-project search index resolution and ports the resolve index API to use it. Relates: ES-12690
1 parent 1d88dbc commit b9f878e

File tree

22 files changed

+674
-162
lines changed

22 files changed

+674
-162
lines changed

server/src/main/java/org/elasticsearch/ElasticsearchException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE;
8181
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
8282
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName;
83-
import static org.elasticsearch.search.crossproject.IndexExpressionsRewriter.NO_MATCHING_PROJECT_EXCEPTION_VERSION;
83+
import static org.elasticsearch.search.crossproject.CrossProjectIndexExpressionsRewriter.NO_MATCHING_PROJECT_EXCEPTION_VERSION;
8484

8585
/**
8686
* A base class for all elasticsearch exceptions.

server/src/main/java/org/elasticsearch/action/IndicesRequest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ default ResolvedIndexExpressions getResolvedIndexExpressions() {
8080
default boolean allowsRemoteIndices() {
8181
return false;
8282
}
83+
84+
/**
85+
* Determines whether the request type allows cross-project processing. Cross-project processing entails cross-project search
86+
* index resolution and error handling. Note: this method only determines in the request _supports_ cross-project.
87+
* Whether cross-project processing is actually performed is determined by {@link IndicesOptions}.
88+
*/
89+
default boolean allowsCrossProject() {
90+
return false;
91+
}
8392
}
8493

8594
/**

server/src/main/java/org/elasticsearch/action/ResolvedIndexExpression.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
package org.elasticsearch.action;
1111

1212
import org.elasticsearch.ElasticsearchException;
13+
import org.elasticsearch.common.io.stream.StreamInput;
14+
import org.elasticsearch.common.io.stream.StreamOutput;
15+
import org.elasticsearch.common.io.stream.Writeable;
1316
import org.elasticsearch.core.Nullable;
1417

18+
import java.io.IOException;
1519
import java.util.Set;
1620

1721
/**
@@ -40,7 +44,21 @@
4044
* and failure info
4145
* @param remoteExpressions the remote expressions that replace the original
4246
*/
43-
public record ResolvedIndexExpression(String original, LocalExpressions localExpressions, Set<String> remoteExpressions) {
47+
public record ResolvedIndexExpression(String original, LocalExpressions localExpressions, Set<String> remoteExpressions)
48+
implements
49+
Writeable {
50+
51+
public ResolvedIndexExpression(StreamInput in) throws IOException {
52+
this(in.readString(), new LocalExpressions(in), in.readCollectionAsImmutableSet(StreamInput::readString));
53+
}
54+
55+
@Override
56+
public void writeTo(StreamOutput out) throws IOException {
57+
out.writeString(original);
58+
localExpressions.writeTo(out);
59+
out.writeStringCollection(remoteExpressions);
60+
}
61+
4462
/**
4563
* Indicates if a local index resolution attempt was successful or failed.
4664
* Failures can be due to concrete resources not being visible (either missing or not visible due to indices options)
@@ -62,13 +80,28 @@ public record LocalExpressions(
6280
Set<String> expressions,
6381
LocalIndexResolutionResult localIndexResolutionResult,
6482
@Nullable ElasticsearchException exception
65-
) {
83+
) implements Writeable {
6684
public LocalExpressions {
6785
assert localIndexResolutionResult != LocalIndexResolutionResult.SUCCESS || exception == null
6886
: "If the local resolution result is SUCCESS, exception must be null";
6987
}
7088

7189
// Singleton for the case where all expressions in a ResolvedIndexExpression instance are remote
7290
public static final LocalExpressions NONE = new LocalExpressions(Set.of(), LocalIndexResolutionResult.NONE, null);
91+
92+
public LocalExpressions(StreamInput in) throws IOException {
93+
this(
94+
in.readCollectionAsImmutableSet(StreamInput::readString),
95+
in.readEnum(LocalIndexResolutionResult.class),
96+
ElasticsearchException.readException(in)
97+
);
98+
}
99+
100+
@Override
101+
public void writeTo(StreamOutput out) throws IOException {
102+
out.writeStringCollection(expressions);
103+
out.writeEnum(localIndexResolutionResult);
104+
ElasticsearchException.writeException(exception, out);
105+
}
73106
}
74107
}

server/src/main/java/org/elasticsearch/action/ResolvedIndexExpressions.java

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99

1010
package org.elasticsearch.action;
1111

12+
import org.elasticsearch.TransportVersion;
1213
import org.elasticsearch.action.ResolvedIndexExpression.LocalExpressions;
14+
import org.elasticsearch.common.io.stream.StreamInput;
15+
import org.elasticsearch.common.io.stream.StreamOutput;
16+
import org.elasticsearch.common.io.stream.Writeable;
1317

18+
import java.io.IOException;
1419
import java.util.ArrayList;
1520
import java.util.HashSet;
1621
import java.util.List;
@@ -20,50 +25,78 @@
2025
/**
2126
* A collection of {@link ResolvedIndexExpression}.
2227
*/
23-
public record ResolvedIndexExpressions(List<ResolvedIndexExpression> expressions) {
28+
public record ResolvedIndexExpressions(List<ResolvedIndexExpression> expressions) implements Writeable {
29+
public static final TransportVersion RESOLVED_INDEX_EXPRESSIONS = TransportVersion.fromName("resolved_index_expressions");
30+
31+
public ResolvedIndexExpressions(StreamInput in) throws IOException {
32+
this(in.readCollectionAsImmutableList(ResolvedIndexExpression::new));
33+
}
2434

2535
public List<String> getLocalIndicesList() {
2636
return expressions.stream().flatMap(e -> e.localExpressions().expressions().stream()).toList();
2737
}
2838

39+
public List<String> getRemoteIndicesList() {
40+
return expressions.stream().flatMap(e -> e.remoteExpressions().stream()).toList();
41+
}
42+
2943
public static Builder builder() {
3044
return new Builder();
3145
}
3246

47+
@Override
48+
public void writeTo(StreamOutput out) throws IOException {
49+
out.writeCollection(expressions);
50+
}
51+
3352
public static final class Builder {
3453
private final List<ResolvedIndexExpression> expressions = new ArrayList<>();
3554

3655
/**
56+
* Add a new resolved expression.
3757
* @param original the original expression that was resolved -- may be blank for "access all" cases
3858
* @param localExpressions is a HashSet as an optimization -- the set needs to be mutable, and we want to avoid copying it.
3959
* May be empty.
4060
*/
41-
public void addLocalExpressions(
61+
public void addExpressions(
4262
String original,
4363
HashSet<String> localExpressions,
44-
ResolvedIndexExpression.LocalIndexResolutionResult resolutionResult
64+
ResolvedIndexExpression.LocalIndexResolutionResult resolutionResult,
65+
Set<String> remoteExpressions
4566
) {
4667
Objects.requireNonNull(original);
4768
Objects.requireNonNull(localExpressions);
4869
Objects.requireNonNull(resolutionResult);
70+
Objects.requireNonNull(remoteExpressions);
4971
expressions.add(
50-
new ResolvedIndexExpression(original, new LocalExpressions(localExpressions, resolutionResult, null), new HashSet<>())
72+
new ResolvedIndexExpression(original, new LocalExpressions(localExpressions, resolutionResult, null), remoteExpressions)
5173
);
5274
}
5375

76+
public void addRemoteExpressions(String original, Set<String> remoteExpressions) {
77+
Objects.requireNonNull(original);
78+
Objects.requireNonNull(remoteExpressions);
79+
expressions.add(new ResolvedIndexExpression(original, LocalExpressions.NONE, remoteExpressions));
80+
}
81+
5482
/**
5583
* Exclude the given expressions from the local expressions of all prior added {@link ResolvedIndexExpression}.
5684
*/
5785
public void excludeFromLocalExpressions(Set<String> expressionsToExclude) {
5886
Objects.requireNonNull(expressionsToExclude);
5987
if (expressionsToExclude.isEmpty() == false) {
6088
for (ResolvedIndexExpression prior : expressions) {
61-
prior.localExpressions().expressions().removeAll(expressionsToExclude);
89+
final Set<String> localExpressions = prior.localExpressions().expressions();
90+
if (localExpressions.isEmpty()) {
91+
continue;
92+
}
93+
localExpressions.removeAll(expressionsToExclude);
6294
}
6395
}
6496
}
6597

6698
public ResolvedIndexExpressions build() {
99+
// TODO make all sets on `expressions` immutable
67100
return new ResolvedIndexExpressions(expressions);
68101
}
69102
}

0 commit comments

Comments
 (0)