From b315e485a5862736891f95f53f940025990c7322 Mon Sep 17 00:00:00 2001 From: Alex Ksikes Date: Sat, 4 Jul 2015 17:28:02 +0200 Subject: [PATCH] Refactors PrefixQuery Relates to #10217 Closes #12032 This PR is against the query-refactoring branch. --- .../index/query/BaseTermQueryBuilder.java | 2 +- .../index/query/PrefixQueryBuilder.java | 103 ++++++++++++++++-- .../index/query/PrefixQueryParser.java | 43 ++------ .../index/query/PrefixQueryBuilderTest.java | 87 +++++++++++++++ 4 files changed, 190 insertions(+), 45 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTest.java diff --git a/core/src/main/java/org/elasticsearch/index/query/BaseTermQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BaseTermQueryBuilder.java index c178335e9c03c..64441845dde8d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BaseTermQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BaseTermQueryBuilder.java @@ -147,7 +147,7 @@ public QueryValidationException validate() { @Override protected final int doHashCode() { - return Objects.hash(getClass(), fieldName, value); + return Objects.hash(fieldName, value); } @Override diff --git a/core/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java index 5a1d8b4cc50b5..65c608a63dd1b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.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.PrefixQuery; +import org.apache.lucene.search.Query; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.lucene.BytesRefs; 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; /** * A Query that matches documents containing terms with a specified prefix. @@ -30,10 +41,10 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder public static final String NAME = "prefix"; - private final String name; - - private final String prefix; - + private final String fieldName; + + private final String value; + private String rewrite; static final PrefixQueryBuilder PROTOTYPE = new PrefixQueryBuilder(null, null); @@ -41,12 +52,20 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder /** * A Query that matches documents containing terms with a specified prefix. * - * @param name The name of the field - * @param prefix The prefix query + * @param fieldName The name of the field + * @param value The prefix query */ - public PrefixQueryBuilder(String name, String prefix) { - this.name = name; - this.prefix = prefix; + public PrefixQueryBuilder(String fieldName, String value) { + this.fieldName = fieldName; + this.value = value; + } + + public String fieldName() { + return this.fieldName; + } + + public String value() { + return this.value; } public PrefixQueryBuilder rewrite(String rewrite) { @@ -54,11 +73,15 @@ public PrefixQueryBuilder rewrite(String rewrite) { return this; } + public String rewrite() { + return this.rewrite; + } + @Override public void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(NAME); - builder.startObject(name); - builder.field("prefix", prefix); + builder.startObject(fieldName); + builder.field("prefix", this.value); if (rewrite != null) { builder.field("rewrite", rewrite); } @@ -71,4 +94,62 @@ public void doXContent(XContentBuilder builder, Params params) throws IOExceptio public String getName() { return NAME; } + + @Override + protected Query doToQuery(QueryParseContext parseContext) throws IOException { + MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), rewrite, null); + + Query query = null; + MappedFieldType fieldType = parseContext.fieldMapper(fieldName); + if (fieldType != null) { + query = fieldType.prefixQuery(value, method, parseContext); + } + if (query == null) { + PrefixQuery prefixQuery = new PrefixQuery(new Term(fieldName, BytesRefs.toBytesRef(value))); + if (method != null) { + prefixQuery.setRewriteMethod(method); + } + query = prefixQuery; + } + + 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("query text cannot be null", validationException); + } + return validationException; + } + + @Override + protected PrefixQueryBuilder doReadFrom(StreamInput in) throws IOException { + PrefixQueryBuilder prefixQueryBuilder = new PrefixQueryBuilder(in.readString(), in.readString()); + prefixQueryBuilder.rewrite = in.readOptionalString(); + return prefixQueryBuilder; + } + + @Override + protected void doWriteTo(StreamOutput out) throws IOException { + out.writeString(fieldName); + out.writeString(value); + out.writeOptionalString(rewrite); + } + + @Override + protected final int doHashCode() { + return Objects.hash(fieldName, value, rewrite); + } + + @Override + protected boolean doEquals(PrefixQueryBuilder 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/PrefixQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/PrefixQueryParser.java index 47bbc3cfff483..47d52fae69710 100644 --- a/core/src/main/java/org/elasticsearch/index/query/PrefixQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/PrefixQueryParser.java @@ -19,22 +19,15 @@ package org.elasticsearch.index.query; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.MultiTermQuery; -import org.apache.lucene.search.PrefixQuery; -import org.apache.lucene.search.Query; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.lucene.BytesRefs; 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 PrefixQueryParser extends BaseQueryParserTemp { +public class PrefixQueryParser extends BaseQueryParser { @Inject public PrefixQueryParser() { @@ -46,14 +39,14 @@ public String[] names() { } @Override - public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException { + public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException { XContentParser parser = parseContext.parser(); String fieldName = parser.currentName(); - String rewriteMethod = null; - String queryName = null; - String value = null; + String rewrite = null; + + String queryName = null; float boost = AbstractQueryBuilder.DEFAULT_BOOST; String currentFieldName = null; XContentParser.Token token; @@ -75,7 +68,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 { throw new QueryParsingException(parseContext, "[regexp] query does not support [" + currentFieldName + "]"); } @@ -94,26 +87,10 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars if (value == null) { throw new QueryParsingException(parseContext, "No value specified for prefix query"); } - - MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), rewriteMethod, null); - - Query query = null; - MappedFieldType fieldType = parseContext.fieldMapper(fieldName); - if (fieldType != null) { - query = fieldType.prefixQuery(value, method, parseContext); - } - if (query == null) { - PrefixQuery prefixQuery = new PrefixQuery(new Term(fieldName, BytesRefs.toBytesRef(value))); - if (method != null) { - prefixQuery.setRewriteMethod(method); - } - query = prefixQuery; - } - query.setBoost(boost); - if (queryName != null) { - parseContext.addNamedQuery(queryName, query); - } - return query; + return new PrefixQueryBuilder(fieldName, value) + .rewrite(rewrite) + .boost(boost) + .queryName(queryName); } @Override diff --git a/core/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTest.java b/core/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTest.java new file mode 100644 index 0000000000000..c02394012b407 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTest.java @@ -0,0 +1,87 @@ +/* + * 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.PrefixQuery; +import org.apache.lucene.search.Query; +import org.elasticsearch.common.ParseFieldMatcher; +import org.elasticsearch.common.lucene.BytesRefs; +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 PrefixQueryBuilderTest extends BaseQueryTestCase { + + @Override + protected PrefixQueryBuilder doCreateTestQueryBuilder() { + String fieldName = randomBoolean() ? STRING_FIELD_NAME : randomAsciiOfLengthBetween(1, 10); + String value = randomAsciiOfLengthBetween(1, 10); + PrefixQueryBuilder query = new PrefixQueryBuilder(fieldName, value); + + if (randomBoolean()) { + query.rewrite(getRandomRewriteMethod()); + } + return query; + } + + @Override + protected Query doCreateExpectedQuery(PrefixQueryBuilder queryBuilder, QueryParseContext context) throws IOException { + //norelease fix to be removed to avoid NPE on unmapped fields (Dtests.seed=BF5D7566DECBC5B1) + context.parseFieldMatcher(randomBoolean() ? ParseFieldMatcher.EMPTY : ParseFieldMatcher.STRICT); + + MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(context.parseFieldMatcher(), queryBuilder.rewrite(), null); + + Query query = null; + MappedFieldType fieldType = context.fieldMapper(queryBuilder.fieldName()); + if (fieldType != null) { + query = fieldType.prefixQuery(queryBuilder.value(), method, context); + } + if (query == null) { + PrefixQuery prefixQuery = new PrefixQuery(new Term(queryBuilder.fieldName(), BytesRefs.toBytesRef(queryBuilder.value()))); + if (method != null) { + prefixQuery.setRewriteMethod(method); + } + query = prefixQuery; + } + + return query; + } + + @Test + public void testValidate() { + PrefixQueryBuilder prefixQueryBuilder = new PrefixQueryBuilder("", "prefix"); + assertThat(prefixQueryBuilder.validate().validationErrors().size(), is(1)); + + prefixQueryBuilder = new PrefixQueryBuilder("field", null); + assertThat(prefixQueryBuilder.validate().validationErrors().size(), is(1)); + + prefixQueryBuilder = new PrefixQueryBuilder("field", "prefix"); + assertNull(prefixQueryBuilder.validate()); + + prefixQueryBuilder = new PrefixQueryBuilder(null, null); + assertThat(prefixQueryBuilder.validate().validationErrors().size(), is(2)); + } +} \ No newline at end of file