Fix blended terms for non-strings take 2
It had some funky errors, like lenient:true not working and queries with two integer fields blowing up if there was no analyzer defined on the query. This throws a bunch more tests at it and rejiggers how non-strings are handled so they don't wander off into scary QueryBuilder-land unless they have a nice strong analyzer to protect them.
This commit is contained in:
parent
d8af49eb91
commit
50098bfb2c
|
@ -389,7 +389,12 @@ public abstract class MappedFieldType extends FieldType {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a term associated with the field of this mapper for the given value */
|
/**
|
||||||
|
* Creates a term associated with the field of this mapper for the given
|
||||||
|
* value. Its important to use termQuery when building term queries because
|
||||||
|
* things like ParentFieldMapper override it to make more interesting
|
||||||
|
* queries.
|
||||||
|
*/
|
||||||
protected Term createTerm(Object value) {
|
protected Term createTerm(Object value) {
|
||||||
return new Term(name(), indexedValueForSearch(value));
|
return new Term(name(), indexedValueForSearch(value));
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,10 +212,6 @@ public class MatchQuery {
|
||||||
this.zeroTermsQuery = zeroTermsQuery;
|
this.zeroTermsQuery = zeroTermsQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean forceAnalyzeQueryString() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Analyzer getAnalyzer(MappedFieldType fieldType) {
|
protected Analyzer getAnalyzer(MappedFieldType fieldType) {
|
||||||
if (this.analyzer == null) {
|
if (this.analyzer == null) {
|
||||||
if (fieldType != null) {
|
if (fieldType != null) {
|
||||||
|
@ -240,17 +236,19 @@ public class MatchQuery {
|
||||||
field = fieldName;
|
field = fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldType != null && fieldType.useTermQueryWithQueryString() && !forceAnalyzeQueryString()) {
|
/*
|
||||||
try {
|
* If the user forced an analyzer we really don't care if they are
|
||||||
return fieldType.termQuery(value, context);
|
* searching a type that wants term queries to be used with query string
|
||||||
} catch (RuntimeException e) {
|
* because the QueryBuilder will take care of it. If they haven't forced
|
||||||
if (lenient) {
|
* an analyzer then types like NumberFieldType that want terms with
|
||||||
return null;
|
* query string will blow up because their analyzer isn't capable of
|
||||||
}
|
* passing through QueryBuilder.
|
||||||
throw e;
|
*/
|
||||||
|
boolean noForcedAnalyzer = this.analyzer == null;
|
||||||
|
if (fieldType != null && fieldType.useTermQueryWithQueryString() && noForcedAnalyzer) {
|
||||||
|
return termQuery(fieldType, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
Analyzer analyzer = getAnalyzer(fieldType);
|
Analyzer analyzer = getAnalyzer(fieldType);
|
||||||
assert analyzer != null;
|
assert analyzer != null;
|
||||||
MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType);
|
MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType);
|
||||||
|
@ -282,6 +280,26 @@ public class MatchQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TermQuery-like-query for MappedFieldTypes that don't support
|
||||||
|
* QueryBuilder which is very string-ish. Just delegates to the
|
||||||
|
* MappedFieldType for MatchQuery but gets more complex for blended queries.
|
||||||
|
*/
|
||||||
|
protected Query termQuery(MappedFieldType fieldType, Object value) {
|
||||||
|
return termQuery(fieldType, value, lenient);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Query termQuery(MappedFieldType fieldType, Object value, boolean lenient) {
|
||||||
|
try {
|
||||||
|
return fieldType.termQuery(value, context);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (lenient) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected Query zeroTermsQuery() {
|
protected Query zeroTermsQuery() {
|
||||||
return zeroTermsQuery == DEFAULT_ZERO_TERMS_QUERY ? Queries.newMatchNoDocsQuery() : Queries.newMatchAllQuery();
|
return zeroTermsQuery == DEFAULT_ZERO_TERMS_QUERY ? Queries.newMatchNoDocsQuery() : Queries.newMatchAllQuery();
|
||||||
}
|
}
|
||||||
|
@ -289,6 +307,7 @@ public class MatchQuery {
|
||||||
private class MatchQueryBuilder extends QueryBuilder {
|
private class MatchQueryBuilder extends QueryBuilder {
|
||||||
|
|
||||||
private final MappedFieldType mapper;
|
private final MappedFieldType mapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new QueryBuilder using the given analyzer.
|
* Creates a new QueryBuilder using the given analyzer.
|
||||||
*/
|
*/
|
||||||
|
@ -302,7 +321,6 @@ public class MatchQuery {
|
||||||
return blendTermQuery(term, mapper);
|
return blendTermQuery(term, mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Query createPhrasePrefixQuery(String field, String queryText, int phraseSlop, int maxExpansions) {
|
public Query createPhrasePrefixQuery(String field, String queryText, int phraseSlop, int maxExpansions) {
|
||||||
final Query query = createFieldQuery(getAnalyzer(), Occur.MUST, field, queryText, true, phraseSlop);
|
final Query query = createFieldQuery(getAnalyzer(), Occur.MUST, field, queryText, true, phraseSlop);
|
||||||
final MultiPhrasePrefixQuery prefixQuery = new MultiPhrasePrefixQuery();
|
final MultiPhrasePrefixQuery prefixQuery = new MultiPhrasePrefixQuery();
|
||||||
|
@ -352,11 +370,16 @@ public class MatchQuery {
|
||||||
protected Query blendTermQuery(Term term, MappedFieldType fieldType) {
|
protected Query blendTermQuery(Term term, MappedFieldType fieldType) {
|
||||||
if (fuzziness != null) {
|
if (fuzziness != null) {
|
||||||
if (fieldType != null) {
|
if (fieldType != null) {
|
||||||
|
try {
|
||||||
Query query = fieldType.fuzzyQuery(term.text(), fuzziness, fuzzyPrefixLength, maxExpansions, transpositions);
|
Query query = fieldType.fuzzyQuery(term.text(), fuzziness, fuzzyPrefixLength, maxExpansions, transpositions);
|
||||||
if (query instanceof FuzzyQuery) {
|
if (query instanceof FuzzyQuery) {
|
||||||
QueryParsers.setRewriteMethod((FuzzyQuery) query, fuzzyRewriteMethod);
|
QueryParsers.setRewriteMethod((FuzzyQuery) query, fuzzyRewriteMethod);
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return new TermQuery(term);
|
||||||
|
// See long comment below about why we're lenient here.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int edits = fuzziness.asDistance(term.text());
|
int edits = fuzziness.asDistance(term.text());
|
||||||
FuzzyQuery query = new FuzzyQuery(term, edits, fuzzyPrefixLength, maxExpansions, transpositions);
|
FuzzyQuery query = new FuzzyQuery(term, edits, fuzzyPrefixLength, maxExpansions, transpositions);
|
||||||
|
@ -364,9 +387,25 @@ public class MatchQuery {
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
if (fieldType != null) {
|
if (fieldType != null) {
|
||||||
Query termQuery = fieldType.queryStringTermQuery(term);
|
/*
|
||||||
if (termQuery != null) {
|
* Its a bit weird to default to lenient here but its the backwards
|
||||||
return termQuery;
|
* compatible. It makes some sense when you think about what we are
|
||||||
|
* doing here: at this point the user has forced an analyzer and
|
||||||
|
* passed some string to the match query. We cut it up using the
|
||||||
|
* analyzer and then tried to cram whatever we get into the field.
|
||||||
|
* lenient=true here means that we try the terms in the query and on
|
||||||
|
* the off chance that they are actually valid terms then we
|
||||||
|
* actually try them. lenient=false would mean that we blow up the
|
||||||
|
* query if they aren't valid terms. "valid" in this context means
|
||||||
|
* "parses properly to something of the type being queried." So "1"
|
||||||
|
* is a valid number, etc.
|
||||||
|
*
|
||||||
|
* We use the text form here because we we've received the term from
|
||||||
|
* an analyzer that cut some string into text.
|
||||||
|
*/
|
||||||
|
Query query = termQuery(fieldType, term.bytes(), true);
|
||||||
|
if (query != null) {
|
||||||
|
return query;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new TermQuery(term);
|
return new TermQuery(term);
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.BoostQuery;
|
import org.apache.lucene.search.BoostQuery;
|
||||||
import org.apache.lucene.search.DisjunctionMaxQuery;
|
import org.apache.lucene.search.DisjunctionMaxQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.lucene.search.Queries;
|
import org.elasticsearch.common.lucene.search.Queries;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
|
@ -146,8 +145,8 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
return MultiMatchQuery.super.blendTermQuery(term, fieldType);
|
return MultiMatchQuery.super.blendTermQuery(term, fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean forceAnalyzeQueryString() {
|
public Query termQuery(MappedFieldType fieldType, Object value) {
|
||||||
return false;
|
return MultiMatchQuery.this.termQuery(fieldType, value, lenient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +195,13 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
} else {
|
} else {
|
||||||
blendedFields = null;
|
blendedFields = null;
|
||||||
}
|
}
|
||||||
final FieldAndFieldType fieldAndFieldType = group.get(0);
|
/*
|
||||||
Query q = parseGroup(type.matchQueryType(), fieldAndFieldType.field, 1f, value, minimumShouldMatch);
|
* We have to pick some field to pass through the superclass so
|
||||||
|
* we just pick the first field. It shouldn't matter because
|
||||||
|
* fields are already grouped by their analyzers/types.
|
||||||
|
*/
|
||||||
|
String representativeField = group.get(0).field;
|
||||||
|
Query q = parseGroup(type.matchQueryType(), representativeField, 1f, value, minimumShouldMatch);
|
||||||
if (q != null) {
|
if (q != null) {
|
||||||
queries.add(q);
|
queries.add(q);
|
||||||
}
|
}
|
||||||
|
@ -206,11 +210,6 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
return queries.isEmpty() ? null : queries;
|
return queries.isEmpty() ? null : queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean forceAnalyzeQueryString() {
|
|
||||||
return blendedFields != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Query blendTerm(Term term, MappedFieldType fieldType) {
|
public Query blendTerm(Term term, MappedFieldType fieldType) {
|
||||||
if (blendedFields == null) {
|
if (blendedFields == null) {
|
||||||
|
@ -231,6 +230,16 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
}
|
}
|
||||||
return BlendedTermQuery.dismaxBlendedQuery(terms, blendedBoost, tieBreaker);
|
return BlendedTermQuery.dismaxBlendedQuery(terms, blendedBoost, tieBreaker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Query termQuery(MappedFieldType fieldType, Object value) {
|
||||||
|
/*
|
||||||
|
* Use the string value of the term because we're reusing the
|
||||||
|
* portion of the query is usually after the analyzer has run on
|
||||||
|
* each term. We just skip that analyzer phase.
|
||||||
|
*/
|
||||||
|
return blendTerm(new Term(fieldType.name(), value.toString()), fieldType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -241,6 +250,15 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
return queryBuilder.blendTerm(term, fieldType);
|
return queryBuilder.blendTerm(term, fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Query termQuery(MappedFieldType fieldType, Object value) {
|
||||||
|
if (queryBuilder == null) {
|
||||||
|
// Can be null when the MultiMatchQuery collapses into a MatchQuery
|
||||||
|
return super.termQuery(fieldType, value);
|
||||||
|
}
|
||||||
|
return queryBuilder.termQuery(fieldType, value);
|
||||||
|
}
|
||||||
|
|
||||||
private static final class FieldAndFieldType {
|
private static final class FieldAndFieldType {
|
||||||
final String field;
|
final String field;
|
||||||
final MappedFieldType fieldType;
|
final MappedFieldType fieldType;
|
||||||
|
@ -255,18 +273,17 @@ public class MultiMatchQuery extends MatchQuery {
|
||||||
|
|
||||||
public Term newTerm(String value) {
|
public Term newTerm(String value) {
|
||||||
try {
|
try {
|
||||||
final BytesRef bytesRef = fieldType.indexedValueForSearch(value);
|
/*
|
||||||
return new Term(field, bytesRef);
|
* Note that this ignore any overrides the fieldType might do
|
||||||
} catch (Exception ex) {
|
* for termQuery, meaning things like _parent won't work here.
|
||||||
|
*/
|
||||||
|
return new Term(fieldType.name(), fieldType.indexedValueForSearch(value));
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
// we can't parse it just use the incoming value -- it will
|
// we can't parse it just use the incoming value -- it will
|
||||||
// just have a DF of 0 at the end of the day and will be ignored
|
// just have a DF of 0 at the end of the day and will be ignored
|
||||||
|
// Note that this is like lenient = true allways
|
||||||
}
|
}
|
||||||
return new Term(field, value);
|
return new Term(field, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean forceAnalyzeQueryString() {
|
|
||||||
return this.queryBuilder == null ? super.forceAnalyzeQueryString() : this.queryBuilder.forceAnalyzeQueryString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,18 @@ import org.apache.lucene.search.BooleanClause;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.FuzzyQuery;
|
import org.apache.lucene.search.FuzzyQuery;
|
||||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
|
import org.apache.lucene.search.NumericRangeQuery;
|
||||||
import org.apache.lucene.search.PhraseQuery;
|
import org.apache.lucene.search.PhraseQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
|
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
|
||||||
import org.elasticsearch.common.lucene.search.Queries;
|
import org.elasticsearch.common.lucene.search.Queries;
|
||||||
|
import org.elasticsearch.common.unit.Fuzziness;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.index.search.MatchQuery;
|
import org.elasticsearch.index.search.MatchQuery;
|
||||||
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
|
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.joda.time.format.ISODateTimeFormat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -120,15 +124,15 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
|
||||||
switch (queryBuilder.type()) {
|
switch (queryBuilder.type()) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(ExtendedCommonTermsQuery.class))
|
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(ExtendedCommonTermsQuery.class))
|
||||||
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)));
|
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)).or(instanceOf(NumericRangeQuery.class)));
|
||||||
break;
|
break;
|
||||||
case PHRASE:
|
case PHRASE:
|
||||||
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(PhraseQuery.class))
|
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(PhraseQuery.class))
|
||||||
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)));
|
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)).or(instanceOf(NumericRangeQuery.class)));
|
||||||
break;
|
break;
|
||||||
case PHRASE_PREFIX:
|
case PHRASE_PREFIX:
|
||||||
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class))
|
assertThat(query, either(instanceOf(BooleanQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class))
|
||||||
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)));
|
.or(instanceOf(TermQuery.class)).or(instanceOf(FuzzyQuery.class)).or(instanceOf(NumericRangeQuery.class)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,10 +177,45 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
|
||||||
// compare lowercased terms here
|
// compare lowercased terms here
|
||||||
String originalTermLc = queryBuilder.value().toString().toLowerCase(Locale.ROOT);
|
String originalTermLc = queryBuilder.value().toString().toLowerCase(Locale.ROOT);
|
||||||
String actualTermLc = fuzzyQuery.getTerm().text().toLowerCase(Locale.ROOT);
|
String actualTermLc = fuzzyQuery.getTerm().text().toLowerCase(Locale.ROOT);
|
||||||
assertThat(actualTermLc, equalTo(originalTermLc));
|
Matcher<String> termLcMatcher = equalTo(originalTermLc);
|
||||||
|
if ("false".equals(originalTermLc) || "true".equals(originalTermLc)) {
|
||||||
|
// Booleans become t/f when querying a boolean field
|
||||||
|
termLcMatcher = either(termLcMatcher).or(equalTo(originalTermLc.substring(0, 1)));
|
||||||
|
}
|
||||||
|
assertThat(actualTermLc, termLcMatcher);
|
||||||
assertThat(queryBuilder.prefixLength(), equalTo(fuzzyQuery.getPrefixLength()));
|
assertThat(queryBuilder.prefixLength(), equalTo(fuzzyQuery.getPrefixLength()));
|
||||||
assertThat(queryBuilder.fuzzyTranspositions(), equalTo(fuzzyQuery.getTranspositions()));
|
assertThat(queryBuilder.fuzzyTranspositions(), equalTo(fuzzyQuery.getTranspositions()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (query instanceof NumericRangeQuery) {
|
||||||
|
// These are fuzzy numeric queries
|
||||||
|
assertTrue(queryBuilder.fuzziness() != null);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
NumericRangeQuery<Number> numericRangeQuery = (NumericRangeQuery<Number>) query;
|
||||||
|
assertTrue(numericRangeQuery.includesMin());
|
||||||
|
assertTrue(numericRangeQuery.includesMax());
|
||||||
|
|
||||||
|
double value;
|
||||||
|
try {
|
||||||
|
value = Double.parseDouble(queryBuilder.value().toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Maybe its a date
|
||||||
|
value = ISODateTimeFormat.dateTimeParser().parseMillis(queryBuilder.value().toString());
|
||||||
|
}
|
||||||
|
double width;
|
||||||
|
if (queryBuilder.fuzziness().equals(Fuzziness.AUTO)) {
|
||||||
|
width = 1;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
width = queryBuilder.fuzziness().asDouble();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Maybe a time value?
|
||||||
|
width = queryBuilder.fuzziness().asTimeValue().getMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(value - width, numericRangeQuery.getMin().doubleValue(), width * .1);
|
||||||
|
assertEquals(value + width, numericRangeQuery.getMax().doubleValue(), width * .1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIllegalValues() {
|
public void testIllegalValues() {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.lucene.search.DisjunctionMaxQuery;
|
||||||
import org.apache.lucene.search.FuzzyQuery;
|
import org.apache.lucene.search.FuzzyQuery;
|
||||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
|
import org.apache.lucene.search.NumericRangeQuery;
|
||||||
import org.apache.lucene.search.PhraseQuery;
|
import org.apache.lucene.search.PhraseQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
|
@ -132,7 +133,8 @@ public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatc
|
||||||
.or(instanceOf(BooleanQuery.class)).or(instanceOf(DisjunctionMaxQuery.class))
|
.or(instanceOf(BooleanQuery.class)).or(instanceOf(DisjunctionMaxQuery.class))
|
||||||
.or(instanceOf(FuzzyQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class))
|
.or(instanceOf(FuzzyQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class))
|
||||||
.or(instanceOf(MatchAllDocsQuery.class)).or(instanceOf(ExtendedCommonTermsQuery.class))
|
.or(instanceOf(MatchAllDocsQuery.class)).or(instanceOf(ExtendedCommonTermsQuery.class))
|
||||||
.or(instanceOf(MatchNoDocsQuery.class)).or(instanceOf(PhraseQuery.class)));
|
.or(instanceOf(MatchNoDocsQuery.class)).or(instanceOf(PhraseQuery.class))
|
||||||
|
.or(instanceOf(NumericRangeQuery.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIllegaArguments() {
|
public void testIllegaArguments() {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.elasticsearch.search.query;
|
package org.elasticsearch.search.query;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
|
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
@ -230,6 +231,12 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
|
||||||
.setQuery(randomizeType(multiMatchQuery("15", "skill"))).get();
|
.setQuery(randomizeType(multiMatchQuery("15", "skill"))).get();
|
||||||
assertNoFailures(searchResponse);
|
assertNoFailures(searchResponse);
|
||||||
assertFirstHit(searchResponse, hasId("theone"));
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "skill", "int-field")).analyzer("category")).get();
|
||||||
|
assertNoFailures(searchResponse);
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
String[] fields = {"full_name", "first_name", "last_name", "last_name_phrase", "first_name_phrase", "category_phrase", "category"};
|
String[] fields = {"full_name", "first_name", "last_name", "last_name_phrase", "first_name_phrase", "category_phrase", "category"};
|
||||||
|
|
||||||
String[] query = {"marvel","hero", "captain", "america", "15", "17", "1", "5", "ultimate", "Man",
|
String[] query = {"marvel","hero", "captain", "america", "15", "17", "1", "5", "ultimate", "Man",
|
||||||
|
@ -459,18 +466,65 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
|
||||||
assertHitCount(searchResponse, 1l);
|
assertHitCount(searchResponse, 1l);
|
||||||
assertFirstHit(searchResponse, hasId("theone"));
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill", "int-field")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category")
|
||||||
|
.operator(Operator.AND))).get();
|
||||||
|
assertHitCount(searchResponse, 1l);
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("captain america 15", "skill", "full_name", "first_name", "last_name", "category", "int-field")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category")
|
||||||
|
.operator(Operator.AND))).get();
|
||||||
|
assertHitCount(searchResponse, 1l);
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
|
||||||
searchResponse = client().prepareSearch("test")
|
searchResponse = client().prepareSearch("test")
|
||||||
.setQuery(randomizeType(multiMatchQuery("captain america 15", "first_name", "last_name", "skill")
|
.setQuery(randomizeType(multiMatchQuery("captain america 15", "first_name", "last_name", "skill")
|
||||||
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
.analyzer("category"))).get();
|
.analyzer("category"))).get();
|
||||||
assertFirstHit(searchResponse, hasId("theone"));
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category"))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("25 15", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category"))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
searchResponse = client().prepareSearch("test")
|
searchResponse = client().prepareSearch("test")
|
||||||
.setQuery(randomizeType(multiMatchQuery("25 15", "int-field", "skill")
|
.setQuery(randomizeType(multiMatchQuery("25 15", "int-field", "skill")
|
||||||
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
.analyzer("category"))).get();
|
.analyzer("category"))).get();
|
||||||
assertFirstHit(searchResponse, hasId("theone"));
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("25 15", "first_name", "int-field", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category"))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("25 15", "int-field", "skill", "first_name")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category"))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("25 15", "int-field", "first_name", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.analyzer("category"))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
searchResponse = client().prepareSearch("test")
|
searchResponse = client().prepareSearch("test")
|
||||||
.setQuery(randomizeType(multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category")
|
.setQuery(randomizeType(multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category")
|
||||||
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
@ -529,6 +583,46 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
|
||||||
assertFirstHit(searchResponse, hasId("ultimate2"));
|
assertFirstHit(searchResponse, hasId("ultimate2"));
|
||||||
assertSecondHit(searchResponse, hasId("ultimate1"));
|
assertSecondHit(searchResponse, hasId("ultimate1"));
|
||||||
assertThat(searchResponse.getHits().hits()[0].getScore(), greaterThan(searchResponse.getHits().hits()[1].getScore()));
|
assertThat(searchResponse.getHits().hits()[0].getScore(), greaterThan(searchResponse.getHits().hits()[1].getScore()));
|
||||||
|
|
||||||
|
// Test group based on numeric fields
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "skill", "first_name")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
// Two numeric fields together caused trouble at one point!
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "int-field", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("15", "int-field", "first_name", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("theone"));
|
||||||
|
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("alpha 15", "first_name", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.lenient(true))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("ultimate1"));
|
||||||
|
/*
|
||||||
|
* Doesn't find theone because "alpha 15" isn't a number and we don't
|
||||||
|
* break on spaces.
|
||||||
|
*/
|
||||||
|
assertHitCount(searchResponse, 1);
|
||||||
|
|
||||||
|
// Lenient wasn't always properly lenient with two numeric fields
|
||||||
|
searchResponse = client().prepareSearch("test")
|
||||||
|
.setQuery(randomizeType(multiMatchQuery("alpha 15", "int-field", "first_name", "skill")
|
||||||
|
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
|
||||||
|
.lenient(true))).get();
|
||||||
|
assertFirstHit(searchResponse, hasId("ultimate1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final void assertEquivalent(String query, SearchResponse left, SearchResponse right) {
|
private static final void assertEquivalent(String query, SearchResponse left, SearchResponse right) {
|
||||||
|
|
Loading…
Reference in New Issue