Push limit term into secondary index queries
Change-Id: Id4ec5fe0375229ff8b82ec46fbe300c78098dbbe
This commit is contained in:
parent
30b280d309
commit
76e7b42490
@ -231,7 +231,7 @@ public class LuceneChangeIndex implements ChangeIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p)
|
public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
Set<Change.Status> statuses = IndexRewriteImpl.getPossibleStatus(p);
|
Set<Change.Status> statuses = IndexRewriteImpl.getPossibleStatus(p);
|
||||||
List<SubIndex> indexes = Lists.newArrayListWithCapacity(2);
|
List<SubIndex> indexes = Lists.newArrayListWithCapacity(2);
|
||||||
@ -241,7 +241,7 @@ public class LuceneChangeIndex implements ChangeIndex {
|
|||||||
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
||||||
indexes.add(closedIndex);
|
indexes.add(closedIndex);
|
||||||
}
|
}
|
||||||
return new QuerySource(indexes, QueryBuilder.toQuery(p));
|
return new QuerySource(indexes, QueryBuilder.toQuery(p), limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -256,16 +256,16 @@ public class LuceneChangeIndex implements ChangeIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class QuerySource implements ChangeDataSource {
|
private static class QuerySource implements ChangeDataSource {
|
||||||
// TODO(dborowitz): Push limit down from predicate tree.
|
|
||||||
private static final int LIMIT = 1000;
|
|
||||||
private static final ImmutableSet<String> FIELDS = ImmutableSet.of(ID_FIELD);
|
private static final ImmutableSet<String> FIELDS = ImmutableSet.of(ID_FIELD);
|
||||||
|
|
||||||
private final List<SubIndex> indexes;
|
private final List<SubIndex> indexes;
|
||||||
private final Query query;
|
private final Query query;
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
public QuerySource(List<SubIndex> indexes, Query query) {
|
public QuerySource(List<SubIndex> indexes, Query query, int limit) {
|
||||||
this.indexes = indexes;
|
this.indexes = indexes;
|
||||||
this.query = query;
|
this.query = query;
|
||||||
|
this.limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -295,9 +295,9 @@ public class LuceneChangeIndex implements ChangeIndex {
|
|||||||
TopDocs[] hits = new TopDocs[indexes.size()];
|
TopDocs[] hits = new TopDocs[indexes.size()];
|
||||||
for (int i = 0; i < indexes.size(); i++) {
|
for (int i = 0; i < indexes.size(); i++) {
|
||||||
searchers[i] = indexes.get(i).acquire();
|
searchers[i] = indexes.get(i).acquire();
|
||||||
hits[i] = searchers[i].search(query, LIMIT, sort);
|
hits[i] = searchers[i].search(query, limit, sort);
|
||||||
}
|
}
|
||||||
TopDocs docs = TopDocs.merge(sort, LIMIT, hits);
|
TopDocs docs = TopDocs.merge(sort, limit, hits);
|
||||||
|
|
||||||
List<ChangeData> result =
|
List<ChangeData> result =
|
||||||
Lists.newArrayListWithCapacity(docs.scoreDocs.length);
|
Lists.newArrayListWithCapacity(docs.scoreDocs.length);
|
||||||
|
@ -62,7 +62,7 @@ public interface ChangeIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p) {
|
public ChangeDataSource getSource(Predicate<ChangeData> p, int limit) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +133,13 @@ public interface ChangeIndex {
|
|||||||
* @param p the predicate to match. Must be a tree containing only AND, OR,
|
* @param p the predicate to match. Must be a tree containing only AND, OR,
|
||||||
* or NOT predicates as internal nodes, and {@link IndexPredicate}s as
|
* or NOT predicates as internal nodes, and {@link IndexPredicate}s as
|
||||||
* leaves.
|
* leaves.
|
||||||
|
* @param limit maximum number of results to return.
|
||||||
* @return a source of documents matching the predicate.
|
* @return a source of documents matching the predicate.
|
||||||
*
|
*
|
||||||
* @throws QueryParseException if the predicate could not be converted to an
|
* @throws QueryParseException if the predicate could not be converted to an
|
||||||
* indexed data source.
|
* indexed data source.
|
||||||
*/
|
*/
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p)
|
public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
|
||||||
throws QueryParseException;
|
throws QueryParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.index;
|
package com.google.gerrit.server.index;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
@ -28,6 +29,7 @@ import com.google.gerrit.server.query.QueryRewriter;
|
|||||||
import com.google.gerrit.server.query.change.AndSource;
|
import com.google.gerrit.server.query.change.AndSource;
|
||||||
import com.google.gerrit.server.query.change.BasicChangeRewrites;
|
import com.google.gerrit.server.query.change.BasicChangeRewrites;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
|
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
||||||
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||||
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
|
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
|
||||||
import com.google.gerrit.server.query.change.OrSource;
|
import com.google.gerrit.server.query.change.OrSource;
|
||||||
@ -62,6 +64,9 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
CLOSED_STATUSES = Sets.immutableEnumSet(closed);
|
CLOSED_STATUSES = Sets.immutableEnumSet(closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final int MAX_LIMIT = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the set of statuses that changes matching the given predicate may have.
|
* Get the set of statuses that changes matching the given predicate may have.
|
||||||
*
|
*
|
||||||
@ -134,11 +139,15 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
@Override
|
@Override
|
||||||
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in) {
|
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in) {
|
||||||
in = basicRewrites.rewrite(in);
|
in = basicRewrites.rewrite(in);
|
||||||
|
// Add 1 to specified limit to match behavior of QueryProcessor.
|
||||||
|
int limit = ChangeQueryBuilder.hasLimit(in)
|
||||||
|
? ChangeQueryBuilder.getLimit(in) + 1
|
||||||
|
: MAX_LIMIT;
|
||||||
|
|
||||||
ChangeIndex index = indexes.getSearchIndex();
|
ChangeIndex index = indexes.getSearchIndex();
|
||||||
Predicate<ChangeData> out = rewriteImpl(in, index);
|
Predicate<ChangeData> out = rewriteImpl(in, index, limit);
|
||||||
if (in == out || out instanceof IndexPredicate) {
|
if (in == out || out instanceof IndexPredicate) {
|
||||||
return query(out, index);
|
return query(out, index, limit);
|
||||||
} else if (out == null /* cannot rewrite */) {
|
} else if (out == null /* cannot rewrite */) {
|
||||||
return in;
|
return in;
|
||||||
} else {
|
} else {
|
||||||
@ -151,6 +160,7 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
*
|
*
|
||||||
* @param in predicate to rewrite.
|
* @param in predicate to rewrite.
|
||||||
* @param index index whose schema determines which fields are indexed.
|
* @param index index whose schema determines which fields are indexed.
|
||||||
|
* @param limit maximum number of results to return.
|
||||||
* @return {@code null} if no part of this subtree can be queried in the
|
* @return {@code null} if no part of this subtree can be queried in the
|
||||||
* index directly. {@code in} if this subtree and all its children can be
|
* index directly. {@code in} if this subtree and all its children can be
|
||||||
* queried directly in the index. Otherwise, a predicate that is
|
* queried directly in the index. Otherwise, a predicate that is
|
||||||
@ -158,7 +168,7 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
* index directly.
|
* index directly.
|
||||||
*/
|
*/
|
||||||
private Predicate<ChangeData> rewriteImpl(Predicate<ChangeData> in,
|
private Predicate<ChangeData> rewriteImpl(Predicate<ChangeData> in,
|
||||||
ChangeIndex index) {
|
ChangeIndex index, int limit) {
|
||||||
if (isIndexPredicate(in, index)) {
|
if (isIndexPredicate(in, index)) {
|
||||||
return in;
|
return in;
|
||||||
} else if (!isRewritePossible(in)) {
|
} else if (!isRewritePossible(in)) {
|
||||||
@ -172,7 +182,7 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
|
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
Predicate<ChangeData> c = in.getChild(i);
|
Predicate<ChangeData> c = in.getChild(i);
|
||||||
Predicate<ChangeData> nc = rewriteImpl(c, index);
|
Predicate<ChangeData> nc = rewriteImpl(c, index, limit);
|
||||||
if (nc == c) {
|
if (nc == c) {
|
||||||
isIndexed.set(i);
|
isIndexed.set(i);
|
||||||
newChildren.add(c);
|
newChildren.add(c);
|
||||||
@ -192,7 +202,7 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
} else if (rewritten.cardinality() == n) {
|
} else if (rewritten.cardinality() == n) {
|
||||||
return in.copy(newChildren); // All children were rewritten.
|
return in.copy(newChildren); // All children were rewritten.
|
||||||
}
|
}
|
||||||
return partitionChildren(in, newChildren, isIndexed, index);
|
return partitionChildren(in, newChildren, isIndexed, index, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isIndexPredicate(Predicate<ChangeData> in, ChangeIndex index) {
|
private boolean isIndexPredicate(Predicate<ChangeData> in, ChangeIndex index) {
|
||||||
@ -207,10 +217,11 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
Predicate<ChangeData> in,
|
Predicate<ChangeData> in,
|
||||||
List<Predicate<ChangeData>> newChildren,
|
List<Predicate<ChangeData>> newChildren,
|
||||||
BitSet isIndexed,
|
BitSet isIndexed,
|
||||||
ChangeIndex index) {
|
ChangeIndex index,
|
||||||
|
int limit) {
|
||||||
if (isIndexed.cardinality() == 1) {
|
if (isIndexed.cardinality() == 1) {
|
||||||
int i = isIndexed.nextSetBit(0);
|
int i = isIndexed.nextSetBit(0);
|
||||||
newChildren.add(0, query(newChildren.remove(i), index));
|
newChildren.add(0, query(newChildren.remove(i), index, limit));
|
||||||
return copy(in, newChildren);
|
return copy(in, newChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +241,7 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
all.add(c);
|
all.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
all.add(0, query(in.copy(indexed), index));
|
all.add(0, query(in.copy(indexed), index, limit));
|
||||||
return copy(in, all);
|
return copy(in, all);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,9 +256,10 @@ public class IndexRewriteImpl implements ChangeQueryRewriter {
|
|||||||
return in.copy(all);
|
return in.copy(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexedChangeQuery query(Predicate<ChangeData> p, ChangeIndex index) {
|
private IndexedChangeQuery query(Predicate<ChangeData> p, ChangeIndex index,
|
||||||
|
int limit) {
|
||||||
try {
|
try {
|
||||||
return new IndexedChangeQuery(index, p);
|
return new IndexedChangeQuery(index, p, limit);
|
||||||
} catch (QueryParseException e) {
|
} catch (QueryParseException e) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Failed to convert " + p + " to index predicate", e);
|
"Failed to convert " + p + " to index predicate", e);
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
package com.google.gerrit.server.index;
|
package com.google.gerrit.server.index;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.server.query.Predicate;
|
import com.google.gerrit.server.query.Predicate;
|
||||||
@ -39,12 +41,14 @@ import java.util.List;
|
|||||||
public class IndexedChangeQuery extends Predicate<ChangeData>
|
public class IndexedChangeQuery extends Predicate<ChangeData>
|
||||||
implements ChangeDataSource {
|
implements ChangeDataSource {
|
||||||
private final Predicate<ChangeData> pred;
|
private final Predicate<ChangeData> pred;
|
||||||
|
private final int limit;
|
||||||
private final ChangeDataSource source;
|
private final ChangeDataSource source;
|
||||||
|
|
||||||
public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred)
|
public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred, int limit)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
this.pred = pred;
|
this.pred = pred;
|
||||||
this.source = index.getSource(pred);
|
this.limit = limit;
|
||||||
|
this.source = index.getSource(pred, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -132,13 +136,19 @@ public class IndexedChangeQuery extends Predicate<ChangeData>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
return other != null
|
if (other == null || getClass() != other.getClass()) {
|
||||||
&& getClass() == other.getClass()
|
return false;
|
||||||
&& pred.equals(((IndexedChangeQuery) other).pred);
|
}
|
||||||
|
IndexedChangeQuery o = (IndexedChangeQuery) other;
|
||||||
|
return pred.equals(o.pred)
|
||||||
|
&& limit == o.limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "index(" + source + ")";
|
return Objects.toStringHelper("index")
|
||||||
|
.add("p", source)
|
||||||
|
.add("limit", limit)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class CommentPredicate extends IndexPredicate<ChangeData> {
|
|||||||
public boolean match(ChangeData object) throws OrmException {
|
public boolean match(ChangeData object) throws OrmException {
|
||||||
try {
|
try {
|
||||||
for (ChangeData cData : index.getSource(
|
for (ChangeData cData : index.getSource(
|
||||||
Predicate.and(new LegacyChangeIdPredicate(db, object.getId()), this))
|
Predicate.and(new LegacyChangeIdPredicate(db, object.getId()), this), 1)
|
||||||
.read()) {
|
.read()) {
|
||||||
if (cData.getId().equals(object.getId())) {
|
if (cData.getId().equals(object.getId())) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -42,7 +42,7 @@ class MessagePredicate extends IndexPredicate<ChangeData> {
|
|||||||
public boolean match(ChangeData object) throws OrmException {
|
public boolean match(ChangeData object) throws OrmException {
|
||||||
try {
|
try {
|
||||||
for (ChangeData cData : index.getSource(
|
for (ChangeData cData : index.getSource(
|
||||||
Predicate.and(new LegacyChangeIdPredicate(db, object.getId()), this))
|
Predicate.and(new LegacyChangeIdPredicate(db, object.getId()), this), 1)
|
||||||
.read()) {
|
.read()) {
|
||||||
if (cData.getId().equals(object.getId())) {
|
if (cData.getId().equals(object.getId())) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -79,7 +79,7 @@ public class IndexRewriteTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p)
|
public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new Source(p);
|
return new Source(p);
|
||||||
}
|
}
|
||||||
@ -269,6 +269,16 @@ public class IndexRewriteTest extends TestCase {
|
|||||||
out.getChildren());
|
out.getChildren());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testLimit() throws Exception {
|
||||||
|
Predicate<ChangeData> in = parse("file:a limit:3");
|
||||||
|
Predicate<ChangeData> out = rewrite(in);
|
||||||
|
assertSame(AndSource.class, out.getClass());
|
||||||
|
assertEquals(ImmutableList.of(
|
||||||
|
query(in.getChild(0), 4),
|
||||||
|
in.getChild(1)),
|
||||||
|
out.getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
public void testGetPossibleStatus() throws Exception {
|
public void testGetPossibleStatus() throws Exception {
|
||||||
assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
|
assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
|
||||||
assertEquals(EnumSet.of(NEW), status("is:new"));
|
assertEquals(EnumSet.of(NEW), status("is:new"));
|
||||||
@ -308,7 +318,12 @@ public class IndexRewriteTest extends TestCase {
|
|||||||
|
|
||||||
private IndexedChangeQuery query(Predicate<ChangeData> p)
|
private IndexedChangeQuery query(Predicate<ChangeData> p)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new IndexedChangeQuery(index, p);
|
return query(p, IndexRewriteImpl.MAX_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexedChangeQuery query(Predicate<ChangeData> p, int limit)
|
||||||
|
throws QueryParseException {
|
||||||
|
return new IndexedChangeQuery(index, p, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Change.Status> status(String query) throws QueryParseException {
|
private Set<Change.Status> status(String query) throws QueryParseException {
|
||||||
|
@ -194,7 +194,7 @@ class SolrChangeIndex implements ChangeIndex, LifecycleListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p)
|
public ChangeDataSource getSource(Predicate<ChangeData> p, int limit)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
Set<Change.Status> statuses = IndexRewriteImpl.getPossibleStatus(p);
|
Set<Change.Status> statuses = IndexRewriteImpl.getPossibleStatus(p);
|
||||||
List<SolrServer> indexes = Lists.newArrayListWithCapacity(2);
|
List<SolrServer> indexes = Lists.newArrayListWithCapacity(2);
|
||||||
@ -204,7 +204,7 @@ class SolrChangeIndex implements ChangeIndex, LifecycleListener {
|
|||||||
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
||||||
indexes.add(closedIndex);
|
indexes.add(closedIndex);
|
||||||
}
|
}
|
||||||
return new QuerySource(indexes, QueryBuilder.toQuery(p));
|
return new QuerySource(indexes, QueryBuilder.toQuery(p), limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commit(SolrServer server) throws IOException {
|
private void commit(SolrServer server) throws IOException {
|
||||||
@ -219,11 +219,12 @@ class SolrChangeIndex implements ChangeIndex, LifecycleListener {
|
|||||||
private final List<SolrServer> indexes;
|
private final List<SolrServer> indexes;
|
||||||
private final SolrQuery query;
|
private final SolrQuery query;
|
||||||
|
|
||||||
public QuerySource(List<SolrServer> indexes, Query q) {
|
public QuerySource(List<SolrServer> indexes, Query q, int limit) {
|
||||||
this.indexes = indexes;
|
this.indexes = indexes;
|
||||||
|
|
||||||
query = new SolrQuery(q.toString());
|
query = new SolrQuery(q.toString());
|
||||||
query.setParam("shards.tolerant", true);
|
query.setParam("shards.tolerant", true);
|
||||||
|
query.setParam("rows", Integer.toString(limit));
|
||||||
query.setFields(ID_FIELD);
|
query.setFields(ID_FIELD);
|
||||||
query.setSort(
|
query.setSort(
|
||||||
ChangeField.UPDATED.getName(),
|
ChangeField.UPDATED.getName(),
|
||||||
|
Loading…
Reference in New Issue
Block a user