Query refactoring: FieldMaskingSpanQueryBuilder 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
Closes #11717
This commit is contained in:
Christoph Büscher 2015-06-16 17:58:49 +02:00
parent fabe91633f
commit 107635b194
3 changed files with 214 additions and 36 deletions

View File

@ -19,10 +19,15 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.spans.FieldMaskingSpanQuery;
import org.apache.lucene.search.spans.SpanQuery;
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 java.io.IOException;
import java.util.Objects;
public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMaskingSpanQueryBuilder> implements SpanQueryBuilder<FieldMaskingSpanQueryBuilder>, BoostableQueryBuilder<FieldMaskingSpanQueryBuilder> {
@ -30,17 +35,37 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
private final SpanQueryBuilder queryBuilder;
private final String field;
private final String fieldName;
private float boost = -1;
private float boost = 1.0f;
private String queryName;
static final FieldMaskingSpanQueryBuilder PROTOTYPE = new FieldMaskingSpanQueryBuilder(null, null);
public FieldMaskingSpanQueryBuilder(SpanQueryBuilder queryBuilder, String field) {
/**
* Constructs a new {@link FieldMaskingSpanQueryBuilder} given an inner {@link SpanQueryBuilder} for
* a given field
* @param queryBuilder inner {@link SpanQueryBuilder}
* @param fieldName the field name
*/
public FieldMaskingSpanQueryBuilder(SpanQueryBuilder queryBuilder, String fieldName) {
this.queryBuilder = queryBuilder;
this.field = field;
this.fieldName = fieldName;
}
/**
* @return the field name for this query
*/
public String fieldName() {
return this.fieldName;
}
/**
* @return the inner {@link QueryBuilder}
*/
public SpanQueryBuilder innerQuery() {
return this.queryBuilder;
}
@Override
@ -49,6 +74,13 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
return this;
}
/**
* @return the boost factor for this query
*/
public float boost() {
return this.boost;
}
/**
* Sets the query name for the filter that can be used when searching for matched_filters per hit.
*/
@ -57,15 +89,20 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
return this;
}
/**
* @return the query name for this query
*/
public String queryName() {
return this.queryName;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("field", field);
if (boost != -1) {
builder.field("boost", boost);
}
builder.field("field", fieldName);
builder.field("boost", boost);
if (queryName != null) {
builder.field("_name", queryName);
}
@ -73,13 +110,73 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
}
@Override
public String getName() {
return NAME;
public SpanQuery toQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
String fieldInQuery = this.fieldName;
MappedFieldType fieldType = parseContext.fieldMapper(fieldName);
if (fieldType != null) {
fieldInQuery = fieldType.names().indexName();
}
SpanQuery innerQuery = this.queryBuilder.toQuery(parseContext);
FieldMaskingSpanQuery query = new FieldMaskingSpanQuery(innerQuery, fieldInQuery);
query.setBoost(boost);
if (queryName != null) {
parseContext.addNamedQuery(queryName, query);
}
return query;
}
@Override
public SpanQuery toQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
//norelease just a temporary implementation, will go away once this query is refactored and properly overrides toQuery
return (SpanQuery)super.toQuery(parseContext);
public QueryValidationException validate() {
QueryValidationException validationExceptions = null;
if (queryBuilder == null) {
validationExceptions = QueryValidationException.addValidationError("[field_masking_span] must have inner span query clause", validationExceptions);
}
if (fieldName == null || fieldName.isEmpty()) {
validationExceptions = QueryValidationException.addValidationError("[field_masking_span] must have set field name", validationExceptions);
}
return validationExceptions;
}
@Override
public FieldMaskingSpanQueryBuilder readFrom(StreamInput in) throws IOException {
QueryBuilder innerQueryBuilder = in.readNamedWriteable();
FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder((SpanQueryBuilder) innerQueryBuilder, in.readString());
queryBuilder.queryName = in.readOptionalString();
queryBuilder.boost = in.readFloat();
return queryBuilder;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeNamedWriteable(this.queryBuilder);
out.writeString(this.fieldName);
out.writeOptionalString(queryName);
out.writeFloat(boost);
}
@Override
public int hashCode() {
return Objects.hash(queryBuilder, fieldName, boost, queryName);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
FieldMaskingSpanQueryBuilder other = (FieldMaskingSpanQueryBuilder) obj;
return Objects.equals(queryBuilder, other.queryBuilder) &&
Objects.equals(fieldName, other.fieldName) &&
Objects.equals(boost, other.boost) &&
Objects.equals(queryName, other.queryName);
}
@Override
public String getName() {
return NAME;
}
}

View File

