Skip to content

Commit 93674b8

Browse files
committed
Refactoring of RegexpQuery
Relates to #10217 Closes #11896 This PR is against the query-refactoring branch.
1 parent 026fd38 commit 93674b8

File tree

5 files changed

+270
-78
lines changed

5 files changed

+270
-78
lines changed

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

Lines changed: 131 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,73 @@
1919

2020
package org.elasticsearch.index.query;
2121

22+
import org.apache.lucene.index.Term;
23+
import org.apache.lucene.search.MultiTermQuery;
24+
import org.apache.lucene.search.Query;
25+
import org.apache.lucene.search.RegexpQuery;
2226
import org.apache.lucene.util.automaton.Operations;
27+
import org.elasticsearch.common.Strings;
28+
import org.elasticsearch.common.io.stream.StreamInput;
29+
import org.elasticsearch.common.io.stream.StreamOutput;
30+
import org.elasticsearch.common.lucene.BytesRefs;
2331
import org.elasticsearch.common.xcontent.XContentBuilder;
32+
import org.elasticsearch.index.mapper.MappedFieldType;
33+
import org.elasticsearch.index.query.support.QueryParsers;
2434

2535
import java.io.IOException;
36+
import java.util.Objects;
2637

2738
/**
2839
* A Query that does fuzzy matching for a specific value.
2940
*/
3041
public class RegexpQueryBuilder extends AbstractQueryBuilder<RegexpQueryBuilder> implements MultiTermQueryBuilder<RegexpQueryBuilder> {
3142

3243
public static final String NAME = "regexp";
33-
private final String name;
34-
private final String regexp;
3544

36-
private int flags = RegexpQueryParser.DEFAULT_FLAGS_VALUE;
45+
public static final int DEFAULT_FLAGS_VALUE = RegexpFlag.ALL.value();
46+
47+
public static final int DEFAULT_MAX_DETERMINIZED_STATES = Operations.DEFAULT_MAX_DETERMINIZED_STATES;
48+
49+
private final String fieldName;
50+
51+
private final String value;
52+
53+
private int flagsValue = DEFAULT_FLAGS_VALUE;
54+
55+
private int maxDeterminizedStates = DEFAULT_MAX_DETERMINIZED_STATES;
3756

3857
private String rewrite;
39-
private int maxDeterminizedStates = Operations.DEFAULT_MAX_DETERMINIZED_STATES;
40-
private boolean maxDetermizedStatesSet;
58+
4159
static final RegexpQueryBuilder PROTOTYPE = new RegexpQueryBuilder(null, null);
4260

4361
/**
44-
* Constructs a new term query.
45-
*
46-
* @param name The name of the field
47-
* @param regexp The regular expression
62+
* Constructs a new regex query.
63+
*
64+
* @param fieldName The name of the field
65+
* @param value The regular expression
66+
*/
67+
public RegexpQueryBuilder(String fieldName, String value) {
68+
this.fieldName = fieldName;
69+
this.value = value;
70+
}
71+
72+
/** Returns the field name used in this query. */
73+
public String fieldName() {
74+
return this.fieldName;
75+
}
76+
77+
/**
78+
* Returns the value used in this query.
4879
*/
49-
public RegexpQueryBuilder(String name, String regexp) {
50-
this.name = name;
51-
this.regexp = regexp;
80+
public String value() {
81+
return this.value;
5282
}
5383

5484
public RegexpQueryBuilder flags(RegexpFlag... flags) {
85+
if (flags == null) {
86+
this.flagsValue = DEFAULT_FLAGS_VALUE;
87+
return this;
88+
}
5589
int value = 0;
5690
if (flags.length == 0) {
5791
value = RegexpFlag.ALL.value;
@@ -60,35 +94,47 @@ public RegexpQueryBuilder flags(RegexpFlag... flags) {
6094
value |= flag.value;
6195
}
6296
}
63-
this.flags = value;
97+
this.flagsValue = value;
98+
return this;
99+
}
100+
101+
public RegexpQueryBuilder flags(int flags) {
102+
this.flagsValue = flags;
64103
return this;
65104
}
66105

106+
public int flags() {
107+
return this.flagsValue;
108+
}
109+
67110
/**
68111
* Sets the regexp maxDeterminizedStates.
69112
*/
70113
public RegexpQueryBuilder maxDeterminizedStates(int value) {
71114
this.maxDeterminizedStates = value;
72-
this.maxDetermizedStatesSet = true;
73115
return this;
74116
}
117+
118+
public int maxDeterminizedStates() {
119+
return this.maxDeterminizedStates;
120+
}
75121

76122
public RegexpQueryBuilder rewrite(String rewrite) {
77123
this.rewrite = rewrite;
78124
return this;
79125
}
126+
127+
public String rewrite() {
128+
return this.rewrite;
129+
}
80130

81131
@Override
82132
public void doXContent(XContentBuilder builder, Params params) throws IOException {
83133
builder.startObject(NAME);
84-
builder.startObject(name);
85-
builder.field("value", regexp);
86-
if (flags != -1) {
87-
builder.field("flags_value", flags);
88-
}
89-
if (maxDetermizedStatesSet) {
90-
builder.field("max_determinized_states", maxDeterminizedStates);
91-
}
134+
builder.startObject(fieldName);
135+
builder.field("value", this.value);
136+
builder.field("flags_value", flagsValue);
137+
builder.field("max_determinized_states", maxDeterminizedStates);
92138
if (rewrite != null) {
93139
builder.field("rewrite", rewrite);
94140
}
@@ -101,4 +147,67 @@ public void doXContent(XContentBuilder builder, Params params) throws IOExceptio
101147
public String getName() {
102148
return NAME;
103149
}
150+
151+
@Override
152+
public Query doToQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
153+
MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), rewrite, null);
154+
155+
Query query = null;
156+
MappedFieldType fieldType = parseContext.fieldMapper(fieldName);
157+
if (fieldType != null) {
158+
query = fieldType.regexpQuery(value, flagsValue, maxDeterminizedStates, method, parseContext);
159+
}
160+
if (query == null) {
161+
RegexpQuery regexpQuery = new RegexpQuery(new Term(fieldName, BytesRefs.toBytesRef(value)), flagsValue, maxDeterminizedStates);
162+
if (method != null) {
163+
regexpQuery.setRewriteMethod(method);
164+
}
165+
query = regexpQuery;
166+
}
167+
return query;
168+
}
169+
170+
@Override
171+
public QueryValidationException validate() {
172+
QueryValidationException validationException = null;
173+
if (Strings.isEmpty(this.fieldName)) {
174+
validationException = addValidationError("field name cannot be null or empty.", validationException);
175+
}
176+
if (this.value == null) {
177+
validationException = addValidationError("query text cannot be null", validationException);
178+
}
179+
return validationException;
180+
}
181+
182+
@Override
183+
public RegexpQueryBuilder doReadFrom(StreamInput in) throws IOException {
184+
RegexpQueryBuilder regexpQueryBuilder = new RegexpQueryBuilder(in.readString(), in.readString());
185+
regexpQueryBuilder.flagsValue = in.readVInt();
186+
regexpQueryBuilder.maxDeterminizedStates = in.readVInt();
187+
regexpQueryBuilder.rewrite = in.readOptionalString();
188+
return regexpQueryBuilder;
189+
}
190+
191+
@Override
192+
public void doWriteTo(StreamOutput out) throws IOException {
193+
out.writeString(fieldName);
194+
out.writeString(value);
195+
out.writeVInt(flagsValue);
196+
out.writeVInt(maxDeterminizedStates);
197+
out.writeOptionalString(rewrite);
198+
}
199+
200+
@Override
201+
public int doHashCode() {
202+
return Objects.hash(fieldName, value, flagsValue, maxDeterminizedStates, rewrite);
203+
}
204+
205+
@Override
206+
public boolean doEquals(RegexpQueryBuilder other) {
207+
return Objects.equals(fieldName, other.fieldName) &&
208+
Objects.equals(value, other.value) &&
209+
Objects.equals(flagsValue, other.flagsValue) &&
210+
Objects.equals(maxDeterminizedStates, other.maxDeterminizedStates) &&
211+
Objects.equals(rewrite, other.rewrite);
212+
}
104213
}

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

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,12 @@
1919

