Skip to content

Commit 77cb0ab

Browse files
committed
Query refactoring: QueryBuilder to extend Writeable
`QueryBuilder`s need to become streamable over the wire, so we can use them as our own intermediate query representation and send it over the wire. Using `Writeable` rather than `Streamable` allows us to make some fields `final` and delete default constructors needed only for serialization purposes. Taken the chance also to revise the internals of `IdsQueryBuilder` (modified some internal data type and added deduplication of ids to reduce bits to serialize). Expanded also `IdsQueryBuilderTest`, injected random types and improved comparison. Moved all query builder tests to leverage lucene's equals and hashcode to compare result of `toQuery` with expected lucene query Also refactored spanterm and term query tests, more core could be shared. Also changed `RangeQueryBuilderTest` to remove the need for query rewriting and added some more coverage (we don't test when search context is null only anymore, search context gets now randomly set) Closes elastic#11191
1 parent 86218bc commit 77cb0ab

16 files changed

+394
-290
lines changed

src/main/java/org/elasticsearch/index/query/BaseTermQueryBuilder.java

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,18 @@
2222
import org.apache.lucene.util.BytesRef;
2323
import org.elasticsearch.common.io.stream.StreamInput;
2424
import org.elasticsearch.common.io.stream.StreamOutput;
25-
import org.elasticsearch.common.io.stream.Streamable;
2625
import org.elasticsearch.common.xcontent.XContentBuilder;
2726

2827
import java.io.IOException;
2928
import java.util.Objects;
3029

