From b979dfa0be8bc7536c4185156a5629f2966c7d77 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 9 Aug 2012 21:27:48 +0200 Subject: [PATCH] Add lenient option to match & multi_match queries. #2156 --- .../index/query/MatchQueryBuilder.java | 15 ++++++++++++ .../index/query/MatchQueryParser.java | 2 ++ .../index/query/MultiMatchQueryBuilder.java | 16 ++++++++++++- .../index/query/MultiMatchQueryParser.java | 2 ++ .../index/search/MatchQuery.java | 20 +++++++++++++++- .../index/search/MultiMatchQuery.java | 16 +++++++++---- .../search/query/SimpleQueryTests.java | 24 +++++++++++++++++++ 7 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java index c581763dc50..44d52dfc935 100644 --- a/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java @@ -73,8 +73,11 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer private String minimumShouldMatch; private String rewrite = null; + private String fuzzyRewrite = null; + private Boolean lenient; + /** * Constructs a new text query. */ @@ -161,6 +164,14 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer return this; } + /** + * Sets whether format based failures will be ignored. + */ + public MatchQueryBuilder setLenient(boolean lenient) { + this.lenient = lenient; + return this; + } + @Override public void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(MatchQueryParser.NAME); @@ -201,6 +212,10 @@ public class MatchQueryBuilder extends BaseQueryBuilder implements BoostableQuer builder.field("fuzzy_rewrite", fuzzyRewrite); } + if (lenient != null) { + builder.field("lenient", lenient); + } + 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 c2bf9674b10..0df86025a94 100644 --- a/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/MatchQueryParser.java @@ -122,6 +122,8 @@ public class MatchQueryParser implements QueryParser { matchQuery.setRewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull(), null)); } else if ("fuzzy_rewrite".equals(currentFieldName) || "fuzzyRewrite".equals(currentFieldName)) { matchQuery.setFuzzyRewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull(), null)); + } else if ("lenient".equals(currentFieldName)) { + matchQuery.setLenient(parser.booleanValue()); } else { throw new QueryParsingException(parseContext.index(), "[match] query does not support [" + currentFieldName + "]"); } diff --git a/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java index 39bdb072658..883d7838aa3 100644 --- a/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -61,6 +61,8 @@ public class MultiMatchQueryBuilder extends BaseQueryBuilder implements Boostabl private Integer tieBreaker; + private Boolean lenient; + /** * Constructs a new text query. */ @@ -152,11 +154,19 @@ public class MultiMatchQueryBuilder extends BaseQueryBuilder implements Boostabl return this; } - public MultiMatchQueryBuilder setTieBreaker(Integer tieBreaker) { + public MultiMatchQueryBuilder tieBreaker(Integer tieBreaker) { this.tieBreaker = tieBreaker; return this; } + /** + * Sets whether format based failures will be ignored. + */ + public MultiMatchQueryBuilder lenient(boolean lenient) { + this.lenient = lenient; + return this; + } + @Override public void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(MultiMatchQueryParser.NAME); @@ -206,6 +216,10 @@ public class MultiMatchQueryBuilder extends BaseQueryBuilder implements Boostabl builder.field("tie_breaker", tieBreaker); } + if (lenient != null) { + builder.field("lenient", lenient); + } + builder.endObject(); } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/index/query/MultiMatchQueryParser.java b/src/main/java/org/elasticsearch/index/query/MultiMatchQueryParser.java index 282ee16a568..4e39f537316 100644 --- a/src/main/java/org/elasticsearch/index/query/MultiMatchQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/MultiMatchQueryParser.java @@ -154,6 +154,8 @@ public class MultiMatchQueryParser implements QueryParser { multiMatchQuery.setUseDisMax(parser.booleanValue()); } else if ("tie_breaker".equals(currentFieldName) || "tieBreaker".equals(currentFieldName)) { multiMatchQuery.setTieBreaker(parser.intValue()); + } else if ("lenient".equals(currentFieldName)) { + multiMatchQuery.setLenient(parser.booleanValue()); } 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 0249fe6c653..55eed4b1787 100644 --- a/src/main/java/org/elasticsearch/index/search/MatchQuery.java +++ b/src/main/java/org/elasticsearch/index/search/MatchQuery.java @@ -68,6 +68,8 @@ public class MatchQuery { protected MultiTermQuery.RewriteMethod rewriteMethod; protected MultiTermQuery.RewriteMethod fuzzyRewriteMethod; + protected boolean lenient; + public MatchQuery(QueryParseContext parseContext) { this.parseContext = parseContext; } @@ -108,6 +110,10 @@ public class MatchQuery { this.fuzzyRewriteMethod = fuzzyRewriteMethod; } + public void setLenient(boolean lenient) { + this.lenient = lenient; + } + public Query parse(Type type, String fieldName, String text) { FieldMapper mapper = null; Term fieldTerm; @@ -124,11 +130,23 @@ public class MatchQuery { String[] previousTypes = QueryParseContext.setTypesWithPrevious(new String[]{smartNameFieldMappers.docMapper().type()}); try { return wrapSmartNameQuery(mapper.fieldQuery(text, parseContext), smartNameFieldMappers, parseContext); + } catch (RuntimeException e) { + if (lenient) { + return null; + } + throw e; } finally { QueryParseContext.setTypes(previousTypes); } } else { - return wrapSmartNameQuery(mapper.fieldQuery(text, parseContext), smartNameFieldMappers, parseContext); + try { + return wrapSmartNameQuery(mapper.fieldQuery(text, parseContext), smartNameFieldMappers, parseContext); + } catch (RuntimeException e) { + if (lenient) { + return null; + } + throw e; + } } } diff --git a/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java b/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java index 328c3543232..fc283073a38 100644 --- a/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java +++ b/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java @@ -51,16 +51,24 @@ public class MultiMatchQuery extends MatchQuery { if (useDisMax) { DisjunctionMaxQuery disMaxQuery = new DisjunctionMaxQuery(tieBreaker); + boolean clauseAdded = false; for (String fieldName : fieldNames) { - disMaxQuery.add(parse(type, fieldName, text)); + Query query = parse(type, fieldName, text); + if (query != null) { + clauseAdded = true; + disMaxQuery.add(query); + } } - return disMaxQuery; + return clauseAdded ? disMaxQuery : null; } else { BooleanQuery booleanQuery = new BooleanQuery(); for (String fieldName : fieldNames) { - booleanQuery.add(parse(type, fieldName, text), BooleanClause.Occur.SHOULD); + Query query = parse(type, fieldName, text); + if (query != null) { + booleanQuery.add(query, BooleanClause.Occur.SHOULD); + } } - return booleanQuery; + return !booleanQuery.clauses().isEmpty() ? booleanQuery : null; } } 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 84983114e42..e1edb39b03c 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 @@ -19,10 +19,13 @@ package org.elasticsearch.test.integration.search.query; +import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.index.query.*; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.facet.FacetBuilders; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterClass; @@ -37,6 +40,8 @@ import static org.elasticsearch.index.query.QueryBuilders.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.testng.Assert.fail; /** * @@ -390,6 +395,25 @@ public class SimpleQueryTests extends AbstractNodesTests { .execute().actionGet(); assertThat(searchResponse.hits().totalHits(), equalTo(1l)); assertThat("1", equalTo(searchResponse.hits().getAt(0).id())); + + // Test lenient + client.prepareIndex("test", "type1", "3").setSource("field1", "value7", "field2", "value8", "field3", 5).execute().actionGet(); + client.admin().indices().prepareRefresh("test").execute().actionGet(); + + builder = QueryBuilders.multiMatchQuery("value1", "field1", "field2", "field3"); + try { + client.prepareSearch() + .setQuery(builder) + .execute().actionGet(); + fail("Exception expected"); + } catch (SearchPhaseExecutionException e) { + assertThat(e.shardFailures()[0].status(), equalTo(RestStatus.BAD_REQUEST)); + } + + builder.lenient(true); + searchResponse = client.prepareSearch().setQuery(builder).execute().actionGet(); + assertThat(searchResponse.hits().totalHits(), equalTo(1l)); + assertThat("1", equalTo(searchResponse.hits().getAt(0).id())); } }