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
diff --git a/gerrit-lucene/BUCK b/gerrit-lucene/BUCK
index 67dd218..5e9f376 100644
--- a/gerrit-lucene/BUCK
+++ b/gerrit-lucene/BUCK
@@ -10,6 +10,7 @@
'//gerrit-reviewdb:server',
'//gerrit-server:server',
'//lib:gwtorm',
+ '//lib:guava',
'//lib/lucene:core',
],
visibility = ['PUBLIC'],
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
index e1dc7c1..f491bc2 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -18,6 +18,7 @@
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.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.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 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 @@
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(