Throw a parsing exception when boost is set in span_or query (#28390) (#34112)

This commit is contained in:
Christophe Bismuth 2018-11-26 18:15:59 +01:00 committed by Mayya Sharipova
parent 0baffda390
commit b95a4db6e6
18 changed files with 474 additions and 49 deletions

View File

@ -18,12 +18,14 @@
now include entire geohash cell, instead of just geohash center.
* Attempts to generate multi-term phrase queries against non-text fields
with a custom analyzer will now throw an exception
with a custom analyzer will now throw an exception.
* An `envelope` crossing the dateline in a `geo_shape `query is now processed
correctly when specified using REST API instead of having its left and
right corners flipped.
* Attempts to set `boost` on inner span queries will now throw a parsing exception.
[float]
==== Adaptive replica selection enabled by default
@ -156,4 +158,4 @@ To deboost a specific query you can use a `boost` comprise between 0 and 1.
Negative scores in the Function Score Query are deprecated in 6.x, and are
not allowed in this version. If a negative score is produced as a result
of computation (e.g. in `script_score` or `field_value_factor` functions),
an error will be thrown.
an error will be thrown.

View File

@ -5,6 +5,12 @@ Span queries are low-level positional queries which provide expert control
over the order and proximity of the specified terms. These are typically used
to implement very specific queries on legal documents or patents.
It is only allowed to set boost on an outer span query. Compound span queries,
like span_near, only use the list of matching spans of inner span queries in
order to find their own spans, which they then use to produce a score. Scores
are never computed on inner span queries, which is the reason why boosts are not
allowed: they only influence the way scores are computed, not spans.
Span queries cannot be mixed with non-span queries (with the exception of the `span_multi` query).
The queries in this group are:
@ -67,4 +73,4 @@ include::span-containing-query.asciidoc[]
include::span-within-query.asciidoc[]
include::span-field-masking-query.asciidoc[]
include::span-field-masking-query.asciidoc[]

View File

@ -32,6 +32,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
/**
* Builder for {@link org.apache.lucene.search.spans.SpanContainingQuery}.
*/
@ -117,12 +119,14 @@ public class SpanContainingQueryBuilder extends AbstractQueryBuilder<SpanContain
throw new ParsingException(parser.getTokenLocation(), "span_containing [big] must be of type span query");
}
big = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, big);
} else if (LITTLE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "span_containing [little] must be of type span query");
}
little = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, little);
} else {
throw new ParsingException(parser.getTokenLocation(),
"[span_containing] query does not support [" + currentFieldName + "]");

View File

@ -32,6 +32,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
public class SpanFirstQueryBuilder extends AbstractQueryBuilder<SpanFirstQueryBuilder> implements SpanQueryBuilder {
public static final String NAME = "span_first";
@ -115,9 +117,10 @@ public class SpanFirstQueryBuilder extends AbstractQueryBuilder<SpanFirstQueryBu
if (MATCH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "spanFirst [match] must be of type span query");
throw new ParsingException(parser.getTokenLocation(), "span_first [match] must be of type span query");
}
match = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, match);
} else {
throw new ParsingException(parser.getTokenLocation(), "[span_first] query does not support [" + currentFieldName + "]");
}
@ -134,10 +137,10 @@ public class SpanFirstQueryBuilder extends AbstractQueryBuilder<SpanFirstQueryBu
}
}
if (match == null) {
throw new ParsingException(parser.getTokenLocation(), "spanFirst must have [match] span query clause");
throw new ParsingException(parser.getTokenLocation(), "span_first must have [match] span query clause");
}
if (end == null) {
throw new ParsingException(parser.getTokenLocation(), "spanFirst must have [end] set for it");
throw new ParsingException(parser.getTokenLocation(), "span_first must have [end] set for it");
}
SpanFirstQueryBuilder queryBuilder = new SpanFirstQueryBuilder(match, end);
queryBuilder.boost(boost).queryName(queryName);

View File