2020
package org.elasticsearch.index.query;
2121

22-
import org.apache.lucene.index.Term;
23-
import org.apache.lucene.search.MultiTermQuery;
24-
import org.apache.lucene.search.Query;
25-
import org.apache.lucene.search.RegexpQuery;
26-
import org.apache.lucene.util.automaton.Operations;
2722
import org.elasticsearch.common.inject.Inject;
28-
import org.elasticsearch.common.lucene.BytesRefs;
2923
import org.elasticsearch.common.xcontent.XContentParser;
30-
import org.elasticsearch.index.mapper.MappedFieldType;
31-
import org.elasticsearch.index.query.support.QueryParsers;
3224

3325
import java.io.IOException;
3426

35-
/**
36-
*
37-
*/
38-
public class RegexpQueryParser extends BaseQueryParserTemp {
27+
public class RegexpQueryParser extends BaseQueryParser {
3928

4029
public static final int DEFAULT_FLAGS_VALUE = RegexpFlag.ALL.value();
4130

@@ -49,16 +38,16 @@ public String[] names() {
4938
}
5039

5140
@Override
52-
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
41+
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
5342
XContentParser parser = parseContext.parser();
5443

5544
String fieldName = parser.currentName();
56-
String rewriteMethod = null;
45+
String rewrite = null;
5746

5847
String value = null;
5948
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
60-
int flagsValue = DEFAULT_FLAGS_VALUE;
61-
int maxDeterminizedStates = Operations.DEFAULT_MAX_DETERMINIZED_STATES;
49+
int flagsValue = RegexpQueryBuilder.DEFAULT_FLAGS_VALUE;
50+
int maxDeterminizedStates = RegexpQueryBuilder.DEFAULT_MAX_DETERMINIZED_STATES;
6251
String queryName = null;
6352
String currentFieldName = null;
6453
XContentParser.Token token;
@@ -78,7 +67,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
7867
} else if ("boost".equals(currentFieldName)) {
7968
boost = parser.floatValue();
8069
} else if ("rewrite".equals(currentFieldName)) {
81-
rewriteMethod = parser.textOrNull();
70+
rewrite = parser.textOrNull();
8271
} else if ("flags".equals(currentFieldName)) {
8372
String flags = parser.textOrNull();
8473
flagsValue = RegexpFlag.resolveValue(flags);
@@ -106,32 +95,16 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
10695
if (value == null) {
10796
throw new QueryParsingException(parseContext, "No value specified for regexp query");
10897
}
109-
110-
MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), rewriteMethod, null);
111-
112-
Query query = null;
113-
MappedFieldType fieldType = parseContext.fieldMapper(fieldName);
114-
if (fieldType != null) {
115-
query = fieldType.regexpQuery(value, flagsValue, maxDeterminizedStates, method, parseContext);
116-
}
117-
if (query == null) {
118-
RegexpQuery regexpQuery = new RegexpQuery(new Term(fieldName, BytesRefs.toBytesRef(value)), flagsValue, maxDeterminizedStates);
119-
if (method != null) {
120-
regexpQuery.setRewriteMethod(method);
121-
}
122-
query = regexpQuery;
123-
}
124-
query.setBoost(boost);
125-
if (queryName != null) {
126-
parseContext.addNamedQuery(queryName, query);
127-
}
128-
return query;
98+
return new RegexpQueryBuilder(fieldName, value)
99+
.flags(flagsValue)
100+
.maxDeterminizedStates(maxDeterminizedStates)
101+
.rewrite(rewrite)
102+
.boost(boost)
103+
.queryName(queryName);
129104
}
130105

