Skip to content

Commit 53db46b

Browse files
committed
Query refactoring: SpanFirstQueryBuilder and Parser
Moving the query building functionality from the parser to the builders new toQuery() method analogous to other recent query refactorings. Relates to #10217
1 parent 6c02301 commit 53db46b

File tree

3 files changed

+153
-19
lines changed

3 files changed

+153
-19
lines changed

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

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@
1919

2020
package org.elasticsearch.index.query;
2121

22+
import org.apache.lucene.search.Query;
23+
import org.apache.lucene.search.spans.SpanFirstQuery;
24+
import org.apache.lucene.search.spans.SpanQuery;
25+
import org.elasticsearch.common.io.stream.StreamInput;
26+
import org.elasticsearch.common.io.stream.StreamOutput;
2227
import org.elasticsearch.common.xcontent.XContentBuilder;
2328

2429
import java.io.IOException;
30+
import java.util.Objects;
2531

2632
public class SpanFirstQueryBuilder extends AbstractQueryBuilder<SpanFirstQueryBuilder> implements SpanQueryBuilder<SpanFirstQueryBuilder>{
2733

@@ -31,11 +37,44 @@ public class SpanFirstQueryBuilder extends AbstractQueryBuilder<SpanFirstQueryBu
3137

3238
private final int end;
3339

34-
static final SpanFirstQueryBuilder SPAN_FIRST_QUERY_BUILDER = new SpanFirstQueryBuilder(null, -1);
40+
static final SpanFirstQueryBuilder SPAN_FIRST_QUERY_BUILDER = new SpanFirstQueryBuilder();
3541

42+
/**
43+
* Query that matches spans queries defined in <code>matchBuilder</code>
44+
* whose end position is less than or equal to <code>end</code>.
45+
* @param matchBuilder inner {@link SpanQueryBuilder}
46+
* @param end maximum end position of the match, needs to be positive
47+
* @throws IllegalArgumentException for negative <code>end</code> positions
48+
*/
3649
public SpanFirstQueryBuilder(SpanQueryBuilder matchBuilder, int end) {
37-
this.matchBuilder = matchBuilder;
38-
this.end = end;
50+
this.matchBuilder = Objects.requireNonNull(matchBuilder);
51+
if (end >= 0) {
52+
this.end = end;
53+
} else {
54+
throw new IllegalArgumentException("end parameter needs to be positive");
55+
}
56+
}
57+
58+
/**
59+
* only used for prototype
60+
*/
61+
private SpanFirstQueryBuilder() {
62+
this.matchBuilder = null;
63+
this.end = -1;
64+
}
65+
66+
/**
67+
* @return the inner {@link SpanQueryBuilder} defined in this query
68+
*/
69+
public SpanQueryBuilder matchBuilder() {
70+
return this.matchBuilder;
71+
}
72+
73+
/**
74+
* @return maximum end position of the matching inner span query
75+
*/
76+
public int end() {
77+
return this.end;
3978
}
4079

4180
@Override
@@ -48,6 +87,42 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
4887
builder.endObject();
4988
}
5089