@ -38,6 +38,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
/**
* Matches spans which are near one another. One can specify slop, the maximum number
* of intervening unmatched positions, as well as whether matches are required to be in-order.
@ -166,9 +168,11 @@ public class SpanNearQueryBuilder extends AbstractQueryBuilder<SpanNearQueryBuil
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "spanNear [clauses] must be of type span query");
throw new ParsingException(parser.getTokenLocation(), "span_near [clauses] must be of type span query");
}
clauses.add((SpanQueryBuilder) query);
final SpanQueryBuilder clause = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, clause);
clauses.add(clause);
}
} else {
throw new ParsingException(parser.getTokenLocation(), "[span_near] query does not support [" + currentFieldName + "]");

View File

@ -32,6 +32,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
public class SpanNotQueryBuilder extends AbstractQueryBuilder<SpanNotQueryBuilder> implements SpanQueryBuilder {
public static final String NAME = "span_not";
@ -181,15 +183,17 @@ public class SpanNotQueryBuilder extends AbstractQueryBuilder<SpanNotQueryBuilde
if (INCLUDE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "spanNot [include] must be of type span query");
throw new ParsingException(parser.getTokenLocation(), "span_not [include] must be of type span query");
}
include = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, include);
} else if (EXCLUDE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "spanNot [exclude] must be of type span query");
throw new ParsingException(parser.getTokenLocation(), "span_not [exclude] must be of type span query");
}
exclude = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, exclude);
} else {
throw new ParsingException(parser.getTokenLocation(), "[span_not] query does not support [" + currentFieldName + "]");
}
@ -210,13 +214,13 @@ public class SpanNotQueryBuilder extends AbstractQueryBuilder<SpanNotQueryBuilde
}
}
if (include == null) {
throw new ParsingException(parser.getTokenLocation(), "spanNot must have [include] span query clause");
throw new ParsingException(parser.getTokenLocation(), "span_not must have [include] span query clause");
}
if (exclude == null) {
throw new ParsingException(parser.getTokenLocation(), "spanNot must have [exclude] span query clause");
throw new ParsingException(parser.getTokenLocation(), "span_not must have [exclude] span query clause");
}
if (dist != null && (pre != null || post != null)) {
throw new ParsingException(parser.getTokenLocation(), "spanNot can either use [dist] or [pre] & [post] (or none)");
throw new ParsingException(parser.getTokenLocation(), "span_not can either use [dist] or [pre] & [post] (or none)");
}
SpanNotQueryBuilder spanNotQuery = new SpanNotQueryBuilder(include, exclude);

View File

@ -35,6 +35,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
/**
* Span query that matches the union of its clauses. Maps to {@link SpanOrQuery}.
*/
@ -113,9 +115,11 @@ public class SpanOrQueryBuilder extends AbstractQueryBuilder<SpanOrQueryBuilder>
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "spanOr [clauses] must be of type span query");
throw new ParsingException(parser.getTokenLocation(), "span_or [clauses] must be of type span query");
}
clauses.add((SpanQueryBuilder) query);
final SpanQueryBuilder clause = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, clause);
clauses.add(clause);
}
} else {
throw new ParsingException(parser.getTokenLocation(), "[span_or] query does not support [" + currentFieldName + "]");
@ -132,7 +136,7 @@ public class SpanOrQueryBuilder extends AbstractQueryBuilder<SpanOrQueryBuilder>
}
if (clauses.isEmpty()) {
throw new ParsingException(parser.getTokenLocation(), "spanOr must include [clauses]");
throw new ParsingException(parser.getTokenLocation(), "span_or must include [clauses]");
}
SpanOrQueryBuilder queryBuilder = new SpanOrQueryBuilder(clauses.get(0));

View File

