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:
Shawn Pearce
2013-06-28 10:54:24 -07:00
parent 9c4720405b
commit 268cee31aa
2 changed files with 56 additions and 15 deletions

View File

@@ -10,6 +10,7 @@ java_library(
'//gerrit-reviewdb:server',
'//gerrit-server:server',
'//lib:gwtorm',
'//lib:guava',
'//lib/lucene:core',
],
visibility = ['PUBLIC'],

View File

@@ -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(