diff --git a/core/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java index d142e3520801e..1320fe9c1a0f3 100644 --- a/core/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java @@ -19,9 +19,20 @@ package org.elasticsearch.index.query; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.WildcardQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.query.support.QueryParsers; import java.io.IOException; +import java.util.Objects; /** * Implements the wildcard search query. Supported wildcards are *, which @@ -35,9 +46,9 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder* or * ?. * - * @param name The field name - * @param wildcard The wildcard query string + * @param fieldName The field name + * @param value The wildcard query string */ - public WildcardQueryBuilder(String name, String wildcard) { - this.name = name; - this.wildcard = wildcard; + public WildcardQueryBuilder(String fieldName, String value) { + this.fieldName = fieldName; + this.value = value; + } + + public String fieldName() { + return fieldName; + } + + public String value() { + return value; } public WildcardQueryBuilder rewrite(String rewrite) { @@ -64,11 +83,20 @@ public WildcardQueryBuilder rewrite(String rewrite) { return this; } + public String rewrite() { + return this.rewrite; + } + + @Override + public String getName() { + return NAME; + } + @Override public void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(NAME); - builder.startObject(name); - builder.field("wildcard", wildcard); + builder.startObject(fieldName); + builder.field("wildcard", value); if (rewrite != null) { builder.field("rewrite", rewrite); } @@ -78,7 +106,60 @@ public void doXContent(XContentBuilder builder, Params params) throws IOExceptio } @Override - public String getName() { - return NAME; + protected Query doToQuery(QueryParseContext parseContext) throws IOException { + String indexFieldName; + BytesRef valueBytes; + + MappedFieldType fieldType = parseContext.fieldMapper(fieldName); + if (fieldType != null) { + indexFieldName = fieldType.names().indexName(); + valueBytes = fieldType.indexedValueForSearch(value); + } else { + indexFieldName = fieldName; + valueBytes = new BytesRef(value); + } + + WildcardQuery query = new WildcardQuery(new Term(indexFieldName, valueBytes)); + MultiTermQuery.RewriteMethod rewriteMethod = QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), rewrite, null); + QueryParsers.setRewriteMethod(query, rewriteMethod); + return query; + } + + @Override + public QueryValidationException validate() { + QueryValidationException validationException = null; + if (Strings.isEmpty(this.fieldName)) { + validationException = addValidationError("field name cannot be null or empty.", validationException); + } + if (this.value == null) { + validationException = addValidationError("wildcard cannot be null", validationException); + } + return validationException; + } + + @Override + protected WildcardQueryBuilder doReadFrom(StreamInput in) throws IOException { + WildcardQueryBuilder wildcardQueryBuilder = new WildcardQueryBuilder(in.readString(), in.readString()); + wildcardQueryBuilder.rewrite = in.readOptionalString(); + return wildcardQueryBuilder; + } + + @Override + protected void doWriteTo(StreamOutput out) throws IOException { + out.writeString(fieldName); + out.writeString(value); + out.writeOptionalString(rewrite); + } + + @Override + protected int doHashCode() { + return Objects.hash(fieldName, value, rewrite); + } + + @Override + protected boolean doEquals(WildcardQueryBuilder other) { + return Objects.equals(fieldName, other.fieldName) && + Objects.equals(value, other.value) && + Objects.equals(rewrite, other.rewrite); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/WildcardQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/WildcardQueryParser.java index 2ac10b80be38d..72330abb882b2 100644 --- a/core/src/main/java/org/elasticsearch/index/query/WildcardQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/WildcardQueryParser.java @@ -19,21 +19,15 @@ package org.elasticsearch.index.query; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.WildcardQuery; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.query.support.QueryParsers; import java.io.IOException; /** * */ -public class WildcardQueryParser extends BaseQueryParserTemp { +public class WildcardQueryParser extends BaseQueryParser { @Inject public WildcardQueryParser() { @@ -45,7 +39,7 @@ public String[] names() { } @Override - public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException { + public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException { XContentParser parser = parseContext.parser(); XContentParser.Token token = parser.nextToken(); @@ -53,7 +47,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars throw new QueryParsingException(parseContext, "[wildcard] query malformed, no field"); } String fieldName = parser.currentName(); - String rewriteMethod = null; + String rewrite = null; String value = null; float boost = AbstractQueryBuilder.DEFAULT_BOOST; @@ -72,7 +66,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars } else if ("boost".equals(currentFieldName)) { boost = parser.floatValue(); } else if ("rewrite".equals(currentFieldName)) { - rewriteMethod = parser.textOrNull(); + rewrite = parser.textOrNull(); } else if ("_name".equals(currentFieldName)) { queryName = parser.text(); } else { @@ -89,23 +83,10 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars if (value == null) { throw new QueryParsingException(parseContext, "No value specified for prefix query"); } - - BytesRef valueBytes; - MappedFieldType fieldType = parseContext.fieldMapper(fieldName); - if (fieldType != null) { - fieldName = fieldType.names().indexName(); - valueBytes = fieldType.indexedValueForSearch(value); - } else { - valueBytes = new BytesRef(value); - } - - WildcardQuery wildcardQuery = new WildcardQuery(new Term(fieldName, valueBytes)); - QueryParsers.setRewriteMethod(wildcardQuery, parseContext.parseFieldMatcher(), rewriteMethod); - wildcardQuery.setBoost(boost); - if (queryName != null) { - parseContext.addNamedQuery(queryName, wildcardQuery); - } - return wildcardQuery; + return new WildcardQueryBuilder(fieldName, value) + .rewrite(rewrite) + .boost(boost) + .queryName(queryName); } @Override diff --git a/core/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTest.java b/core/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTest.java new file mode 100644 index 0000000000000..dc3c6789f5ff4 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.query; + +import org.apache.lucene.index.Term; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.WildcardQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.ParseFieldMatcher; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.query.support.QueryParsers; +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.Matchers.is; + +public class WildcardQueryBuilderTest extends BaseQueryTestCase { + + @Override + protected WildcardQueryBuilder doCreateTestQueryBuilder() { + WildcardQueryBuilder query; + + // mapped or unmapped field + String text = randomAsciiOfLengthBetween(1, 10); + if (randomBoolean()) { + query = new WildcardQueryBuilder(STRING_FIELD_NAME, text); + } else { + query = new WildcardQueryBuilder(randomAsciiOfLengthBetween(1, 10), text); + } + if (randomBoolean()) { + query.rewrite(randomFrom(getRandomRewriteMethod())); + } + return query; + } + + @Override + protected Query doCreateExpectedQuery(WildcardQueryBuilder queryBuilder, QueryParseContext context) throws IOException { + String indexFieldName; + BytesRef valueBytes; + + MappedFieldType fieldType = context.fieldMapper(queryBuilder.fieldName()); + if (fieldType != null) { + indexFieldName = fieldType.names().indexName(); + valueBytes = fieldType.indexedValueForSearch(queryBuilder.value()); + } else { + indexFieldName = queryBuilder.fieldName(); + valueBytes = new BytesRef(queryBuilder.value()); + } + + WildcardQuery expectedQuery = new WildcardQuery(new Term(indexFieldName, valueBytes)); + + //norelease fix to be removed to avoid NPE on unmapped fields + context.parseFieldMatcher(randomBoolean() ? ParseFieldMatcher.EMPTY : ParseFieldMatcher.STRICT); + MultiTermQuery.RewriteMethod rewriteMethod = QueryParsers.parseRewriteMethod(context.parseFieldMatcher(), queryBuilder.rewrite(), null); + QueryParsers.setRewriteMethod(expectedQuery, rewriteMethod); + return expectedQuery; + } + + @Test + public void testValidate() { + WildcardQueryBuilder wildcardQueryBuilder = new WildcardQueryBuilder("", "text"); + assertThat(wildcardQueryBuilder.validate().validationErrors().size(), is(1)); + + wildcardQueryBuilder = new WildcardQueryBuilder("field", null); + assertThat(wildcardQueryBuilder.validate().validationErrors().size(), is(1)); + + wildcardQueryBuilder = new WildcardQueryBuilder(null, null); + assertThat(wildcardQueryBuilder.validate().validationErrors().size(), is(2)); + + wildcardQueryBuilder = new WildcardQueryBuilder("field", "text"); + assertNull(wildcardQueryBuilder.validate()); + } + + @Test + public void testEmptyValue() throws IOException { + QueryParseContext context = createContext(); + context.setAllowUnmappedFields(true); + + WildcardQueryBuilder wildcardQueryBuilder = new WildcardQueryBuilder(getRandomType(), ""); + assertEquals(wildcardQueryBuilder.toQuery(context).getClass(), WildcardQuery.class); + } +}