Parameterize Lucene's QueryBuilder
Change-Id: I02f2e7b7b929f5cb25b56a642f89c56dce85a405
This commit is contained in:
@@ -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) {
|
||||||
|
@@ -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) {
|
||||||
|
Reference in New Issue
Block a user