Skip to content

Commit

Permalink
Refactors PrefixQuery
Browse files Browse the repository at this point in the history
Relates to elastic#10217
Closes elastic#12032

This PR is against the query-refactoring branch.
  • Loading branch information
alexksikes committed Jul 24, 2015
1 parent c01eecc commit b315e48
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public QueryValidationException validate() {

@Override
protected final int doHashCode() {
return Objects.hash(getClass(), fieldName, value);
return Objects.hash(fieldName, value);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -30,35 +41,47 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>

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);

/**
* 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) {
this.rewrite = 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);
}
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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;
Expand All @@ -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 + "]");
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<PrefixQueryBuilder> {

@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));
}
}

0 comments on commit b315e48

Please sign in to comment.