31-
public abstract class BaseTermQueryBuilder<QB extends BoostableQueryBuilder<QB>> extends QueryBuilder implements Streamable, BoostableQueryBuilder<QB> {
32-
30+
public abstract class BaseTermQueryBuilder<QB extends BaseTermQueryBuilder<QB>> extends QueryBuilder<QB> implements BoostableQueryBuilder<QB> {
31+
3332
/** Name of field to match against. */
34-
protected String fieldName;
33+
protected final String fieldName;
3534

3635
/** Value to find matches for. */
37-
protected Object value;
36+
protected final Object value;
3837

3938
/** Query boost. */
4039
protected float boost = 1.0f;
@@ -116,10 +115,6 @@ public BaseTermQueryBuilder(String fieldName, Object value) {
116115
this.value = convertToBytesRefIfString(value);
117116
}
118117

119-
BaseTermQueryBuilder() {
120-
// for serialization only
121-
}
122-
123118
/** Returns the field name used in this query. */
124119
public String fieldName() {
125120
return this.fieldName;
@@ -206,24 +201,23 @@ public boolean equals(Object obj) {
206201
if (obj == null || getClass() != obj.getClass()) {
207202
return false;
208203
}
209-
@SuppressWarnings("rawtypes")
210204
BaseTermQueryBuilder other = (BaseTermQueryBuilder) obj;
211205
return Objects.equals(fieldName, other.fieldName) &&
212206
Objects.equals(value, other.value) &&
213207
Objects.equals(boost, other.boost) &&
214208
Objects.equals(queryName, other.queryName);
215209
}
216210

217-
/** Read the given parameters. */
218211
@Override
219-
public void readFrom(StreamInput in) throws IOException {
220-
fieldName = in.readString();
221-
value = in.readGenericValue();
222-
boost = in.readFloat();
223-
queryName = in.readOptionalString();
212+
public QB readFrom(StreamInput in) throws IOException {
213+
QB emptyBuilder = createBuilder(in.readString(), in.readGenericValue());
214+
emptyBuilder.boost = in.readFloat();
215+
emptyBuilder.queryName = in.readOptionalString();
216+
return emptyBuilder;
224217
}
225218

226-
/** Writes the given parameters. */
219+
protected abstract QB createBuilder(String fieldName, Object value);
220+
227221
@Override
228222
public void writeTo(StreamOutput out) throws IOException {
229223
out.writeString(fieldName);

src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java

Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,59 +19,53 @@
1919

2020
package org.elasticsearch.index.query;
2121

22-
import com.google.common.collect.Iterables;
23-
22+
import com.google.common.collect.Sets;
2423
import org.apache.lucene.queries.TermsQuery;
2524
import org.apache.lucene.search.Query;
25+
import org.elasticsearch.cluster.metadata.MetaData;
26+
import org.elasticsearch.common.Nullable;
2627
import org.elasticsearch.common.io.stream.StreamInput;
2728
import org.elasticsearch.common.io.stream.StreamOutput;
28-
import org.elasticsearch.common.io.stream.Streamable;
2929
import org.elasticsearch.common.lucene.search.Queries;
3030
import org.elasticsearch.common.xcontent.XContentBuilder;
3131
import org.elasticsearch.index.mapper.Uid;
3232
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
3333

3434
import java.io.IOException;
35-
import java.util.ArrayList;
36-
import java.util.Arrays;
37-
import java.util.Collection;
38-
import java.util.List;
39-
import java.util.Objects;
35+
import java.util.*;
4036

4137
/**
4238
* A query that will return only documents matching specific ids (and a type).
4339
*/
44-
public class IdsQueryBuilder extends QueryBuilder implements Streamable, BoostableQueryBuilder<IdsQueryBuilder> {
40+
public class IdsQueryBuilder extends QueryBuilder<IdsQueryBuilder> implements BoostableQueryBuilder<IdsQueryBuilder> {
4541

46-
private List<String> types = new ArrayList<>();
42+
private final Set<String> ids = Sets.newHashSet();
4743

48-
private List<String> ids = new ArrayList<>();
44+
private final String[] types;
4945

5046
private float boost = 1.0f;
5147

5248
private String queryName;
5349

54-
public IdsQueryBuilder() {
55-
//for serialization only
56-
}
57-
58-
public IdsQueryBuilder(String... types) {
59-
this.types = (types == null || types.length == 0) ? new ArrayList<String>() : Arrays.asList(types);
50+
/**
51+
* Creates a new IdsQueryBuilder by optionally providing the types of the documents to look for
52+
*/
53+
public IdsQueryBuilder(@Nullable String... types) {
54+
this.types = types;
6055
}
6156

6257
/**
63-
* Get the types used in this query
64-
* @return the types
58+
* Returns the types used in this query
6559
*/
66-
public Collection<String> types() {
60+
public String[] types() {
6761
return this.types;
6862
}
6963

7064
/**
7165
* Adds ids to the query.
7266
*/
7367
public IdsQueryBuilder addIds(String... ids) {
74-
this.ids.addAll(Arrays.asList(ids));
68+
Collections.addAll(this.ids, ids);
7569
return this;
7670
}
7771

@@ -83,9 +77,9 @@ public IdsQueryBuilder ids(String... ids) {
8377
}
8478

8579
/**
86-
* Gets the ids for the query.
80+
* Returns the ids for the query.
8781
*/
88-
public Collection<String> ids() {
82+
public Set<String> ids() {
8983
return this.ids;
9084
}
9185

@@ -125,22 +119,18 @@ public String queryName() {
125119
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
126120
builder.startObject(IdsQueryParser.NAME);
127121
if (types != null) {
128-
if (types.size() == 1) {
129-
builder.field("type", types.get(0));
122+
if (types.length == 1) {
123+
builder.field("type", types[0]);
130124
} else {
131-
builder.startArray("types");
132-
for (String type : types) {
133-
builder.value(type);
134-
}
135-
builder.endArray();
125+
builder.array("types", types);
136126
}
137127
}
138128
builder.startArray("values");
139129
for (String value : ids) {
140130
builder.value(value);
141131
}
142132
builder.endArray();
143-
if (boost != -1) {
133+
if (boost != 1.0f) {
144134
builder.field("boost", boost);
145135
}
146136
if (queryName != null) {
@@ -155,18 +145,21 @@ protected String parserName() {
155145
}
156146

157147
public Query toQuery(QueryParseContext parseContext) throws IOException, QueryParsingException {
148+
Query query;
158149
if (this.ids.isEmpty()) {
159-
return Queries.newMatchNoDocsQuery();
160-
}
150+
query = Queries.newMatchNoDocsQuery();
151+
} else {
152+
Collection<String> typesForQuery;
153+
if (types == null || types.length == 0) {
154+
typesForQuery = parseContext.queryTypes();
155+
} else if (types.length == 1 && MetaData.ALL.equals(types[0])) {
156+
typesForQuery = parseContext.mapperService().types();
157+
} else {
158+
typesForQuery = Sets.newHashSet(types);
159+
}
161160

162-
Collection<String> typesForQuery = this.types;
163-
if (typesForQuery == null || typesForQuery.isEmpty()) {
164-
typesForQuery = parseContext.queryTypes();
165-
} else if (typesForQuery.size() == 1 && Iterables.getFirst(typesForQuery, null).equals("_all")) {
166-
typesForQuery = parseContext.mapperService().types();
161+
query = new TermsQuery(UidFieldMapper.NAME, Uid.createUidsForTypesAndIds(typesForQuery, ids));
167162
}
168-
169-
TermsQuery query = new TermsQuery(UidFieldMapper.NAME, Uid.createUidsForTypesAndIds(typesForQuery, ids));
170163
query.setBoost(boost);
171164
if (queryName != null) {
172165
parseContext.addNamedQuery(queryName, query);
@@ -181,24 +174,25 @@ public QueryValidationException validate() {
181174
}
182175

183176
@Override
184-
public void readFrom(StreamInput in) throws IOException {
185-
this.types = in.readStringList();
186-
this.ids = in.readStringList();
187-
queryName = in.readOptionalString();
188-
boost = in.readFloat();
177+
public IdsQueryBuilder readFrom(StreamInput in) throws IOException {
178+
IdsQueryBuilder idsQueryBuilder = new IdsQueryBuilder(in.readStringArray());
179+
idsQueryBuilder.addIds(in.readStringArray());
180+
idsQueryBuilder.queryName = in.readOptionalString();
181+
idsQueryBuilder.boost = in.readFloat();
182+
return idsQueryBuilder;
189183
}
190184

191185
@Override
192186
public void writeTo(StreamOutput out) throws IOException {
193-
out.writeStringList(this.types);
194-
out.writeStringList(this.ids);
187+
out.writeStringArray(this.types);
188+
out.writeStringArray(this.ids.toArray(new String[this.ids.size()]));
195189
out.writeOptionalString(queryName);
196190
out.writeFloat(boost);
197191
}
198192

199193
@Override
200194
public int hashCode() {
201-
return Objects.hash(ids, types, boost, queryName);
195+
return Objects.hash(ids, Arrays.hashCode(types), boost, queryName);
202196
}
203197

204198
@Override
@@ -211,7 +205,7 @@ public boolean equals(Object obj) {
211205
}
212206
IdsQueryBuilder other = (IdsQueryBuilder) obj;
213207
return Objects.equals(ids, other.ids) &&
214-
Objects.equals(types, other.types) &&
208+
Arrays.equals(types, other.types) &&
215209
Objects.equals(boost, other.boost) &&
216210
Objects.equals(queryName, other.queryName);
217211
}

src/main/java/org/elasticsearch/index/query/IdsQueryParser.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.elasticsearch.index.query;
2121

2222
import com.google.common.collect.ImmutableList;
23-
2423
import org.elasticsearch.common.inject.Inject;
2524
import org.elasticsearch.common.xcontent.XContentParser;
2625

src/main/java/org/elasticsearch/index/query/MatchAllQueryBuilder.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.apache.lucene.search.Query;
2424
import org.elasticsearch.common.io.stream.StreamInput;
2525
import org.elasticsearch.common.io.stream.StreamOutput;
26-
import org.elasticsearch.common.io.stream.Streamable;
2726
import org.elasticsearch.common.lucene.search.Queries;
2827
import org.elasticsearch.common.xcontent.XContentBuilder;
2928

@@ -32,7 +31,7 @@
3231
/**
3332
* A query that matches on all documents.
3433
*/
35-
public class MatchAllQueryBuilder extends QueryBuilder implements Streamable, BoostableQueryBuilder<MatchAllQueryBuilder> {
34+
public class MatchAllQueryBuilder extends QueryBuilder<MatchAllQueryBuilder> implements BoostableQueryBuilder<MatchAllQueryBuilder> {
3635

3736
private float boost = 1.0f;
3837

@@ -67,7 +66,6 @@ public Query toQuery(QueryParseContext parseContext) {
6766
if (this.boost == 1.0f) {
6867
return Queries.newMatchAllQuery();
6968
}
70-
7169
MatchAllDocsQuery query = new MatchAllDocsQuery();
7270
query.setBoost(boost);
7371
return query;
@@ -91,8 +89,10 @@ public int hashCode() {
9189
}
9290

9391
@Override
94-
public void readFrom(StreamInput in) throws IOException {
95-
this.boost = in.readFloat();
92+
public MatchAllQueryBuilder readFrom(StreamInput in) throws IOException {
93+
MatchAllQueryBuilder matchAllQueryBuilder = new MatchAllQueryBuilder();
94+
matchAllQueryBuilder.boost = in.readFloat();
95+
return matchAllQueryBuilder;
9696
}
9797

9898
@Override

src/main/java/org/elasticsearch/index/query/MultiTermQueryBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
*/
1919
package org.elasticsearch.index.query;
2020

21-
public abstract class MultiTermQueryBuilder extends QueryBuilder {
21+
public abstract class MultiTermQueryBuilder<QB extends MultiTermQueryBuilder<QB>> extends QueryBuilder<QB> {
2222

2323
}

src/main/java/org/elasticsearch/index/query/QueryBuilder.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import org.elasticsearch.action.support.ToXContentToBytes;
2424
import org.apache.lucene.util.BytesRef;
2525
import org.elasticsearch.common.lucene.BytesRefs;
26+
import org.elasticsearch.common.io.stream.StreamInput;
27+
import org.elasticsearch.common.io.stream.StreamOutput;
28+
import org.elasticsearch.common.io.stream.Writeable;
2629
import org.elasticsearch.common.xcontent.XContentBuilder;
2730
import org.elasticsearch.common.xcontent.XContentType;
2831

@@ -32,7 +35,7 @@
3235
* Base class for all classes producing lucene queries.
3336
* Supports conversion to BytesReference and creation of lucene Query objects.
3437
*/
35-
public abstract class QueryBuilder extends ToXContentToBytes {
38+
public abstract class QueryBuilder<QB> extends ToXContentToBytes implements Writeable<QB> {
3639

3740
protected QueryBuilder() {
3841
super(XContentType.JSON);
@@ -103,4 +106,15 @@ protected static Object convertToStringIfBytesRef(Object obj) {
103106
}
104107
return obj;
105108
}
109+
110+
//norelease remove this once all builders implement readFrom themselves
111+
@Override
112+
public QB readFrom(StreamInput in) throws IOException {
113+
return null;
114+
}
115+
116+
//norelease remove this once all builders implement writeTo themselves
117+
@Override
118+
public void writeTo(StreamOutput out) throws IOException {
119+
}
106120
}

0 commit comments

Comments
 (0)