@ -19,9 +19,37 @@
package org.elasticsearch.index.query;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.xcontent.XContentParser;
/**
* Marker interface for a specific type of {@link QueryBuilder} that allows to build span queries
* Marker interface for a specific type of {@link QueryBuilder} that allows to build span queries.
*/
public interface SpanQueryBuilder extends QueryBuilder {
class SpanQueryBuilderUtil {
private SpanQueryBuilderUtil() {
// utility class
}
/**
* Checks boost value of a nested span clause is equal to {@link AbstractQueryBuilder#DEFAULT_BOOST}.
*
* @param queryName a query name
* @param fieldName a field name
* @param parser a parser
* @param clause a span query builder
* @throws ParsingException if query boost value isn't equal to {@link AbstractQueryBuilder#DEFAULT_BOOST}
*/
static void checkNoBoost(String queryName, String fieldName, XContentParser parser, SpanQueryBuilder clause) {
try {
if (clause.boost() != AbstractQueryBuilder.DEFAULT_BOOST) {
throw new ParsingException(parser.getTokenLocation(), queryName + " [" + fieldName + "] " +
"as a nested span clause can't have non-default boost value [" + clause.boost() + "]");
}
} catch (UnsupportedOperationException ignored) {
// if boost is unsupported it can't have been set
}
}
}
}

View File

