Fix -owner:self query
Lucene does not support negation without using the all docs query to subtract the negated results from. Try to simplify the query tree when negated operators appear by using MUST_NOT in a parent's BooleanQuery instead of creating a child that scans all documents. Change-Id: Idd37202fcd908534e22169c0c87405d363e0b71f
This commit is contained in:
@@ -10,6 +10,7 @@ java_library(
|
||||
'//gerrit-reviewdb:server',
|
||||
'//gerrit-server:server',
|
||||
'//lib:gwtorm',
|
||||
'//lib:guava',
|
||||
'//lib/lucene:core',
|
||||
],
|
||||
visibility = ['PUBLIC'],
|
||||
|
@@ -18,6 +18,7 @@ import static org.apache.lucene.search.BooleanClause.Occur.MUST;
|
||||
import static org.apache.lucene.search.BooleanClause.Occur.MUST_NOT;
|
||||
import static org.apache.lucene.search.BooleanClause.Occur.SHOULD;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.server.index.ChangeField;
|
||||
import com.google.gerrit.server.index.FieldType;
|
||||
import com.google.gerrit.server.index.IndexPredicate;
|
||||
@@ -32,9 +33,9 @@ import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.SortKeyPredicate;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.FuzzyQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.NumericRangeQuery;
|
||||
import org.apache.lucene.search.PrefixQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
@@ -44,6 +45,7 @@ import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
public class QueryBuilder {
|
||||
private static final String ID_FIELD = ChangeField.LEGACY_ID.getName();
|
||||
@@ -55,28 +57,66 @@ public class QueryBuilder {
|
||||
public static Query toQuery(Predicate<ChangeData> p)
|
||||
throws QueryParseException {
|
||||
if (p instanceof AndPredicate) {
|
||||
return booleanQuery(p, MUST);
|
||||
return and(p);
|
||||
} else if (p instanceof OrPredicate) {
|
||||
return booleanQuery(p, SHOULD);
|
||||
return or(p);
|
||||
} else if (p instanceof NotPredicate) {
|
||||
if (p.getChild(0) instanceof TimestampRangePredicate) {
|
||||
return notTimestampQuery(
|
||||
(TimestampRangePredicate<ChangeData>) p.getChild(0));
|
||||
}
|
||||
return booleanQuery(p, MUST_NOT);
|
||||
return not(p);
|
||||
} else if (p instanceof IndexPredicate) {
|
||||
return fieldQuery((IndexPredicate<ChangeData>) p);
|
||||
} else {
|
||||
throw new QueryParseException("Cannot convert to index predicate: " + p);
|
||||
throw new QueryParseException("cannot create query for index: " + p);
|
||||
}
|
||||
}
|
||||
|
||||
private static Query booleanQuery(Predicate<ChangeData> p, BooleanClause.Occur o)
|
||||
throws QueryParseException {
|
||||
BooleanQuery q = new BooleanQuery();
|
||||
for (int i = 0; i < p.getChildCount(); i++) {
|
||||
q.add(toQuery(p.getChild(i)), o);
|
||||
private static Query or(Predicate<ChangeData> p) throws QueryParseException {
|
||||
try {
|
||||
BooleanQuery q = new BooleanQuery();
|
||||
for (int i = 0; i < p.getChildCount(); i++) {
|
||||
q.add(toQuery(p.getChild(i)), SHOULD);
|
||||
}
|
||||
return q;
|
||||
} catch (BooleanQuery.TooManyClauses e) {
|
||||
throw new QueryParseException("cannot create query for index: " + p, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Query and(Predicate<ChangeData> p) throws QueryParseException {
|
||||
try {
|
||||
BooleanQuery b = new BooleanQuery();
|
||||
List<Query> not = Lists.newArrayListWithCapacity(p.getChildCount());
|
||||
for (int i = 0; i < p.getChildCount(); i++) {
|
||||
Predicate<ChangeData> c = p.getChild(i);
|
||||
if (c instanceof NotPredicate) {
|
||||
Predicate<ChangeData> n = c.getChild(0);
|
||||
if (n instanceof TimestampRangePredicate) {
|
||||
b.add(notTimestamp((TimestampRangePredicate<ChangeData>) n), MUST);
|
||||
} else {
|
||||
not.add(toQuery(n));
|
||||
}
|
||||
} else {
|
||||
b.add(toQuery(c), MUST);
|
||||
}
|
||||
}
|
||||
for (Query q : not) {
|
||||
b.add(q, MUST_NOT);
|
||||
}
|
||||
return b;
|
||||
} catch (BooleanQuery.TooManyClauses e) {
|
||||
throw new QueryParseException("cannot create query for index: " + p, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Query not(Predicate<ChangeData> p) throws QueryParseException {
|
||||
Predicate<ChangeData> n = p.getChild(0);
|
||||
if (n instanceof TimestampRangePredicate) {
|
||||
return notTimestamp((TimestampRangePredicate<ChangeData>) n);
|
||||
}
|
||||
|
||||
// Lucene does not support negation, start with all and subtract.
|
||||
BooleanQuery q = new BooleanQuery();
|
||||
q.add(new MatchAllDocsQuery(), MUST);
|
||||
q.add(toQuery(n), MUST_NOT);
|
||||
return q;
|
||||
}
|
||||
|
||||
@@ -140,7 +180,7 @@ public class QueryBuilder {
|
||||
throw new QueryParseException("not a timestamp: " + p);
|
||||
}
|
||||
|
||||
private static Query notTimestampQuery(TimestampRangePredicate<ChangeData> r)
|
||||
private static Query notTimestamp(TimestampRangePredicate<ChangeData> r)
|
||||
throws QueryParseException {
|
||||
if (r.getMinTimestamp().getTime() == 0) {
|
||||
return NumericRangeQuery.newIntRange(
|
||||
|
Reference in New Issue
Block a user