Skip to content

Commit 7eacb55

Browse files
author
Christoph Büscher
committed
Query refactoring: refactored IdsQuery and added test
1 parent e142fc9 commit 7eacb55

File tree

8 files changed

+344
-53
lines changed

8 files changed

+344
-53
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.apache.lucene.search.Query;
2323
import org.elasticsearch.ElasticsearchException;
24+
import org.elasticsearch.action.ActionRequestValidationException;
2425
import org.elasticsearch.common.bytes.BytesReference;
2526
import org.elasticsearch.common.xcontent.XContentBuilder;
2627
import org.elasticsearch.common.xcontent.XContentFactory;
@@ -85,4 +86,11 @@ public Query toQuery(QueryParseContext parseContext) throws QueryParsingExceptio
8586
protected abstract String parserName();
8687

8788
protected abstract void doXContent(XContentBuilder builder, Params params) throws IOException;
89+
90+
@Override
91+
public QueryValidationException validate() {
92+
// default impl does not validate, subclasses should override.
93+
//norelease to be removed once all queries support validation
94+
return null;
95+
}
8896
}

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

Lines changed: 133 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,45 +19,72 @@
1919

2020
package org.elasticsearch.index.query;
2121

22+
import com.google.common.collect.Iterables;
23+
24+
import org.apache.lucene.queries.TermsQuery;
25+
import org.apache.lucene.search.Query;
26+
import org.apache.lucene.util.BytesRef;
27+
import org.elasticsearch.common.io.stream.StreamInput;
28+
import org.elasticsearch.common.io.stream.StreamOutput;
29+
import org.elasticsearch.common.io.stream.Streamable;
30+
import org.elasticsearch.common.lucene.search.Queries;
2231
import org.elasticsearch.common.xcontent.XContentBuilder;
32+
import org.elasticsearch.index.mapper.Uid;
33+
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
2334

2435
import java.io.IOException;
2536
import java.util.ArrayList;
2637
import java.util.Arrays;
27-
import java.util.List;
38+
import java.util.Collection;
39+
import java.util.Objects;
2840