@ -19,21 +19,15 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.FieldMaskingSpanQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
/**
*
*/
public class FieldMaskingSpanQueryParser extends BaseQueryParserTemp {
public class FieldMaskingSpanQueryParser extends BaseQueryParser {
@Inject
public FieldMaskingSpanQueryParser() {
@ -45,12 +39,12 @@ public class FieldMaskingSpanQueryParser extends BaseQueryParserTemp {
}
@Override
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();
float boost = 1.0f;
SpanQuery inner = null;
SpanQueryBuilder inner = null;
String field = null;
String queryName = null;
@ -61,11 +55,11 @@ public class FieldMaskingSpanQueryParser extends BaseQueryParserTemp {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("query".equals(currentFieldName)) {
Query query = parseContext.parseInnerQuery();
if (!(query instanceof SpanQuery)) {
QueryBuilder query = parseContext.parseInnerQueryBuilder();
if (!(query instanceof SpanQueryBuilder)) {
throw new QueryParsingException(parseContext, "[field_masking_span] query] must be of type span query");
}
inner = (SpanQuery) query;
inner = (SpanQueryBuilder) query;
} else {
throw new QueryParsingException(parseContext, "[field_masking_span] query does not support ["
+ currentFieldName + "]");
@ -89,17 +83,10 @@ public class FieldMaskingSpanQueryParser extends BaseQueryParserTemp {
throw new QueryParsingException(parseContext, "field_masking_span must have [field] set for it");
}
MappedFieldType fieldType = parseContext.fieldMapper(field);
if (fieldType != null) {
field = fieldType.names().indexName();
}
FieldMaskingSpanQuery query = new FieldMaskingSpanQuery(inner, field);
query.setBoost(boost);
if (queryName != null) {
parseContext.addNamedQuery(queryName, query);
}
return query;
FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder(inner, field);
queryBuilder.boost(boost);
queryBuilder.queryName(queryName);
return queryBuilder;
}
@Override

View File

@ -0,0 +1,94 @@
/*
* 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.search.Query;
import org.apache.lucene.search.spans.FieldMaskingSpanQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.equalTo;
public class FieldMaskingSpanQueryBuilderTest extends BaseQueryTestCase<FieldMaskingSpanQueryBuilder> {
@Override
protected Query createExpectedQuery(FieldMaskingSpanQueryBuilder testQueryBuilder, QueryParseContext context) throws IOException {
String fieldInQuery = testQueryBuilder.fieldName();
MappedFieldType fieldType = context.fieldMapper(fieldInQuery);
if (fieldType != null) {
fieldInQuery = fieldType.names().indexName();
}
SpanQuery innerQuery = testQueryBuilder.innerQuery().toQuery(context);
Query expectedQuery = new FieldMaskingSpanQuery(innerQuery, fieldInQuery);
expectedQuery.setBoost(testQueryBuilder.boost());
if (testQueryBuilder.queryName() != null) {
context.addNamedQuery(testQueryBuilder.queryName(), expectedQuery);
}
return expectedQuery;
}
@Override
protected void assertLuceneQuery(FieldMaskingSpanQueryBuilder queryBuilder, Query query, QueryParseContext context) {
if (queryBuilder.queryName() != null) {
Query namedQuery = context.copyNamedFilters().get(queryBuilder.queryName());
assertThat(namedQuery, equalTo(query));
}
}
@Override
protected FieldMaskingSpanQueryBuilder createTestQueryBuilder() {
String fieldName = null;
if (randomBoolean()) {
fieldName = randomFrom(mappedFieldNames);
} else {
fieldName = randomAsciiOfLengthBetween(1, 10);
}
SpanTermQueryBuilder innerQuery = new SpanTermQueryBuilderTest().createTestQueryBuilder();
FieldMaskingSpanQueryBuilder query = new FieldMaskingSpanQueryBuilder(innerQuery, fieldName);
if (randomBoolean()) {
query.boost(2.0f / randomIntBetween(1, 20));
}
if (randomBoolean()) {
query.queryName(randomAsciiOfLengthBetween(1, 10));
}
return query;
}
@Test
public void testValidate() {
FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder(new SpanTermQueryBuilder("name", "value"), "fieldName");
assertNull(queryBuilder.validate());
queryBuilder = new FieldMaskingSpanQueryBuilder(null, "fieldName");
assertThat(queryBuilder.validate().validationErrors().size(), is(1));
queryBuilder = new FieldMaskingSpanQueryBuilder(null, "");
assertThat(queryBuilder.validate().validationErrors().size(), is(2));
queryBuilder = new FieldMaskingSpanQueryBuilder(null, null);
assertThat(queryBuilder.validate().validationErrors().size(), is(2));
}
}