Skip to content

Commit 99ac708

Browse files
committed
Refactoring of Indices Query
Relates to #10217 This PR is against the query-refactoring branch. Closes #12031
1 parent 4a3faf1 commit 99ac708

File tree

14 files changed

+437
-108
lines changed

14 files changed

+437
-108
lines changed

core/src/main/java/org/elasticsearch/index/query/CommonTermsQueryBuilder.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ public boolean disableCoord() {
198198
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
199199
builder.startObject(NAME);
200200
builder.startObject(fieldName);
201-
202201
builder.field("query", text);
203202
builder.field("disable_coord", disableCoord);
204203
builder.field("high_freq_operator", highFreqOperator.toString());

core/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222
import org.apache.lucene.search.Query;
2323
import org.apache.lucene.util.CloseableThreadLocal;
2424
import org.elasticsearch.Version;
25+
import org.elasticsearch.action.support.IndicesOptions;
26+
import org.elasticsearch.cluster.ClusterService;
27+
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
2528
import org.elasticsearch.common.Nullable;
2629
import org.elasticsearch.common.ParseFieldMatcher;
2730
import org.elasticsearch.common.bytes.BytesReference;
2831
import org.elasticsearch.common.inject.Inject;
2932
import org.elasticsearch.common.lucene.search.Queries;
33+
import org.elasticsearch.common.regex.Regex;
3034
import org.elasticsearch.common.settings.Settings;
3135
import org.elasticsearch.common.xcontent.XContentFactory;
3236
import org.elasticsearch.common.xcontent.XContentHelper;
@@ -72,6 +76,10 @@ protected QueryShardContext initialValue() {
7276

7377
final IndexFieldDataService fieldDataService;
7478

79+
final ClusterService clusterService;
80+
81+
final IndexNameExpressionResolver indexNameExpressionResolver;
82+
7583
final BitsetFilterCache bitsetFilterCache;
7684

7785
private final IndicesQueriesRegistry indicesQueriesRegistry;
@@ -87,7 +95,8 @@ public IndexQueryParserService(Index index, @IndexSettings Settings indexSetting
8795
ScriptService scriptService, AnalysisService analysisService,
8896
MapperService mapperService, IndexCache indexCache, IndexFieldDataService fieldDataService,
8997
BitsetFilterCache bitsetFilterCache,
90-
@Nullable SimilarityService similarityService) {
98+
@Nullable SimilarityService similarityService, ClusterService clusterService,
99+
IndexNameExpressionResolver indexNameExpressionResolver) {
91100
super(index, indexSettings);
92101
this.scriptService = scriptService;
93102
this.analysisService = analysisService;
@@ -96,6 +105,8 @@ public IndexQueryParserService(Index index, @IndexSettings Settings indexSetting
96105
this.indexCache = indexCache;
97106
this.fieldDataService = fieldDataService;
98107
this.bitsetFilterCache = bitsetFilterCache;
108+
this.clusterService = clusterService;
109+
this.indexNameExpressionResolver = indexNameExpressionResolver;
99110

100111
this.defaultField = indexSettings.get(DEFAULT_FIELD, AllFieldMapper.NAME);
101112
this.queryStringLenient = indexSettings.getAsBoolean(QUERY_STRING_LENIENT, false);
@@ -318,4 +329,14 @@ private static ParsedQuery innerParse(QueryShardContext context, QueryBuilder qu
318329
public ParseFieldMatcher parseFieldMatcher() {
319330
return parseFieldMatcher;
320331
}
332+
333+
public boolean matchesIndices(String... indices) {
334+
final String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterService.state(), IndicesOptions.lenientExpandOpen(), indices);
335+
for (String index : concreteIndices) {
336+
if (Regex.simpleMatch(index, this.index.name())) {
337+
return true;
338+
}
339+
}
340+
return false;
341+
}
321342
}

core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919

2020
package org.elasticsearch.index.query;
2121

22+
import org.apache.lucene.search.Query;
23+
import org.elasticsearch.common.io.stream.StreamInput;
24+
import org.elasticsearch.common.io.stream.StreamOutput;
2225
import org.elasticsearch.common.xcontent.XContentBuilder;
2326

2427
import java.io.IOException;
28+
import java.util.Arrays;
29+
import java.util.Objects;
2530

2631
/**
2732
* A query that will execute the wrapped query only for the specified indices, and "match_all" when
@@ -31,48 +36,64 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde
3136

3237
public static final String NAME = "indices";
3338

34-
private final QueryBuilder queryBuilder;
39+
private final QueryBuilder innerQuery;
3540

3641
private final String[] indices;
3742

38-
private String sNoMatchQuery;
39-
private QueryBuilder noMatchQuery;
43+
private QueryBuilder noMatchQuery = defaultNoMatchQuery();
4044

41-
static final IndicesQueryBuilder PROTOTYPE = new IndicesQueryBuilder(null);
45+
static final IndicesQueryBuilder PROTOTYPE = new IndicesQueryBuilder();
4246

43-
public IndicesQueryBuilder(QueryBuilder queryBuilder, String... indices) {
44-
this.queryBuilder = queryBuilder;
47+
private IndicesQueryBuilder() {
48+
this.innerQuery = null;
49+
this.indices = null;
50+
}
51+
52+
public IndicesQueryBuilder(QueryBuilder innerQuery, String... indices) {
53+
this.innerQuery = Objects.requireNonNull(innerQuery);
4554
this.indices = indices;
4655
}
4756

57+
public QueryBuilder innerQuery() {
58+
return this.innerQuery;
59+
}
60+
61+
public String[] indices() {
62+
return this.indices;
63+
}
64+
4865
/**
49-
* Sets the no match query, can either be <tt>all</tt> or <tt>none</tt>.
66+
* Sets the query to use when it executes on an index that does not match the indices provided.
5067
*/
51-
public IndicesQueryBuilder noMatchQuery(String type) {
52-
this.sNoMatchQuery = type;
68+
public IndicesQueryBuilder noMatchQuery(QueryBuilder noMatchQuery) {
69+
this.noMatchQuery = (noMatchQuery != null) ? noMatchQuery : defaultNoMatchQuery();
5370
return this;
5471
}
5572

5673
/**
57-
* Sets the query to use when it executes on an index that does not match the indices provided.
74+
* Sets the no match query, can either be <tt>all</tt> or <tt>none</tt>.
5875
*/
59-
public IndicesQueryBuilder noMatchQuery(QueryBuilder noMatchQuery) {
60-
this.noMatchQuery = noMatchQuery;
76+
public IndicesQueryBuilder noMatchQuery(String type) {
77+
this.noMatchQuery = IndicesQueryParser.parseNoMatchQuery(type);
6178
return this;
6279
}
6380

81+
public QueryBuilder noMatchQuery() {
82+
return this.noMatchQuery;
83+
}
84+
85+
static QueryBuilder defaultNoMatchQuery() {
86+
return QueryBuilders.matchAllQuery();
87+
}
88+
6489
@Override
6590
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
6691
builder.startObject(NAME);
6792
builder.field("indices", indices);
6893
builder.field("query");
69-
queryBuilder.toXContent(builder, params);
70-
if (noMatchQuery != null) {
71-
builder.field("no_match_query");
72-
noMatchQuery.toXContent(builder, params);
73-
} else if (sNoMatchQuery != null) {
74-
builder.field("no_match_query", sNoMatchQuery);
75-
}
94+
innerQuery.toXContent(builder, params);
95+
builder.field("no_match_query");
96+
noMatchQuery.toXContent(builder, params);
7697
printBoostAndQueryName(builder);
7798
builder.endObject();
7899
}
@@ -81,4 +102,52 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
81102
public String getWriteableName() {
82103
return NAME;
83104
}
105+
106+
@Override
107+
protected Query doToQuery(QueryShardContext context) throws IOException {
108+
if (context.matchesIndices(indices)) {
109+
return innerQuery.toQuery(context);
110+
}
111+
return noMatchQuery.toQuery(context);
112+
}
113+
114+
@Override
115+
public QueryValidationException validate() {
116+
QueryValidationException validationException = null;
117+
if (this.innerQuery == null) {
118+
validationException = addValidationError("inner query cannot be null", validationException);
119+
}
120+
if (this.indices == null || this.indices.length == 0) {
121+
validationException = addValidationError("list of indices cannot be null or empty", validationException);
122+
}
123+
validationException = validateInnerQuery(innerQuery, validationException);
124+
validationException = validateInnerQuery(noMatchQuery, validationException);
125+
return validationException;
126+
}
127+
128+
@Override
129+
protected IndicesQueryBuilder doReadFrom(StreamInput in) throws IOException {
130+
IndicesQueryBuilder indicesQueryBuilder = new IndicesQueryBuilder(in.readQuery(), in.readStringArray());
131+
indicesQueryBuilder.noMatchQuery = in.readQuery();
132+
return indicesQueryBuilder;
133+
}
134+
135+
@Override
136+
protected void doWriteTo(StreamOutput out) throws IOException {
137+
out.writeQuery(innerQuery);
138+
out.writeStringArray(indices);
139+
out.writeQuery(noMatchQuery);
140+
}
141+
142+
@Override
143+
public int doHashCode() {
144+
return Objects.hash(innerQuery, noMatchQuery, Arrays.hashCode(indices));
145+
}
146+
147+
@Override
148+
protected boolean doEquals(IndicesQueryBuilder other) {
149+
return Objects.equals(innerQuery, other.innerQuery) &&
150+
Arrays.equals(indices, other.indices) && // otherwise we are comparing pointers
151+
Objects.equals(noMatchQuery, other.noMatchQuery);
152+
}
84153
}

core/src/main/java/org/elasticsearch/index/query/IndicesQueryParser.java

Lines changed: 27 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,24 @@
1919

2020
package org.elasticsearch.index.query;
2121

22-
import org.apache.lucene.search.Query;
23-
import org.elasticsearch.action.support.IndicesOptions;
24-
import org.elasticsearch.cluster.ClusterService;
25-
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
26-
import org.elasticsearch.common.Nullable;
2722
import org.elasticsearch.common.ParseField;
2823
import org.elasticsearch.common.inject.Inject;
29-
import org.elasticsearch.common.lucene.search.Queries;
30-
import org.elasticsearch.common.regex.Regex;
3124
import org.elasticsearch.common.xcontent.XContentParser;
32-
import org.elasticsearch.index.query.support.XContentStructure;
3325

3426
import java.io.IOException;
3527
import java.util.ArrayList;
3628
import java.util.Collection;
3729

3830
/**
31+
* Parser for {@link IndicesQueryBuilder}.
3932
*/
40-
public class IndicesQueryParser extends BaseQueryParserTemp {
33+
public class IndicesQueryParser extends BaseQueryParser {
4134

4235
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
4336
private static final ParseField NO_MATCH_QUERY = new ParseField("no_match_query", "no_match_filter");
4437

45-
@Nullable
46-
private final ClusterService clusterService;
47-
private final IndexNameExpressionResolver indexNameExpressionResolver;
48-
4938
@Inject
50-
public IndicesQueryParser(@Nullable ClusterService clusterService, IndexNameExpressionResolver indexNameExpressionResolver) {
51-
this.clusterService = clusterService;
52-
this.indexNameExpressionResolver = indexNameExpressionResolver;
39+
public IndicesQueryParser() {
5340
}
5441

5542
@Override
@@ -58,65 +45,52 @@ public String[] names() {
5845
}
5946

6047
@Override
61-
public Query parse(QueryShardContext context) throws IOException, QueryParsingException {
62-
QueryParseContext parseContext = context.parseContext();
48+
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
6349
XContentParser parser = parseContext.parser();
6450

65-
Query noMatchQuery = null;
66-
boolean queryFound = false;
67-
boolean indicesFound = false;
68-
boolean currentIndexMatchesIndices = false;
51+
QueryBuilder innerQuery = null;
52+
Collection<String> indices = new ArrayList<>();
53+
QueryBuilder noMatchQuery = IndicesQueryBuilder.defaultNoMatchQuery();
54+
6955
String queryName = null;
7056
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
7157

7258
String currentFieldName = null;
7359
XContentParser.Token token;
74-
XContentStructure.InnerQuery innerQuery = null;
75-
XContentStructure.InnerQuery innerNoMatchQuery = null;
7660
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
7761
if (token == XContentParser.Token.FIELD_NAME) {
7862
currentFieldName = parser.currentName();
7963
} else if (token == XContentParser.Token.START_OBJECT) {
8064
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
81-
innerQuery = new XContentStructure.InnerQuery(parseContext, null);
82-
queryFound = true;
65+
innerQuery = parseContext.parseInnerQueryBuilder();
8366
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_QUERY)) {
84-
innerNoMatchQuery = new XContentStructure.InnerQuery(parseContext, null);
67+
noMatchQuery = parseContext.parseInnerQueryBuilder();
8568
} else {
8669
throw new QueryParsingException(parseContext, "[indices] query does not support [" + currentFieldName + "]");
8770
}
8871
} else if (token == XContentParser.Token.START_ARRAY) {
8972
if ("indices".equals(currentFieldName)) {
90-
if (indicesFound) {
73+
if (indices.isEmpty() == false) {
9174
throw new QueryParsingException(parseContext, "[indices] indices or index already specified");
9275
}
93-
indicesFound = true;
94-
Collection<String> indices = new ArrayList<>();
9576
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
9677
String value = parser.textOrNull();
9778
if (value == null) {
9879
throw new QueryParsingException(parseContext, "[indices] no value specified for 'indices' entry");
9980
}
10081
indices.add(value);
10182
}
102-
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), indices.toArray(new String[indices.size()]));
10383
} else {
10484
throw new QueryParsingException(parseContext, "[indices] query does not support [" + currentFieldName + "]");
10585
}
10686
} else if (token.isValue()) {
10787
if ("index".equals(currentFieldName)) {
108-
if (indicesFound) {
88+
if (indices.isEmpty() == false) {
10989
throw new QueryParsingException(parseContext, "[indices] indices or index already specified");
11090
}
111-
indicesFound = true;
112-
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), parser.text());
91+
indices.add(parser.text());
11392
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_QUERY)) {
114-
String type = parser.text();
115-
if ("all".equals(type)) {
116-
noMatchQuery = Queries.newMatchAllQuery();
117-
} else if ("none".equals(type)) {
118-
noMatchQuery = Queries.newMatchNoDocsQuery();
119-
}
93+
noMatchQuery = parseNoMatchQuery(parser.text());
12094
} else if ("_name".equals(currentFieldName)) {
12195
queryName = parser.text();
12296
} else if ("boost".equals(currentFieldName)) {
@@ -126,44 +100,26 @@ public Query parse(QueryShardContext context) throws IOException, QueryParsingEx
126100
}
127101
}
128102
}
129-
if (!queryFound) {
103+
104+
if (innerQuery == null) {
130105
throw new QueryParsingException(parseContext, "[indices] requires 'query' element");
131106
}
132-
if (!indicesFound) {
107+
if (indices.isEmpty()) {
133108
throw new QueryParsingException(parseContext, "[indices] requires 'indices' or 'index' element");
134109
}
135-
136-
Query chosenQuery;
137-
if (currentIndexMatchesIndices) {
138-
chosenQuery = innerQuery.asQuery();
139-
} else {
140-
// If noMatchQuery is set, it means "no_match_query" was "all" or "none"
141-
if (noMatchQuery != null) {
142-
chosenQuery = noMatchQuery;
143-
} else {
144-
// There might be no "no_match_query" set, so default to the match_all if not set
145-
if (innerNoMatchQuery == null) {
146-
chosenQuery = Queries.newMatchAllQuery();
147-
} else {
148-
chosenQuery = innerNoMatchQuery.asQuery();
149-
}
150-
}
151-
}
152-
if (queryName != null) {
153-
context.addNamedQuery(queryName, chosenQuery);
154-
}
155-
chosenQuery.setBoost(boost);
156-
return chosenQuery;
110+
return new IndicesQueryBuilder(innerQuery, indices.toArray(new String[indices.size()]))
111+
.noMatchQuery(noMatchQuery)
112+
.boost(boost)
113+
.queryName(queryName);
157114
}
158115

159-
protected boolean matchesIndices(String currentIndex, String... indices) {
160-
final String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterService.state(), IndicesOptions.lenientExpandOpen(), indices);
161-
for (String index : concreteIndices) {
162-
if (Regex.simpleMatch(index, currentIndex)) {
163-
return true;
164-
}
116+
static QueryBuilder parseNoMatchQuery(String type) {
117+
if ("all".equals(type)) {
118+
return QueryBuilders.matchAllQuery();
119+
} else if ("none".equals(type)) {
120+
return new MatchNoneQueryBuilder();
165121
}
166-
return false;
122+
throw new IllegalArgumentException("query type can only be [all] or [none] but not " + "[" + type + "]");
167123
}
168124

169125
@Override

0 commit comments

Comments
 (0)