minimum_number_should_match in a query_string, closes #1420.

This commit is contained in:
Shay Banon 2011-10-26 01:58:12 +02:00
parent b652c655d2
commit 0cde90fcb1
7 changed files with 108 additions and 0 deletions

View File

@ -43,6 +43,7 @@ public class QueryParserSettings {
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 class QueryParserSettings {
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 class QueryParserSettings {
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 File

@ -26,9 +26,11 @@ import org.apache.lucene.search.DisjunctionMaxQuery;
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 class Queries {
}
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 File

@ -88,6 +88,8 @@ public class BoolQueryParser implements QueryParser {
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 File

@ -67,6 +67,8 @@ public class FieldQueryBuilder extends BaseQueryBuilder {
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 class FieldQueryBuilder extends BaseQueryBuilder {
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 class FieldQueryBuilder extends BaseQueryBuilder {
if (rewrite != null) {
builder.field("rewrite", rewrite);
}
if (minimumShouldMatch != null) {
builder.field("minimum_should_match", minimumShouldMatch);
}
builder.endObject();
}
builder.endObject();

View File

@ -22,8 +22,10 @@ package org.elasticsearch.index.query;
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 @@ public class FieldQueryParser implements QueryParser {
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 @@ public class FieldQueryParser implements QueryParser {
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 File

@ -80,6 +80,8 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
private String rewrite = null;
private String minimumShouldMatch;
public QueryStringQueryBuilder(String queryString) {
this.queryString = queryString;
}
@ -242,6 +244,11 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
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 class QueryStringQueryBuilder extends BaseQueryBuilder {
if (rewrite != null) {
builder.field("rewrite", rewrite);
}
if (minimumShouldMatch != null) {
builder.field("minimum_should_write", minimumShouldMatch);
}
builder.endObject();
}
}

View File

@ -22,10 +22,12 @@ package org.elasticsearch.index.query;
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 @@ public class QueryStringQueryParser implements QueryParser {
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 @@ public class QueryStringQueryParser implements QueryParser {
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) {