From 2541847945eca84404e657a5af64147227846158 Mon Sep 17 00:00:00 2001 From: Chris Male Date: Thu, 22 Nov 2012 22:13:29 +1300 Subject: [PATCH] Added control over Query used by MatchQuery with there are zero terms after analysis --- .../index/query/MatchQueryBuilder.java | 15 ++++++++ .../index/query/MatchQueryParser.java | 9 +++++ .../index/search/MatchQuery.java | 20 ++++++++-- .../search/query/SimpleQueryTests.java | 37 +++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java index c20944790cc..c46cb880c61 100644 --- a/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java @@ -50,6 +50,11 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer PHRASE_PREFIX } + public static enum ZeroTermsQuery { + NONE, + ALL + } + private final String name; private final Object text; @@ -80,6 +85,8 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer private Boolean fuzzyTranspositions = null; + private ZeroTermsQuery zeroTermsQuery; + /** * Constructs a new text query. */ @@ -180,6 +187,11 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer return this; } + public MatchQueryBuilder zeroTermsQuery(ZeroTermsQuery zeroTermsQuery) { + this.zeroTermsQuery = zeroTermsQuery; + return this; + } + @Override public void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(MatchQueryParser.NAME); @@ -226,6 +238,9 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer if (lenient != null) { builder.field("lenient", lenient); } + if (zeroTermsQuery != null) { + builder.field("zero_terms_query", zeroTermsQuery.toString()); + } builder.endObject(); builder.endObject(); diff --git a/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java b/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java index 2c4251fb060..76d4f35ecd9 100644 --- a/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java @@ -126,6 +126,15 @@ public class MatchQueryParser implements QueryParser { matchQuery.setTranspositions(parser.booleanValue()); } else if ("lenient".equals(currentFieldName)) { matchQuery.setLenient(parser.booleanValue()); + } else if ("zero_terms_query".equals(currentFieldName)) { + String zeroTermsDocs = parser.text(); + if ("none".equalsIgnoreCase(zeroTermsDocs)) { + matchQuery.setZeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE); + } else if ("all".equalsIgnoreCase(zeroTermsDocs)) { + matchQuery.setZeroTermsQuery(MatchQuery.ZeroTermsQuery.ALL); + } else { + throw new QueryParsingException(parseContext.index(), "Unsupported zero_terms_docs value [" + zeroTermsDocs +"]"); + } } else { throw new QueryParsingException(parseContext.index(), "[match] query does not support [" + currentFieldName + "]"); } diff --git a/src/main/java/org/elasticsearch/index/search/MatchQuery.java b/src/main/java/org/elasticsearch/index/search/MatchQuery.java index 8ea2d0d982b..6b65df79de6 100644 --- a/src/main/java/org/elasticsearch/index/search/MatchQuery.java +++ b/src/main/java/org/elasticsearch/index/search/MatchQuery.java @@ -24,18 +24,17 @@ import org.apache.lucene.analysis.CachingTokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; import org.apache.lucene.index.Term; import org.apache.lucene.search.*; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.UnicodeUtil; import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.ElasticSearchIllegalStateException; -import org.elasticsearch.ElasticSearchParseException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.FastStringReader; import org.elasticsearch.common.lucene.search.MatchNoDocsQuery; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; +import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.QueryParseContext; @@ -55,6 +54,11 @@ public class MatchQuery { PHRASE_PREFIX } + public static enum ZeroTermsQuery { + NONE, + ALL + } + protected final QueryParseContext parseContext; protected String analyzer; @@ -77,6 +81,8 @@ public class MatchQuery { protected boolean lenient; + protected ZeroTermsQuery zeroTermsQuery = ZeroTermsQuery.NONE; + public MatchQuery(QueryParseContext parseContext) { this.parseContext = parseContext; } @@ -125,6 +131,10 @@ public class MatchQuery { this.lenient = lenient; } + public void setZeroTermsQuery(ZeroTermsQuery zeroTermsQuery) { + this.zeroTermsQuery = zeroTermsQuery; + } + public Query parse(Type type, String fieldName, String text) { FieldMapper mapper = null; final String field; @@ -238,7 +248,7 @@ public class MatchQuery { } if (numTokens == 0) { - return MatchNoDocsQuery.INSTANCE; + return zeroTermsQuery(); } else if (type == Type.BOOLEAN) { if (numTokens == 1) { try { @@ -399,4 +409,8 @@ public class MatchQuery { UnicodeUtil.UTF16toUTF8WithHash(attr.buffer(), 0, attr.length(), ref); return ref; } + + protected Query zeroTermsQuery() { + return zeroTermsQuery == ZeroTermsQuery.NONE ? MatchNoDocsQuery.INSTANCE : Queries.MATCH_ALL_QUERY; + } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/test/integration/search/query/SimpleQueryTests.java b/src/test/java/org/elasticsearch/test/integration/search/query/SimpleQueryTests.java index 733a5aed140..64a12971f6b 100644 --- a/src/test/java/org/elasticsearch/test/integration/search/query/SimpleQueryTests.java +++ b/src/test/java/org/elasticsearch/test/integration/search/query/SimpleQueryTests.java @@ -485,4 +485,41 @@ public class SimpleQueryTests extends AbstractNodesTests { assertThat("1", equalTo(searchResponse.hits().getAt(0).id())); } + @Test + public void testMatchQueryZeroTermsQuery() { + try { + client.admin().indices().prepareDelete("test").execute().actionGet(); + } catch (Exception e) { + // ignore + } + + client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("number_of_shards", 1)).execute().actionGet(); + client.prepareIndex("test", "type1", "1").setSource("field1", "value1").execute().actionGet(); + client.prepareIndex("test", "type1", "2").setSource("field1", "value2").execute().actionGet(); + client.admin().indices().prepareRefresh("test").execute().actionGet(); + + BoolQueryBuilder boolQuery = boolQuery() + .must(matchQuery("field1", "a").zeroTermsQuery(MatchQueryBuilder.ZeroTermsQuery.NONE)) + .must(matchQuery("field1", "value1").zeroTermsQuery(MatchQueryBuilder.ZeroTermsQuery.NONE)); + SearchResponse searchResponse = client.prepareSearch() + .setQuery(boolQuery) + .execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(0l)); + + boolQuery = boolQuery() + .must(matchQuery("field1", "a").zeroTermsQuery(MatchQueryBuilder.ZeroTermsQuery.ALL)) + .must(matchQuery("field1", "value1").zeroTermsQuery(MatchQueryBuilder.ZeroTermsQuery.ALL)); + searchResponse = client.prepareSearch() + .setQuery(boolQuery) + .execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(1l)); + + boolQuery = boolQuery() + .must(matchQuery("field1", "a").zeroTermsQuery(MatchQueryBuilder.ZeroTermsQuery.ALL)); + searchResponse = client.prepareSearch() + .setQuery(boolQuery) + .execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(2l)); + } + }