Parameterize Lucene's QueryBuilder

Change-Id: I02f2e7b7b929f5cb25b56a642f89c56dce85a405
This commit is contained in:
Dave Borowitz
2016-03-16 13:15:51 +01:00
parent 1068a6904f
commit aa01c29b5b
2 changed files with 50 additions and 47 deletions

View File

@@ -126,6 +126,14 @@ public class LuceneChangeIndex implements ChangeIndex {
private static final String ID_SORT_FIELD = private static final String ID_SORT_FIELD =
sortFieldName(ChangeField.LEGACY_ID); sortFieldName(ChangeField.LEGACY_ID);
static Term idTerm(ChangeData cd) {
return QueryBuilder.intTerm(LEGACY_ID.getName(), cd.getId().get());
}
static Term idTerm(Change.Id id) {
return QueryBuilder.intTerm(LEGACY_ID.getName(), id.get());
}
private static String sortFieldName(FieldDef<?, ?> f) { private static String sortFieldName(FieldDef<?, ?> f) {
return f.getName() + "_SORT"; return f.getName() + "_SORT";
} }
@@ -140,7 +148,7 @@ public class LuceneChangeIndex implements ChangeIndex {
private final Provider<ReviewDb> db; private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory; private final ChangeData.Factory changeDataFactory;
private final Schema<ChangeData> schema; private final Schema<ChangeData> schema;
private final QueryBuilder queryBuilder; private final QueryBuilder<ChangeData> queryBuilder;
private final ChangeSubIndex openIndex; private final ChangeSubIndex openIndex;
private final ChangeSubIndex closedIndex; private final ChangeSubIndex closedIndex;
@@ -165,7 +173,7 @@ public class LuceneChangeIndex implements ChangeIndex {
GerritIndexWriterConfig closedConfig = GerritIndexWriterConfig closedConfig =
new GerritIndexWriterConfig(cfg, "changes_closed"); new GerritIndexWriterConfig(cfg, "changes_closed");
queryBuilder = new QueryBuilder(openConfig.getAnalyzer()); queryBuilder = new QueryBuilder<>(schema, openConfig.getAnalyzer());
SearcherFactory searcherFactory = new SearcherFactory(); SearcherFactory searcherFactory = new SearcherFactory();
if (LuceneIndexModule.isInMemoryTest(cfg)) { if (LuceneIndexModule.isInMemoryTest(cfg)) {
@@ -207,7 +215,7 @@ public class LuceneChangeIndex implements ChangeIndex {
@Override @Override
public void replace(ChangeData cd) throws IOException { public void replace(ChangeData cd) throws IOException {
Term id = QueryBuilder.idTerm(cd); Term id = LuceneChangeIndex.idTerm(cd);
Document doc = toDocument(cd); Document doc = toDocument(cd);
try { try {
if (cd.change().getStatus().isOpen()) { if (cd.change().getStatus().isOpen()) {
@@ -226,7 +234,7 @@ public class LuceneChangeIndex implements ChangeIndex {
@Override @Override
public void delete(Change.Id id) throws IOException { public void delete(Change.Id id) throws IOException {
Term idTerm = QueryBuilder.idTerm(id); Term idTerm = LuceneChangeIndex.idTerm(id);
try { try {
Futures.allAsList( Futures.allAsList(
openIndex.delete(idTerm), openIndex.delete(idTerm),
@@ -498,12 +506,12 @@ public class LuceneChangeIndex implements ChangeIndex {
return result; return result;
} }
private void add(Document doc, Values<ChangeData> values) { private static <V> void add(Document doc, Values<V> values) {
String name = values.getField().getName(); String name = values.getField().getName();
FieldType<?> type = values.getField().getType(); FieldType<?> type = values.getField().getType();
Store store = store(values.getField()); Store store = store(values.getField());
FieldDef<ChangeData, ?> f = values.getField(); FieldDef<V, ?> f = values.getField();
// Add separate DocValues fields for those fields needed for sorting. // Add separate DocValues fields for those fields needed for sorting.
if (f == ChangeField.LEGACY_ID) { if (f == ChangeField.LEGACY_ID) {

View File

@@ -14,24 +14,23 @@
package com.google.gerrit.lucene; package com.google.gerrit.lucene;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID; import static com.google.common.base.Preconditions.checkArgument;
import static org.apache.lucene.search.BooleanClause.Occur.MUST; 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.MUST_NOT;
import static org.apache.lucene.search.BooleanClause.Occur.SHOULD; import static org.apache.lucene.search.BooleanClause.Occur.SHOULD;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.FieldType; import com.google.gerrit.server.index.FieldType;
import com.google.gerrit.server.index.IndexPredicate; import com.google.gerrit.server.index.IndexPredicate;
import com.google.gerrit.server.index.IntegerRangePredicate; import com.google.gerrit.server.index.IntegerRangePredicate;
import com.google.gerrit.server.index.RegexPredicate; import com.google.gerrit.server.index.RegexPredicate;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.TimestampRangePredicate; import com.google.gerrit.server.index.TimestampRangePredicate;
import com.google.gerrit.server.query.AndPredicate; import com.google.gerrit.server.query.AndPredicate;
import com.google.gerrit.server.query.NotPredicate; import com.google.gerrit.server.query.NotPredicate;
import com.google.gerrit.server.query.OrPredicate; import com.google.gerrit.server.query.OrPredicate;
import com.google.gerrit.server.query.Predicate; import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException; import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
@@ -48,23 +47,22 @@ import org.apache.lucene.util.NumericUtils;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
public class QueryBuilder { public class QueryBuilder<V> {
static Term intTerm(String name, int value) {
public static Term idTerm(ChangeData cd) { BytesRefBuilder builder = new BytesRefBuilder();
return intTerm(LEGACY_ID.getName(), cd.getId().get()); NumericUtils.intToPrefixCodedBytes(value, 0, builder);
} return new Term(name, builder.get());
public static Term idTerm(Change.Id id) {
return intTerm(LEGACY_ID.getName(), id.get());
} }
private final Schema<V> schema;
private final org.apache.lucene.util.QueryBuilder queryBuilder; private final org.apache.lucene.util.QueryBuilder queryBuilder;
public QueryBuilder(Analyzer analyzer) { public QueryBuilder(Schema<V> schema, Analyzer analyzer) {
this.schema = schema;
queryBuilder = new org.apache.lucene.util.QueryBuilder(analyzer); queryBuilder = new org.apache.lucene.util.QueryBuilder(analyzer);
} }
public Query toQuery(Predicate<ChangeData> p) throws QueryParseException { public Query toQuery(Predicate<V> p) throws QueryParseException {
if (p instanceof AndPredicate) { if (p instanceof AndPredicate) {
return and(p); return and(p);
} else if (p instanceof OrPredicate) { } else if (p instanceof OrPredicate) {
@@ -72,13 +70,13 @@ public class QueryBuilder {
} else if (p instanceof NotPredicate) { } else if (p instanceof NotPredicate) {
return not(p); return not(p);
} else if (p instanceof IndexPredicate) { } else if (p instanceof IndexPredicate) {
return fieldQuery((IndexPredicate<ChangeData>) p); return fieldQuery((IndexPredicate<V>) p);
} else { } else {
throw new QueryParseException("cannot create query for index: " + p); throw new QueryParseException("cannot create query for index: " + p);
} }
} }
private Query or(Predicate<ChangeData> p) private Query or(Predicate<V> p)
throws QueryParseException { throws QueryParseException {
try { try {
BooleanQuery.Builder q = new BooleanQuery.Builder(); BooleanQuery.Builder q = new BooleanQuery.Builder();
@@ -91,17 +89,17 @@ public class QueryBuilder {
} }
} }
private Query and(Predicate<ChangeData> p) private Query and(Predicate<V> p)
throws QueryParseException { throws QueryParseException {
try { try {
BooleanQuery.Builder b = new BooleanQuery.Builder(); BooleanQuery.Builder b = new BooleanQuery.Builder();
List<Query> not = Lists.newArrayListWithCapacity(p.getChildCount()); List<Query> not = Lists.newArrayListWithCapacity(p.getChildCount());
for (int i = 0; i < p.getChildCount(); i++) { for (int i = 0; i < p.getChildCount(); i++) {
Predicate<ChangeData> c = p.getChild(i); Predicate<V> c = p.getChild(i);
if (c instanceof NotPredicate) { if (c instanceof NotPredicate) {
Predicate<ChangeData> n = c.getChild(0); Predicate<V> n = c.getChild(0);
if (n instanceof TimestampRangePredicate) { if (n instanceof TimestampRangePredicate) {
b.add(notTimestamp((TimestampRangePredicate<ChangeData>) n), MUST); b.add(notTimestamp((TimestampRangePredicate<V>) n), MUST);
} else { } else {
not.add(toQuery(n)); not.add(toQuery(n));
} }
@@ -118,11 +116,11 @@ public class QueryBuilder {
} }
} }
private Query not(Predicate<ChangeData> p) private Query not(Predicate<V> p)
throws QueryParseException { throws QueryParseException {
Predicate<ChangeData> n = p.getChild(0); Predicate<V> n = p.getChild(0);
if (n instanceof TimestampRangePredicate) { if (n instanceof TimestampRangePredicate) {
return notTimestamp((TimestampRangePredicate<ChangeData>) n); return notTimestamp((TimestampRangePredicate<V>) n);
} }
// Lucene does not support negation, start with all and subtract. // Lucene does not support negation, start with all and subtract.
@@ -132,8 +130,11 @@ public class QueryBuilder {
.build(); .build();
} }
private Query fieldQuery(IndexPredicate<ChangeData> p) private Query fieldQuery(IndexPredicate<V> p)
throws QueryParseException { throws QueryParseException {
checkArgument(schema.hasField(p.getField()),
"field not in schema v%s: %s", schema.getVersion(),
p.getField().getName());
if (p.getType() == FieldType.INTEGER) { if (p.getType() == FieldType.INTEGER) {
return intQuery(p); return intQuery(p);
} else if (p.getType() == FieldType.INTEGER_RANGE) { } else if (p.getType() == FieldType.INTEGER_RANGE) {
@@ -151,13 +152,7 @@ public class QueryBuilder {
} }
} }
private static Term intTerm(String name, int value) { private Query intQuery(IndexPredicate<V> p)
BytesRefBuilder builder = new BytesRefBuilder();
NumericUtils.intToPrefixCodedBytes(value, 0, builder);
return new Term(name, builder.get());
}
private Query intQuery(IndexPredicate<ChangeData> p)
throws QueryParseException { throws QueryParseException {
int value; int value;
try { try {
@@ -170,11 +165,11 @@ public class QueryBuilder {
return new TermQuery(intTerm(p.getField().getName(), value)); return new TermQuery(intTerm(p.getField().getName(), value));
} }
private Query intRangeQuery(IndexPredicate<ChangeData> p) private Query intRangeQuery(IndexPredicate<V> p)
throws QueryParseException { throws QueryParseException {
if (p instanceof IntegerRangePredicate) { if (p instanceof IntegerRangePredicate) {
IntegerRangePredicate<ChangeData> r = IntegerRangePredicate<V> r =
(IntegerRangePredicate<ChangeData>) p; (IntegerRangePredicate<V>) p;
int minimum = r.getMinimumValue(); int minimum = r.getMinimumValue();
int maximum = r.getMaximumValue(); int maximum = r.getMaximumValue();
if (minimum == maximum) { if (minimum == maximum) {
@@ -192,11 +187,11 @@ public class QueryBuilder {
throw new QueryParseException("not an integer range: " + p); throw new QueryParseException("not an integer range: " + p);
} }
private Query timestampQuery(IndexPredicate<ChangeData> p) private Query timestampQuery(IndexPredicate<V> p)
throws QueryParseException { throws QueryParseException {
if (p instanceof TimestampRangePredicate) { if (p instanceof TimestampRangePredicate) {
TimestampRangePredicate<ChangeData> r = TimestampRangePredicate<V> r =
(TimestampRangePredicate<ChangeData>) p; (TimestampRangePredicate<V>) p;
return NumericRangeQuery.newLongRange( return NumericRangeQuery.newLongRange(
r.getField().getName(), r.getField().getName(),
r.getMinTimestamp().getTime(), r.getMinTimestamp().getTime(),
@@ -206,7 +201,7 @@ public class QueryBuilder {
throw new QueryParseException("not a timestamp: " + p); throw new QueryParseException("not a timestamp: " + p);
} }
private Query notTimestamp(TimestampRangePredicate<ChangeData> r) private Query notTimestamp(TimestampRangePredicate<V> r)
throws QueryParseException { throws QueryParseException {
if (r.getMinTimestamp().getTime() == 0) { if (r.getMinTimestamp().getTime() == 0) {
return NumericRangeQuery.newLongRange( return NumericRangeQuery.newLongRange(
@@ -218,7 +213,7 @@ public class QueryBuilder {
throw new QueryParseException("cannot negate: " + r); throw new QueryParseException("cannot negate: " + r);
} }
private Query exactQuery(IndexPredicate<ChangeData> p) { private Query exactQuery(IndexPredicate<V> p) {
if (p instanceof RegexPredicate<?>) { if (p instanceof RegexPredicate<?>) {
return regexQuery(p); return regexQuery(p);
} else { } else {
@@ -226,7 +221,7 @@ public class QueryBuilder {
} }
} }
private Query regexQuery(IndexPredicate<ChangeData> p) { private Query regexQuery(IndexPredicate<V> p) {
String re = p.getValue(); String re = p.getValue();
if (re.startsWith("^")) { if (re.startsWith("^")) {
re = re.substring(1); re = re.substring(1);
@@ -237,11 +232,11 @@ public class QueryBuilder {
return new RegexpQuery(new Term(p.getField().getName(), re)); return new RegexpQuery(new Term(p.getField().getName(), re));
} }
private Query prefixQuery(IndexPredicate<ChangeData> p) { private Query prefixQuery(IndexPredicate<V> p) {
return new PrefixQuery(new Term(p.getField().getName(), p.getValue())); return new PrefixQuery(new Term(p.getField().getName(), p.getValue()));
} }
private Query fullTextQuery(IndexPredicate<ChangeData> p) private Query fullTextQuery(IndexPredicate<V> p)
throws QueryParseException { throws QueryParseException {
String value = p.getValue(); String value = p.getValue();
if (value == null) { if (value == null) {