@ -32,6 +32,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.index.query.SpanQueryBuilder.SpanQueryBuilderUtil.checkNoBoost;
/**
* Builder for {@link org.apache.lucene.search.spans.SpanWithinQuery}.
*/
@ -122,12 +124,14 @@ public class SpanWithinQueryBuilder extends AbstractQueryBuilder<SpanWithinQuery
throw new ParsingException(parser.getTokenLocation(), "span_within [big] must be of type span query");
}
big = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, big);
} else if (LITTLE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
QueryBuilder query = parseInnerQueryBuilder(parser);
if (query instanceof SpanQueryBuilder == false) {
throw new ParsingException(parser.getTokenLocation(), "span_within [little] must be of type span query");
}
little = (SpanQueryBuilder) query;
checkNoBoost(NAME, currentFieldName, parser, little);
} else {
throw new ParsingException(parser.getTokenLocation(),
"[span_within] query does not support [" + currentFieldName + "]");

View File

@ -21,11 +21,13 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanContainingQuery;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.test.AbstractQueryTestCase;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
public class SpanContainingQueryBuilderTests extends AbstractQueryTestCase<SpanContainingQueryBuilder> {
@ -80,7 +82,7 @@ public class SpanContainingQueryBuilderTests extends AbstractQueryTestCase<SpanC
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" \"boost\" : 2.0\n" +
" }\n" +
"}";
@ -89,5 +91,92 @@ public class SpanContainingQueryBuilderTests extends AbstractQueryTestCase<SpanC
assertEquals(json, 2, ((SpanNearQueryBuilder) parsed.bigQuery()).clauses().size());
assertEquals(json, "foo", ((SpanTermQueryBuilder) parsed.littleQuery()).value());
assertEquals(json, 2.0, parsed.boost(), 0.0);
}
public void testFromJsoWithNonDefaultBoostInBigQuery() {
String json =
"{\n" +
" \"span_containing\" : {\n" +
" \"big\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"bar\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"baz\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 5,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"little\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"foo\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_containing [big] as a nested span clause can't have non-default boost value [2.0]"));
}
public void testFromJsonWithNonDefaultBoostInLittleQuery() {
String json =
"{\n" +
" \"span_containing\" : {\n" +
" \"little\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"bar\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"baz\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 5,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"big\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"foo\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_containing [little] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -31,6 +31,7 @@ import org.elasticsearch.test.AbstractQueryTestCase;
import java.io.IOException;
import static org.elasticsearch.index.query.QueryBuilders.spanTermQuery;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
public class SpanFirstQueryBuilderTests extends AbstractQueryTestCase<SpanFirstQueryBuilder> {
@ -59,7 +60,7 @@ public class SpanFirstQueryBuilderTests extends AbstractQueryTestCase<SpanFirstQ
builder.endObject();
ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(Strings.toString(builder)));
assertTrue(e.getMessage().contains("spanFirst must have [end] set"));
assertTrue(e.getMessage().contains("span_first must have [end] set"));
}
{
XContentBuilder builder = XContentFactory.jsonBuilder();
@ -70,7 +71,7 @@ public class SpanFirstQueryBuilderTests extends AbstractQueryTestCase<SpanFirstQ
builder.endObject();
ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(Strings.toString(builder)));
assertTrue(e.getMessage().contains("spanFirst must have [match] span query clause"));
assertTrue(e.getMessage().contains("span_first must have [match] span query clause"));
}
}
@ -97,4 +98,27 @@ public class SpanFirstQueryBuilderTests extends AbstractQueryTestCase<SpanFirstQ
assertEquals(json, 3, parsed.end());
assertEquals(json, "kimchy", ((SpanTermQueryBuilder) parsed.innerQuery()).value());
}
public void testFromJsonWithNonDefaultBoostInMatchQuery() {
String json =
"{\n" +
" \"span_first\" : {\n" +
" \"match\" : {\n" +
" \"span_term\" : {\n" +
" \"user\" : {\n" +
" \"value\" : \"kimchy\",\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"end\" : 3,\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_first [match] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -114,7 +114,7 @@ public class SpanNearQueryBuilderTests extends AbstractQueryTestCase<SpanNearQue
" } ],\n" +
" \"slop\" : 12,\n" +
" \"in_order\" : false,\n" +
" \"boost\" : 1.0\n" +
" \"boost\" : 2.0\n" +
" }\n" +
"}";
@ -124,6 +124,7 @@ public class SpanNearQueryBuilderTests extends AbstractQueryTestCase<SpanNearQue
assertEquals(json, 3, parsed.clauses().size());
assertEquals(json, 12, parsed.slop());
assertEquals(json, false, parsed.inOrder());
assertEquals(json, 2.0, parsed.boost(), 0.0);
}
public void testParsingSlopDefault() throws IOException {
@ -187,4 +188,40 @@ public class SpanNearQueryBuilderTests extends AbstractQueryTestCase<SpanNearQue
assertThat(e.getMessage(), containsString("[span_near] query does not support [collect_payloads]"));
}
public void testFromJsonWithNonDefaultBoostInInnerQuery() {
String json =
"{\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field\" : {\n" +
" \"value\" : \"value1\",\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field\" : {\n" +
" \"value\" : \"value2\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field\" : {\n" +
" \"value\" : \"value3\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 12,\n" +
" \"in_order\" : false,\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_near [clauses] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -132,7 +132,7 @@ public class SpanNotQueryBuilderTests extends AbstractQueryTestCase<SpanNotQuery
builder.endObject();
ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(Strings.toString(builder)));
assertThat(e.getDetailedMessage(), containsString("spanNot must have [include]"));
assertThat(e.getDetailedMessage(), containsString("span_not must have [include]"));
}
{
XContentBuilder builder = XContentFactory.jsonBuilder();
@ -146,7 +146,7 @@ public class SpanNotQueryBuilderTests extends AbstractQueryTestCase<SpanNotQuery
builder.endObject();
ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(Strings.toString(builder)));
assertThat(e.getDetailedMessage(), containsString("spanNot must have [exclude]"));
assertThat(e.getDetailedMessage(), containsString("span_not must have [exclude]"));
}
{
XContentBuilder builder = XContentFactory.jsonBuilder();
@ -163,7 +163,7 @@ public class SpanNotQueryBuilderTests extends AbstractQueryTestCase<SpanNotQuery
builder.endObject();
ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(Strings.toString(builder)));
assertThat(e.getDetailedMessage(), containsString("spanNot can either use [dist] or [pre] & [post] (or none)"));
assertThat(e.getDetailedMessage(), containsString("span_not can either use [dist] or [pre] & [post] (or none)"));
}
}
@ -203,7 +203,7 @@ public class SpanNotQueryBuilderTests extends AbstractQueryTestCase<SpanNotQuery
" },\n" +
" \"pre\" : 0,\n" +
" \"post\" : 0,\n" +
" \"boost\" : 1.0\n" +
" \"boost\" : 2.0\n" +
" }\n" +
"}";
@ -212,5 +212,97 @@ public class SpanNotQueryBuilderTests extends AbstractQueryTestCase<SpanNotQuery
assertEquals(json, "hoya", ((SpanTermQueryBuilder) parsed.includeQuery()).value());
assertEquals(json, 2, ((SpanNearQueryBuilder) parsed.excludeQuery()).clauses().size());
assertEquals(json, 2.0, parsed.boost(), 0.0);
}
public void testFromJsonWithNonDefaultBoostInIncludeQuery() {
String json =
"{\n" +
" \"span_not\" : {\n" +
" \"exclude\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"hoya\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"include\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"la\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"hoya\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 0,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"pre\" : 0,\n" +
" \"post\" : 0,\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_not [include] as a nested span clause can't have non-default boost value [2.0]"));
}
public void testFromJsonWithNonDefaultBoostInExcludeQuery() {
String json =
"{\n" +
" \"span_not\" : {\n" +
" \"include\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"hoya\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"exclude\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"la\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"hoya\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 0,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"pre\" : 0,\n" +
" \"post\" : 0,\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_not [exclude] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.test.AbstractQueryTestCase;
@ -94,7 +95,7 @@ public class SpanOrQueryBuilderTests extends AbstractQueryTestCase<SpanOrQueryBu
" }\n" +
" }\n" +
" } ],\n" +
" \"boost\" : 1.0\n" +
" \"boost\" : 2.0\n" +
" }\n" +
"}";
@ -102,5 +103,27 @@ public class SpanOrQueryBuilderTests extends AbstractQueryTestCase<SpanOrQueryBu
checkGeneratedJson(json, parsed);
assertEquals(json, 3, parsed.clauses().size());
assertEquals(json, 2.0, parsed.boost(), 0.0);
}
public void testFromJsonWithNonDefaultBoostInInnerQuery() {
String json =
"{\n" +
" \"span_or\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field\" : {\n" +
" \"value\" : \"value1\",\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_or [clauses] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -76,19 +76,16 @@ public class SpanTermQueryBuilderTests extends AbstractTermQueryTestCase<SpanTer
}
/**
* @param amount the number of clauses that will be returned
* @return an array of random {@link SpanTermQueryBuilder} with same field name
* @param amount a number of clauses that will be returned
* @return the array of random {@link SpanTermQueryBuilder} with same field name
*/
public SpanTermQueryBuilder[] createSpanTermQueryBuilders(int amount) {
SpanTermQueryBuilder[] clauses = new SpanTermQueryBuilder[amount];
SpanTermQueryBuilder first = createTestQueryBuilder();
SpanTermQueryBuilder first = createTestQueryBuilder(false, true);
clauses[0] = first;
for (int i = 1; i < amount; i++) {
// we need same field name in all clauses, so we only randomize value
SpanTermQueryBuilder spanTermQuery = new SpanTermQueryBuilder(first.fieldName(), getRandomValueForFieldName(first.fieldName()));
if (randomBoolean()) {
spanTermQuery.boost(2.0f / randomIntBetween(1, 20));
}
if (randomBoolean()) {
spanTermQuery.queryName(randomAlphaOfLengthBetween(1, 10));
}

View File

@ -21,11 +21,13 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanWithinQuery;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.test.AbstractQueryTestCase;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
public class SpanWithinQueryBuilderTests extends AbstractQueryTestCase<SpanWithinQueryBuilder> {
@ -80,7 +82,7 @@ public class SpanWithinQueryBuilderTests extends AbstractQueryTestCase<SpanWithi
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" \"boost\" : 2.0\n" +
" }\n" +
"}";
@ -89,5 +91,92 @@ public class SpanWithinQueryBuilderTests extends AbstractQueryTestCase<SpanWithi
assertEquals(json, "foo", ((SpanTermQueryBuilder) parsed.littleQuery()).value());
assertEquals(json, 2, ((SpanNearQueryBuilder) parsed.bigQuery()).clauses().size());
assertEquals(json, 2.0, parsed.boost(), 0.0);
}
public void testFromJsonWithNonDefaultBoostInBigQuery() {
String json =
"{\n" +
" \"span_within\" : {\n" +
" \"big\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"bar\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"baz\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 5,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"little\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"foo\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_within [big] as a nested span clause can't have non-default boost value [2.0]"));
}
public void testFromJsonWithNonDefaultBoostInLittleQuery() {
String json =
"{\n" +
" \"span_within\" : {\n" +
" \"little\" : {\n" +
" \"span_near\" : {\n" +
" \"clauses\" : [ {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"bar\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" }, {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"baz\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" } ],\n" +
" \"slop\" : 5,\n" +
" \"in_order\" : true,\n" +
" \"boost\" : 2.0\n" +
" }\n" +
" },\n" +
" \"big\" : {\n" +
" \"span_term\" : {\n" +
" \"field1\" : {\n" +
" \"value\" : \"foo\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
" }\n" +
" },\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
Exception exception = expectThrows(ParsingException.class, () -> parseQuery(json));
assertThat(exception.getMessage(),
equalTo("span_within [little] as a nested span clause can't have non-default boost value [2.0]"));
}
}