131106
@Override
132107
public RegexpQueryBuilder getBuilderPrototype() {
133108
return RegexpQueryBuilder.PROTOTYPE;
134109
}
135-
136-
137110
}

core/src/main/java/org/elasticsearch/index/query/support/QueryParsers.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
*/
3030
public final class QueryParsers {
3131

32-
private static final ParseField CONSTANT_SCORE = new ParseField("constant_score", "constant_score_auto", "constant_score_filter");
33-
private static final ParseField SCORING_BOOLEAN = new ParseField("scoring_boolean");
34-
private static final ParseField CONSTANT_SCORE_BOOLEAN = new ParseField("constant_score_boolean");
35-
private static final ParseField TOP_TERMS = new ParseField("top_terms_");
36-
private static final ParseField TOP_TERMS_BOOST = new ParseField("top_terms_boost_");
37-
private static final ParseField TOP_TERMS_BLENDED_FREQS = new ParseField("top_terms_blended_freqs_");
32+
public static final ParseField CONSTANT_SCORE = new ParseField("constant_score", "constant_score_auto", "constant_score_filter");
33+
public static final ParseField SCORING_BOOLEAN = new ParseField("scoring_boolean");
34+
public static final ParseField CONSTANT_SCORE_BOOLEAN = new ParseField("constant_score_boolean");
35+
public static final ParseField TOP_TERMS = new ParseField("top_terms_");
36+
public static final ParseField TOP_TERMS_BOOST = new ParseField("top_terms_boost_");
37+
public static final ParseField TOP_TERMS_BLENDED_FREQS = new ParseField("top_terms_blended_freqs_");
3838

3939
private QueryParsers() {
4040

core/src/test/java/org/elasticsearch/index/query/BaseQueryTestCase.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.cluster.ClusterService;
2626
import org.elasticsearch.cluster.metadata.IndexMetaData;
2727
import org.elasticsearch.cluster.metadata.MetaData;
28+
import org.elasticsearch.common.ParseField;
2829
import org.elasticsearch.common.compress.CompressedXContent;
2930
import org.elasticsearch.common.inject.AbstractModule;
3031
import org.elasticsearch.common.inject.Injector;
@@ -46,6 +47,7 @@
4647
import org.elasticsearch.index.cache.IndexCacheModule;
4748
import org.elasticsearch.index.mapper.MapperService;
4849
import org.elasticsearch.index.query.functionscore.FunctionScoreModule;
50+
import org.elasticsearch.index.query.support.QueryParsers;
4951
import org.elasticsearch.index.settings.IndexSettingsModule;
5052
import org.elasticsearch.index.similarity.SimilarityModule;
5153
import org.elasticsearch.indices.breaker.CircuitBreakerService;
@@ -58,20 +60,11 @@
5860
import org.elasticsearch.test.VersionUtils;
5961
import org.elasticsearch.threadpool.ThreadPool;
6062
import org.elasticsearch.threadpool.ThreadPoolModule;
61-
import org.junit.After;
62-
import org.junit.AfterClass;
63-
import org.junit.Before;
64-
import org.junit.BeforeClass;
65-
import org.junit.Ignore;
66-
import org.junit.Test;
63+
import org.junit.*;
6764

6865
import java.io.IOException;
6966

70-
import static org.hamcrest.Matchers.equalTo;
71-
import static org.hamcrest.Matchers.instanceOf;
72-
import static org.hamcrest.Matchers.is;
73-
import static org.hamcrest.Matchers.notNullValue;
74-
import static org.hamcrest.Matchers.nullValue;
67+
import static org.hamcrest.Matchers.*;
7568

7669
@Ignore
7770
public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ElasticsearchTestCase {
@@ -338,4 +331,21 @@ protected static Object randomValueForField(String fieldName) {
338331
}
339332
return value;
340333
}
334+
335+
/**
336+
* Helper method to return a random rewrite method
337+
*/
338+
protected static String getRandomRewriteMethod() {
339+
String rewrite;
340+
if (randomBoolean()) {
341+
rewrite = randomFrom(new ParseField[]{QueryParsers.CONSTANT_SCORE,
342+
QueryParsers.SCORING_BOOLEAN,
343+
QueryParsers.CONSTANT_SCORE_BOOLEAN}).getPreferredName();
344+
} else {
345+
rewrite = randomFrom(new ParseField[]{QueryParsers.TOP_TERMS,
346+
QueryParsers.TOP_TERMS_BOOST,
347+
QueryParsers.TOP_TERMS_BLENDED_FREQS}).getPreferredName() + "1";
348+
}
349+
return rewrite;
350+
}
341351
}

0 commit comments

Comments
 (0)