Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

minimum_number_should_match in a query_string, closes #1420.

  • Loading branch information...
commit 0cde90fcb10c93c56d5f7d6c9228002ffdff1fec 1 parent b652c65
@kimchy kimchy authored
View
11 modules/elasticsearch/src/main/java/org/apache/lucene/queryParser/QueryParserSettings.java
@@ -43,6 +43,7 @@
private boolean escape = false;
private Analyzer analyzer = null;
private MultiTermQuery.RewriteMethod rewriteMethod = MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
+ private String minimumShouldMatch;
public String queryString() {
return queryString;
@@ -164,6 +165,14 @@ public void rewriteMethod(MultiTermQuery.RewriteMethod rewriteMethod) {
this.rewriteMethod = rewriteMethod;
}
+ public String minimumShouldMatch() {
+ return this.minimumShouldMatch;
+ }
+
+ public void minimumShouldMatch(String minimumShouldMatch) {
+ this.minimumShouldMatch = minimumShouldMatch;
+ }
+
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -186,6 +195,8 @@ public void rewriteMethod(MultiTermQuery.RewriteMethod rewriteMethod) {
if (queryString != null ? !queryString.equals(that.queryString) : that.queryString != null) return false;
if (rewriteMethod != null ? !rewriteMethod.equals(that.rewriteMethod) : that.rewriteMethod != null)
return false;
+ if (minimumShouldMatch != null ? !minimumShouldMatch.equals(that.minimumShouldMatch) : that.minimumShouldMatch != null)
+ return false;
return true;
}
View
61 modules/elasticsearch/src/main/java/org/elasticsearch/common/lucene/search/Queries.java
@@ -26,9 +26,11 @@
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
+import org.elasticsearch.common.Nullable;
import java.lang.reflect.Field;
import java.util.List;
+import java.util.regex.Pattern;
/**
* @author kimchy (shay.banon)
@@ -123,4 +125,63 @@ public static boolean isMatchAllQuery(Query query) {
}
return false;
}
+
+ public static void applyMinimumShouldMatch(BooleanQuery query, @Nullable String minimumShouldMatch) {
+ if (minimumShouldMatch == null) {
+ return;
+ }
+ int optionalClauses = 0;
+ for (BooleanClause c : query.clauses()) {
+ if (c.getOccur() == BooleanClause.Occur.SHOULD) {
+ optionalClauses++;
+ }
+ }
+
+ int msm = calculateMinShouldMatch(optionalClauses, minimumShouldMatch);
+ if (0 < msm) {
+ query.setMinimumNumberShouldMatch(msm);
+ }
+ }
+
+ private static Pattern spaceAroundLessThanPattern = Pattern.compile("(\\s+<\\s*)|(\\s*<\\s+)");
+ private static Pattern spacePattern = Pattern.compile(" ");
+ private static Pattern lessThanPattern = Pattern.compile("<");
+
+ static int calculateMinShouldMatch(int optionalClauseCount, String spec) {
+ int result = optionalClauseCount;
+ spec = spec.trim();
+
+ if (-1 < spec.indexOf("<")) {
+ /* we have conditional spec(s) */
+ spec = spaceAroundLessThanPattern.matcher(spec).replaceAll("<");
+ for (String s : spacePattern.split(spec)) {
+ String[] parts = lessThanPattern.split(s, 0);
+ int upperBound = Integer.parseInt(parts[0]);
+ if (optionalClauseCount <= upperBound) {
+ return result;
+ } else {
+ result = calculateMinShouldMatch
+ (optionalClauseCount, parts[1]);
+ }
+ }
+ return result;
+ }
+
+ /* otherwise, simple expresion */
+
+ if (-1 < spec.indexOf('%')) {
+ /* percentage - assume the % was the last char. If not, let Integer.parseInt fail. */
+ spec = spec.substring(0, spec.length() - 1);
+ int percent = Integer.parseInt(spec);
+ float calc = (result * percent) * (1 / 100f);
+ result = calc < 0 ? result + (int) calc : (int) calc;
+ } else {
+ int calc = Integer.parseInt(spec);
+ result = calc < 0 ? result + calc : calc;
+ }
+
+ return (optionalClauseCount < result ?
+ optionalClauseCount : (result < 0 ? 0 : result));
+
+ }
}
View
2  modules/elasticsearch/src/main/java/org/elasticsearch/index/query/BoolQueryParser.java
@@ -88,6 +88,8 @@
disableCoord = parser.booleanValue();
} else if ("minimum_number_should_match".equals(currentFieldName) || "minimumNumberShouldMatch".equals(currentFieldName)) {
minimumNumberShouldMatch = parser.intValue();
+ } else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
+ minimumNumberShouldMatch = parser.intValue();
} else if ("boost".equals(currentFieldName)) {
boost = parser.floatValue();
}
View
10 modules/elasticsearch/src/main/java/org/elasticsearch/index/query/FieldQueryBuilder.java
@@ -67,6 +67,8 @@
private String rewrite;
+ private String minimumShouldMatch;
+
/**
* A query that executes the query string against a field. It is a simplified
* version of {@link QueryStringQueryBuilder} that simply runs against
@@ -276,6 +278,11 @@ public FieldQueryBuilder rewrite(String rewrite) {
return this;
}
+ public FieldQueryBuilder minimumShouldMatch(String minimumShouldMatch) {
+ this.minimumShouldMatch = minimumShouldMatch;
+ return this;
+ }
+
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(FieldQueryParser.NAME);
if (!extraSet) {
@@ -319,6 +326,9 @@ public FieldQueryBuilder rewrite(String rewrite) {
if (rewrite != null) {
builder.field("rewrite", rewrite);
}
+ if (minimumShouldMatch != null) {
+ builder.field("minimum_should_match", minimumShouldMatch);
+ }
builder.endObject();
}
builder.endObject();
View
7 modules/elasticsearch/src/main/java/org/elasticsearch/index/query/FieldQueryParser.java
@@ -22,8 +22,10 @@
import org.apache.lucene.queryParser.MapperQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParserSettings;
+import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.support.QueryParsers;
@@ -96,6 +98,8 @@
qpSettings.analyzeWildcard(parser.booleanValue());
} else if ("rewrite".equals(currentFieldName)) {
qpSettings.rewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull()));
+ } else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
+ qpSettings.minimumShouldMatch(parser.textOrNull());
}
}
}
@@ -129,6 +133,9 @@
query = queryParser.parse(qpSettings.queryString());
query.setBoost(qpSettings.boost());
query = optimizeQuery(fixNegativeQueryIfNeeded(query));
+ if (query instanceof BooleanQuery) {
+ Queries.applyMinimumShouldMatch((BooleanQuery) query, qpSettings.minimumShouldMatch());
+ }
parseContext.indexCache().queryParserCache().put(qpSettings, query);
return query;
} catch (ParseException e) {
View
10 ...es/elasticsearch/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java
@@ -80,6 +80,8 @@
private String rewrite = null;
+ private String minimumShouldMatch;
+
public QueryStringQueryBuilder(String queryString) {
this.queryString = queryString;
}
@@ -242,6 +244,11 @@ public QueryStringQueryBuilder rewrite(String rewrite) {
return this;
}
+ public QueryStringQueryBuilder minimumShouldMatch(String minimumShouldMatch) {
+ this.minimumShouldMatch = minimumShouldMatch;
+ return this;
+ }
+
/**
* Sets the boost for this query. Documents matching this query will (in addition to the normal
* weightings) have their score multiplied by the boost provided.
@@ -313,6 +320,9 @@ public QueryStringQueryBuilder boost(float boost) {
if (rewrite != null) {
builder.field("rewrite", rewrite);
}
+ if (minimumShouldMatch != null) {
+ builder.field("minimum_should_write", minimumShouldMatch);
+ }
builder.endObject();
}
}
View
7 ...les/elasticsearch/src/main/java/org/elasticsearch/index/query/QueryStringQueryParser.java
@@ -22,10 +22,12 @@
import org.apache.lucene.queryParser.MapperQueryParser;
import org.apache.lucene.queryParser.MultiFieldQueryParserSettings;
import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.trove.impl.Constants;
import org.elasticsearch.common.trove.map.hash.TObjectFloatHashMap;
@@ -147,6 +149,8 @@
qpSettings.analyzeWildcard(parser.booleanValue());
} else if ("rewrite".equals(currentFieldName)) {
qpSettings.rewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull()));
+ } else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
+ qpSettings.minimumShouldMatch(parser.textOrNull());
}
}
}
@@ -184,6 +188,9 @@
query = queryParser.parse(qpSettings.queryString());
query.setBoost(qpSettings.boost());
query = optimizeQuery(fixNegativeQueryIfNeeded(query));
+ if (query instanceof BooleanQuery) {
+ Queries.applyMinimumShouldMatch((BooleanQuery) query, qpSettings.minimumShouldMatch());
+ }
parseContext.indexCache().queryParserCache().put(qpSettings, query);
return query;
} catch (ParseException e) {
Please sign in to comment.
Something went wrong with that request. Please try again.