2941
/**
3042
* A query that will return only documents matching specific ids (and a type).
3143
*/
32-
public class IdsQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<IdsQueryBuilder> {
44+
public class IdsQueryBuilder extends BaseQueryBuilder implements Streamable, BoostableQueryBuilder<IdsQueryBuilder> {
3345

34-
private final List<String> types;
46+
private Collection<String> types = new ArrayList<>();
3547

36-
private List<String> values = new ArrayList<>();
48+
private Collection<String> ids = new ArrayList<>();
3749

38-
private float boost = -1;
50+
private float boost = 1.0f;
3951

4052
private String queryName;
4153

4254
public IdsQueryBuilder(String... types) {
43-
this.types = types == null ? null : Arrays.asList(types);
55+
this.types = (types == null || types.length == 0) ? new ArrayList<String>() : Arrays.asList(types);
4456
}
4557

4658
/**
47-
* Adds ids to the filter.
59+
* Get the types used in this query
60+
* @return the types
61+
*/
62+
public Collection<String> types() {
63+
return this.types;
64+
}
65+
66+
/**
67+
* Adds ids to the query.
4868
*/
4969
public IdsQueryBuilder addIds(String... ids) {
50-
values.addAll(Arrays.asList(ids));
70+
this.ids.addAll(Arrays.asList(ids));
5171
return this;
5272
}
5373

5474
/**
55-
* Adds ids to the filter.
75+
* Adds ids to the query.
5676
*/
5777
public IdsQueryBuilder ids(String... ids) {
5878
return addIds(ids);
5979
}
6080

81+
/**
82+
* Gets the ids for the query.
83+
*/
84+
public Collection<String> ids() {
85+
return this.ids;
86+
}
87+
6188
/**
6289
* Sets the boost for this query. Documents matching this query will (in addition to the normal
6390
* weightings) have their score multiplied by the boost provided.
@@ -69,19 +96,33 @@ public IdsQueryBuilder boost(float boost) {
6996
}
7097

7198
/**
72-
* Sets the query name for the filter that can be used when searching for matched_filters per hit.
99+
* Gets the boost for this query.
100+
*/
101+
public float boost() {
102+
return this.boost;
103+
}
104+
105+
/**
106+
* Sets the query name for the query that can be used when searching for matched_filters per hit.
73107
*/
74108
public IdsQueryBuilder queryName(String queryName) {
75109
this.queryName = queryName;
76110
return this;
77111
}
78112

113+
/**
114+
* Gets the query name for the query.
115+
*/
116+
public String queryName() {
117+
return this.queryName;
118+
}
119+
79120
@Override
80121
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
81122
builder.startObject(IdsQueryParser.NAME);
82123
if (types != null) {
83124
if (types.size() == 1) {
84-
builder.field("type", types.get(0));
125+
builder.field("type", types.iterator().next());
85126
} else {
86127
builder.startArray("types");
87128
for (Object type : types) {
@@ -91,7 +132,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
91132
}
92133
}
93134
builder.startArray("values");
94-
for (Object value : values) {
135+
for (Object value : ids) {
95136
builder.value(value);
96137
}
97138
builder.endArray();
@@ -108,4 +149,84 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
108149
protected String parserName() {
109150
return IdsQueryParser.NAME;
110151
}
152+
153+
public Query toQuery(QueryParseContext parseContext) throws IOException, QueryParsingException {
154+
ArrayList<BytesRef> ids = new ArrayList<>();
155+
156+
for (String value : this.ids) {
157+
BytesRef ref = new BytesRef(value);
158+
ids.add(ref);
159+
}
160+
161+
if (ids.isEmpty()) {
162+
return Queries.newMatchNoDocsQuery();
163+
}
164+
165+
if (types == null || types.isEmpty()) {
166+
types = parseContext.queryTypes();
167+
} else if (types.size() == 1 && Iterables.getFirst(types, null).equals("_all")) {
168+
types = parseContext.mapperService().types();
169+
}
170+
171+
TermsQuery query = new TermsQuery(UidFieldMapper.NAME, Uid.createTypeUids(types, ids));
172+
query.setBoost(boost);
173+
if (queryName != null) {
174+
parseContext.addNamedQuery(queryName, query);
175+
}
176+
return query;
177+
}
178+
179+
@Override
180+
public QueryValidationException validate() {
181+
// all fields can be empty or null
182+
return null;
183+
}
184+
185+
@Override
186+
public void readFrom(StreamInput in) throws IOException {
187+
int typeSize = in.readInt();
188+
for (int i = 0; i < typeSize; i++) {
189+
types.add(in.readString());
190+
}
191+
int valueSize = in.readInt();
192+
for (int i = 0; i < valueSize; i++) {
193+
ids.add(in.readString());
194+
}
195+
queryName = in.readOptionalString();
196+
boost = in.readFloat();
197+
}
198+
199+
@Override
200+
public void writeTo(StreamOutput out) throws IOException {
201+
out.writeInt(types.size());
202+
for (String type : types) {
203+
out.writeString(type);
204+
}
205+
out.writeInt(ids.size());
206+
for (String value : ids) {
207+
out.writeString(value);
208+
}
209+
out.writeOptionalString(queryName);
210+
out.writeFloat(boost);
211+
}
212+
213+
@Override
214+
public int hashCode() {
215+
return Objects.hash(ids, types, boost, queryName);
216+
}
217+
218+
@Override
219+
public boolean equals(Object obj) {
220+
if (this == obj) {
221+
return true;
222+
}
223+
if (obj == null || getClass() != obj.getClass()) {
224+
return false;
225+
}
226+
IdsQueryBuilder other = (IdsQueryBuilder) obj;
227+
return Objects.equals(ids, other.ids) &&
228+
Objects.equals(types, other.types) &&
229+
Objects.equals(boost, other.boost) &&
230+
Objects.equals(queryName, other.queryName);
231+
}
111232
}

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

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,18 @@
2020
package org.elasticsearch.index.query;
2121

2222
import com.google.common.collect.ImmutableList;
23-
import com.google.common.collect.Iterables;
2423

25-
26-
import org.apache.lucene.queries.TermsQuery;
27-
import org.apache.lucene.search.Query;
28-
import org.apache.lucene.util.BytesRef;
2924
import org.elasticsearch.common.inject.Inject;
30-
import org.elasticsearch.common.lucene.search.Queries;
3125
import org.elasticsearch.common.xcontent.XContentParser;
32-
import org.elasticsearch.index.mapper.Uid;
33-
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
3426

3527
import java.io.IOException;
3628
import java.util.ArrayList;
37-
import java.util.Collection;
3829
import java.util.List;
3930

4031
/**
41-
*
32+
* Parser for the IdsQuery.
4233
*/
43-
public class IdsQueryParser extends BaseQueryParserTemp {
34+
public class IdsQueryParser extends BaseQueryParser {
4435

4536
public static final String NAME = "ids";
4637

@@ -53,15 +44,18 @@ public String[] names() {
5344
return new String[]{NAME};
5445
}
5546

47+
/**
48+
* @return a QueryBuilder representation of the query passed in as XContent in the parse context
49+
*/
5650
@Override
57-
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
51+
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
5852
XContentParser parser = parseContext.parser();
59-
60-
List<BytesRef> ids = new ArrayList<>();
61-
Collection<String> types = null;
62-
String currentFieldName = null;
53+
List<String> ids = new ArrayList<>();
54+
List<String> types = new ArrayList<>();
6355
float boost = 1.0f;
6456
String queryName = null;
57+
58+
String currentFieldName = null;
6559
XContentParser.Token token;
6660
boolean idsProvided = false;
6761
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@@ -73,18 +67,17 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
7367
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
7468
if ((token == XContentParser.Token.VALUE_STRING) ||
7569
(token == XContentParser.Token.VALUE_NUMBER)) {
76-
BytesRef value = parser.utf8BytesOrNull();
77-
if (value == null) {
70+
String id = parser.textOrNull();
71+
if (id == null) {
7872
throw new QueryParsingException(parseContext, "No value specified for term filter");
7973
}
80-
ids.add(value);
74+
ids.add(id);
8175
} else {
8276
throw new QueryParsingException(parseContext, "Illegal value for id, expecting a string or number, got: "
8377
+ token);
8478
}
8579
}
8680
} else if ("types".equals(currentFieldName) || "type".equals(currentFieldName)) {
87-
types = new ArrayList<>();
8881
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
8982
String value = parser.textOrNull();
9083
if (value == null) {
@@ -107,25 +100,16 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
107100
}
108101
}
109102
}
110-
111103
if (!idsProvided) {
112104
throw new QueryParsingException(parseContext, "[ids] query, no ids values provided");
113105
}
114-
115-
if (ids.isEmpty()) {
116-
return Queries.newMatchNoDocsQuery();
106+
IdsQueryBuilder query = new IdsQueryBuilder(types.toArray(new String[types.size()]));
107+
query.addIds(ids.toArray(new String[ids.size()]));
108+
if (boost != 1.0f) {
109+
query.boost(boost);
117110
}
118-
119-
if (types == null || types.isEmpty()) {
120-
types = parseContext.queryTypes();
121-
} else if (types.size() == 1 && Iterables.getFirst(types, null).equals("_all")) {
122-
types = parseContext.mapperService().types();
123-
}
124-
125-
TermsQuery query = new TermsQuery(UidFieldMapper.NAME, Uid.createTypeUids(types, ids));
126-
query.setBoost(boost);
127111
if (queryName != null) {
128-
parseContext.addNamedQuery(queryName, query);
112+
query.queryName(queryName);
129113
}
130114
return query;
131115
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,11 @@ public interface QueryBuilder extends ToXContent {
4444
* @throws IOException
4545
*/
4646
Query toQuery(QueryParseContext parseContext) throws QueryParsingException, IOException;
47+
48+
/**
49+
* Validate the query.
50+
* @return {@code null} if query is valid, otherwise {@link ActionRequestValidationException} containing error messages,
51+
* e.g. if fields that are needed to create the lucene query are missing.
52+
*/
53+
QueryValidationException validate();
4754
}

0 commit comments

Comments
 (0)