View File

@ -39,7 +39,12 @@ import java.io.UnsupportedEncodingException;
public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQueryBuilder> {
@Override
protected boolean supportsBoostAndQueryName() {
protected boolean supportsBoost() {
return false;
}
@Override
protected boolean supportsQueryName() {
return false;
}

View File

@ -20,7 +20,6 @@
package org.elasticsearch.test;
import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
@ -83,15 +82,16 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
private static final int NUMBER_OF_TESTQUERIES = 20;
public final QB createTestQueryBuilder() {
return createTestQueryBuilder(supportsBoost(), supportsQueryName());
}
public final QB createTestQueryBuilder(boolean supportsBoost, boolean supportsQueryName) {
QB query = doCreateTestQueryBuilder();
//we should not set boost and query name for queries that don't parse it
if (supportsBoostAndQueryName()) {
if (randomBoolean()) {
query.boost(2.0f / randomIntBetween(1, 20));
}
if (randomBoolean()) {
query.queryName(createUniqueRandomName());
}
if (supportsBoost && randomBoolean()) {
query.boost(2.0f / randomIntBetween(1, 20));
}
if (supportsQueryName && randomBoolean()) {
query.queryName(createUniqueRandomName());
}
return query;
}
@ -460,7 +460,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
rewrite(secondLuceneQuery), rewrite(firstLuceneQuery));
}
if (supportsBoostAndQueryName()) {
if (supportsBoost()) {
secondQuery.boost(firstQuery.boost() + 1f + randomFloat());
Query thirdLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context);
assertNotEquals("modifying the boost doesn't affect the corresponding lucene query", rewrite(firstLuceneQuery),
@ -487,12 +487,22 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
}
/**
* Few queries allow you to set the boost and queryName on the java api, although the corresponding parser
* doesn't parse them as they are not supported. This method allows to disable boost and queryName related tests for those queries.
* Those queries are easy to identify: their parsers don't parse `boost` and `_name` as they don't apply to the specific query:
* wrapper query and match_none
* Few queries allow you to set the boost on the Java API, although the corresponding parser
* doesn't parse it as it isn't supported. This method allows to disable boost related tests for those queries.
* Those queries are easy to identify: their parsers don't parse {@code boost} as they don't apply to the specific query:
* wrapper query and {@code match_none}.
*/
protected boolean supportsBoostAndQueryName() {
protected boolean supportsBoost() {
return true;
}
/**
* Few queries allow you to set the query name on the Java API, although the corresponding parser
* doesn't parse it as it isn't supported. This method allows to disable query name related tests for those queries.
* Those queries are easy to identify: their parsers don't parse {@code _name} as they don't apply to the specific query:
* wrapper query and {@code match_none}.
*/
protected boolean supportsQueryName() {
return true;
}