90+
@Override
91+
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
92+
Query innerSpanQuery = matchBuilder.toQuery(parseContext);
93+
assert innerSpanQuery instanceof SpanQuery;
94+
return new SpanFirstQuery((SpanQuery) innerSpanQuery, end);
95+
}
96+
97+
@Override
98+
public QueryValidationException validate() {
99+
return validateInnerQuery(matchBuilder, null);
100+
}
101+
102+
@Override
103+
protected SpanFirstQueryBuilder doReadFrom(StreamInput in) throws IOException {
104+
SpanQueryBuilder matchBuilder = in.readNamedWriteable();
105+
int end = in.readInt();
106+
return new SpanFirstQueryBuilder(matchBuilder, end);
107+
}
108+
109+
@Override
110+
protected void doWriteTo(StreamOutput out) throws IOException {
111+
out.writeNamedWriteable(matchBuilder);
112+
out.writeInt(end);
113+
}
114+
115+
@Override
116+
protected int doHashCode() {
117+
return Objects.hash(matchBuilder, end);
118+
}
119+
120+
@Override
121+
protected boolean doEquals(SpanFirstQueryBuilder other) {
122+
return Objects.equals(matchBuilder, other.matchBuilder) &&
123+
Objects.equals(end, other.end);
124+
}
125+
51126
@Override
52127
public String getName() {
53128
return NAME;

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

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

2020
package org.elasticsearch.index.query;
2121

22-
import org.apache.lucene.search.Query;
23-
import org.apache.lucene.search.spans.SpanFirstQuery;
24-
import org.apache.lucene.search.spans.SpanQuery;
2522
import org.elasticsearch.common.Strings;
2623
import org.elasticsearch.common.inject.Inject;
2724
import org.elasticsearch.common.xcontent.XContentParser;
@@ -31,7 +28,7 @@
3128
/**
3229
*
3330
*/
34-
public class SpanFirstQueryParser extends BaseQueryParserTemp {
31+
public class SpanFirstQueryParser extends BaseQueryParser {
3532

3633
@Inject
3734
public SpanFirstQueryParser() {
@@ -43,12 +40,12 @@ public String[] names() {
4340
}
4441

4542
@Override
46-
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
43+
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
4744
XContentParser parser = parseContext.parser();
4845

4946
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
5047

51-
SpanQuery match = null;
48+
SpanQueryBuilder match = null;
5249
int end = -1;
5350
String queryName = null;
5451

@@ -59,11 +56,11 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
5956
currentFieldName = parser.currentName();
6057
} else if (token == XContentParser.Token.START_OBJECT) {
6158
if ("match".equals(currentFieldName)) {
62-
Query query = parseContext.parseInnerQuery();
63-
if (!(query instanceof SpanQuery)) {
59+
QueryBuilder query = parseContext.parseInnerQueryBuilder();
60+
if (!(query instanceof SpanQueryBuilder)) {
6461
throw new QueryParsingException(parseContext, "spanFirst [match] must be of type span query");
6562
}
66-
match = (SpanQuery) query;
63+
match = (SpanQueryBuilder) query;
6764
} else {
6865
throw new QueryParsingException(parseContext, "[span_first] query does not support [" + currentFieldName + "]");
6966
}
@@ -85,13 +82,9 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
8582
if (end == -1) {
8683
throw new QueryParsingException(parseContext, "spanFirst must have [end] set for it");
8784
}
88-
89-
SpanFirstQuery query = new SpanFirstQuery(match, end);
90-
query.setBoost(boost);
91-
if (queryName != null) {
92-
parseContext.addNamedQuery(queryName, query);
93-
}
94-
return query;
85+
SpanFirstQueryBuilder queryBuilder = new SpanFirstQueryBuilder(match, end);
86+
queryBuilder.boost(boost).queryName(queryName);
87+
return queryBuilder;
9588
}
9689

9790
@Override
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.query;
21+
22+
import org.apache.lucene.search.Query;
23+
import org.apache.lucene.search.spans.SpanFirstQuery;
24+
import org.apache.lucene.search.spans.SpanQuery;
25+
import org.junit.Test;
26+
27+
import java.io.IOException;
28+
29+
public class SpanFirstQueryBuilderTest extends BaseQueryTestCase<SpanFirstQueryBuilder> {
30+
31+
@Override
32+
protected Query doCreateExpectedQuery(SpanFirstQueryBuilder testQueryBuilder, QueryParseContext context) throws IOException {
33+
SpanQuery innerQuery = (SpanQuery) testQueryBuilder.matchBuilder().toQuery(context);
34+
return new SpanFirstQuery(innerQuery, testQueryBuilder.end());
35+
}
36+
37+
@Override
38+
protected SpanFirstQueryBuilder doCreateTestQueryBuilder() {
39+
SpanTermQueryBuilder innerQueryBuilder = new SpanTermQueryBuilderTest().createTestQueryBuilder();
40+
return new SpanFirstQueryBuilder(innerQueryBuilder, randomIntBetween(0, 1000));
41+
}
42+
43+
@Test
44+
public void testValidate() {
45+
int totalExpectedErrors = 0;
46+
SpanQueryBuilder innerSpanQueryBuilder;
47+
if (randomBoolean()) {
48+
innerSpanQueryBuilder = new SpanTermQueryBuilder("", "test");
49+
totalExpectedErrors++;
50+
} else {
51+
innerSpanQueryBuilder = new SpanTermQueryBuilder("name", "value");
52+
}
53+
SpanFirstQueryBuilder queryBuilder = new SpanFirstQueryBuilder(innerSpanQueryBuilder, 10);
54+
assertValidate(queryBuilder, totalExpectedErrors);
55+
}
56+
57+
@Test(expected=IllegalArgumentException.class)
58+
public void testEndValueNegative() {
59+
new SpanFirstQueryBuilder(new SpanTermQueryBuilder("name", "value"), -1);
60+
}
61+
62+
@Test(expected=NullPointerException.class)
63+
public void testInnerQueryNull() {
64+
new SpanFirstQueryBuilder(null, 1);
65+
}
66+
}

0 commit comments

Comments
 (0)