Store users that starred a change in change index
Within notedb each change that is starred by a user is tracked by a
refs/starred-changes/XX/YYYY/ZZZZ ref in the All-Users meta repository
where XX/YYYY is the sharded change ID and ZZZZ is the account ID.
With this storage format finding all users that have starred a
change is cheap because we just need to list all refs that have
refs/starred-changes/XX/YYYY/ as prefix and looking up refs with a
prefix that ends on '/' is optimized in jgit. However to find all
changes that were starred by a user we must scan the complete
refs/starred-changes/ namespace and check which of the refs end with
the account ID. This results in bad performance when there are many
starred changes. Having the users that starred a change stored in the
change index makes this lookup cheap.
Looking up all changes that were starred by a user is needed for
supporting the "is:starred" query operator and for populating the
"starred" field in ChangeInfo.
Change-Id: Iecc9ca8ef133b0d0f86f1471b9ed31be78a43f6c
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 8f7e899..840f8b4 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -114,6 +114,7 @@
private static final String PATCH_SET_FIELD = ChangeField.PATCH_SET.getName();
private static final String REVIEWEDBY_FIELD =
ChangeField.REVIEWEDBY.getName();
+ private static final String STARREDBY_FIELD = ChangeField.STARREDBY.getName();
static Term idTerm(ChangeData cd) {
return QueryBuilder.intTerm(LEGACY_ID.getName(), cd.getId().get());
@@ -413,6 +414,9 @@
if (fields.contains(REVIEWEDBY_FIELD)) {
decodeReviewedBy(doc, cd);
}
+ if (fields.contains(STARREDBY_FIELD)) {
+ decodeStarredBy(doc, cd);
+ }
return cd;
}
@@ -466,6 +470,16 @@
}
}
+ private void decodeStarredBy(Document doc, ChangeData cd) {
+ IndexableField[] starredBy = doc.getFields(STARREDBY_FIELD);
+ Set<Account.Id> accounts =
+ Sets.newHashSetWithExpectedSize(starredBy.length);
+ for (IndexableField r : starredBy) {
+ accounts.add(new Account.Id(r.numericValue().intValue()));
+ }
+ cd.setStarredBy(accounts);
+ }
+
private static <T> List<T> decodeProtos(Document doc, String fieldName,
ProtobufCodec<T> codec) {
BytesRef[] bytesRefs = doc.getBinaryValues(fieldName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index 50b20f0..fb75091b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -338,7 +338,7 @@
FluentIterable.from(
starredQuery != null
? starredQuery
- : starredChangesUtil.query(accountId))
+ : starredChangesUtil.queryFromIndex(accountId))
.toSet();
} finally {
starredQuery = null;
@@ -356,7 +356,7 @@
public void asyncStarredChanges() {
if (starredChanges == null && starredChangesUtil != null) {
- starredQuery = starredChangesUtil.query(accountId);
+ starredQuery = starredChangesUtil.queryFromIndex(accountId);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index 6413414..49d3689 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -15,18 +15,24 @@
package com.google.gerrit.server;
import com.google.common.base.Function;
-import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.StarredChange;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.change.ChangeField;
+import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.ListResultSet;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -65,26 +71,33 @@
private final NotesMigration migration;
private final Provider<ReviewDb> dbProvider;
private final PersonIdent serverIdent;
+ private final ChangeIndexer indexer;
+ private final Provider<InternalChangeQuery> queryProvider;
@Inject
StarredChangesUtil(GitRepositoryManager repoManager,
AllUsersName allUsers,
NotesMigration migration,
Provider<ReviewDb> dbProvider,
- @GerritPersonIdent PersonIdent serverIdent) {
+ @GerritPersonIdent PersonIdent serverIdent,
+ ChangeIndexer indexer,
+ Provider<InternalChangeQuery> queryProvider) {
this.repoManager = repoManager;
this.allUsers = allUsers;
this.migration = migration;
this.dbProvider = dbProvider;
this.serverIdent = serverIdent;
+ this.indexer = indexer;
+ this.queryProvider = queryProvider;
}
- public void star(Account.Id accountId, Change.Id changeId)
- throws OrmException {
+ public void star(Account.Id accountId, Project.NameKey project,
+ Change.Id changeId) throws OrmException, IOException {
dbProvider.get().starredChanges()
.insert(Collections.singleton(new StarredChange(
new StarredChange.Key(accountId, changeId))));
if (!migration.writeAccounts()) {
+ indexer.index(dbProvider.get(), project, changeId);
return;
}
try (Repository repo = repoManager.openRepository(allUsers);
@@ -98,6 +111,7 @@
RefUpdate.Result result = u.update(rw);
switch (result) {
case NEW:
+ indexer.index(dbProvider.get(), project, changeId);
return;
case FAST_FORWARD:
case FORCED:
@@ -128,12 +142,13 @@
}
}
- public void unstar(Account.Id accountId, Change.Id changeId)
- throws OrmException {
+ public void unstar(Account.Id accountId, Project.NameKey project,
+ Change.Id changeId) throws OrmException, IOException {
dbProvider.get().starredChanges()
.delete(Collections.singleton(new StarredChange(
new StarredChange.Key(accountId, changeId))));
if (!migration.writeAccounts()) {
+ indexer.index(dbProvider.get(), project, changeId);
return;
}
try (Repository repo = repoManager.openRepository(allUsers);
@@ -146,6 +161,7 @@
RefUpdate.Result result = u.delete();
switch (result) {
case FORCED:
+ indexer.index(dbProvider.get(), project, changeId);
return;
case FAST_FORWARD:
case IO_FAILURE:
@@ -168,10 +184,12 @@
}
}
- public void unstarAll(Change.Id changeId) throws OrmException {
+ public void unstarAll(Project.NameKey project, Change.Id changeId)
+ throws OrmException, IOException, NoSuchChangeException {
dbProvider.get().starredChanges().delete(
dbProvider.get().starredChanges().byChange(changeId));
if (!migration.writeAccounts()) {
+ indexer.index(dbProvider.get(), project, changeId);
return;
}
try (Repository repo = repoManager.openRepository(allUsers);
@@ -180,7 +198,7 @@
batchUpdate.setAllowNonFastForwards(true);
batchUpdate.setRefLogIdent(serverIdent);
batchUpdate.setRefLogMessage("Unstar change " + changeId.get(), true);
- for (Account.Id accountId : byChange(changeId)) {
+ for (Account.Id accountId : byChangeFromIndex(changeId)) {
String refName = RefNames.refsStarredChanges(changeId, accountId);
Ref ref = repo.getRefDatabase().getRef(refName);
batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(),
@@ -194,13 +212,14 @@
changeId.get(), command.getRefName(), command.getResult()));
}
}
+ indexer.index(dbProvider.get(), project, changeId);
} catch (IOException e) {
throw new OrmException(
String.format("Unstar change %d failed", changeId.get()), e);
}
}
- public Iterable<Account.Id> byChange(Change.Id changeId)
+ public Set<Account.Id> byChange(Change.Id changeId)
throws OrmException {
if (!migration.readAccounts()) {
return FluentIterable
@@ -210,7 +229,7 @@
public Account.Id apply(StarredChange in) {
return in.getAccountId();
}
- });
+ }).toSet();
}
return FluentIterable
.from(getRefNames(RefNames.refsStarredChangesPrefix(changeId)))
@@ -219,29 +238,40 @@
public Account.Id apply(String refPart) {
return Account.Id.parse(refPart);
}
- });
+ }).toSet();
+ }
+
+ public Set<Account.Id> byChangeFromIndex(Change.Id changeId)
+ throws OrmException, NoSuchChangeException {
+ Set<String> fields = ImmutableSet.of(
+ ChangeField.ID.getName(),
+ ChangeField.STARREDBY.getName());
+ List<ChangeData> changeData = queryProvider.get().setRequestedFields(fields)
+ .byLegacyChangeId(changeId);
+ if (changeData.size() != 1) {
+ throw new NoSuchChangeException(changeId);
+ }
+ return changeData.get(0).starredBy();
}
@Deprecated
- public ResultSet<Change.Id> query(final Account.Id accountId) {
+ public ResultSet<Change.Id> queryFromIndex(final Account.Id accountId) {
try {
if (!migration.readAccounts()) {
return new ChangeIdResultSet(
dbProvider.get().starredChanges().byAccount(accountId));
}
+ Set<String> fields = ImmutableSet.of(
+ ChangeField.ID.getName());
+ List<ChangeData> changeData =
+ queryProvider.get().setRequestedFields(fields).byIsStarred(accountId);
return new ListResultSet<>(FluentIterable
- .from(getRefNames(RefNames.REFS_STARRED_CHANGES))
- .filter(new Predicate<String>() {
+ .from(changeData)
+ .transform(new Function<ChangeData, Change.Id>() {
@Override
- public boolean apply(String refPart) {
- return refPart.endsWith("/" + accountId.get());
- }
- })
- .transform(new Function<String, Change.Id>() {
- @Override
- public Change.Id apply(String changeId) {
- return Change.Id.parse(changeId);
+ public Change.Id apply(ChangeData cd) {
+ return cd.getId();
}
}).toList());
} catch (OrmException | RuntimeException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
index f8a23d8..1ed29a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
@@ -42,6 +42,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+
@Singleton
public class StarredChanges implements
ChildCollection<AccountResource, AccountResource.StarredChange>,
@@ -130,12 +132,13 @@
@Override
public Response<?> apply(AccountResource rsrc, EmptyInput in)
- throws AuthException, OrmException {
+ throws AuthException, OrmException, IOException {
if (self.get() != rsrc.getUser()) {
throw new AuthException("not allowed to add starred change");
}
try {
- starredChangesUtil.star(self.get().getAccountId(), change.getId());
+ starredChangesUtil.star(self.get().getAccountId(), change.getProject(),
+ change.getId());
} catch (OrmDuplicateKeyException e) {
return Response.none();
}
@@ -177,12 +180,12 @@
@Override
public Response<?> apply(AccountResource.StarredChange rsrc,
- EmptyInput in) throws AuthException, OrmException {
+ EmptyInput in) throws AuthException, OrmException, IOException {
if (self.get() != rsrc.getUser()) {
throw new AuthException("not allowed remove starred change");
}
starredChangesUtil.unstar(self.get().getAccountId(),
- rsrc.getChange().getId());
+ rsrc.getChange().getProject(), rsrc.getChange().getId());
return Response.none();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 55d077f..b3126d3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -193,7 +193,7 @@
IdString.fromUrl(id));
starredChangesCreate.setChange(rsrc);
starredChangesCreate.apply(account, new StarredChanges.EmptyInput());
- } catch (OrmException e) {
+ } catch (OrmException | IOException e) {
throw new RestApiException("Cannot star change", e);
}
}
@@ -207,7 +207,7 @@
new AccountResource.StarredChange(account.getUser(), rsrc);
starredChangesDelete.apply(starredChange,
new StarredChanges.EmptyInput());
- } catch (OrmException e) {
+ } catch (OrmException | IOException e) {
throw new RestApiException("Cannot unstar change", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
index d90fc73..6ac17b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChangeOp.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
import com.google.gerrit.server.git.BatchUpdate.RepoContext;
import com.google.gerrit.server.git.BatchUpdateReviewDb;
+import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -78,8 +79,8 @@
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException {
+ public boolean updateChange(ChangeContext ctx) throws RestApiException,
+ OrmException, IOException, NoSuchChangeException {
checkState(ctx.getOrder() == BatchUpdate.Order.DB_BEFORE_REPO,
"must use DeleteDraftChangeOp with DB_BEFORE_REPO");
checkState(id == null, "cannot reuse DeleteDraftChangeOp");
@@ -117,7 +118,7 @@
// Non-atomic operation on Accounts table; not much we can do to make it
// atomic.
- starredChangesUtil.unstarAll(id);
+ starredChangesUtil.unstarAll(change.getProject(), id);
ctx.deleteChange();
return true;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 59d4290..2edff4b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -35,6 +35,7 @@
import com.google.gerrit.server.git.UpdateException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -99,8 +100,8 @@
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException, IOException {
+ public boolean updateChange(ChangeContext ctx) throws RestApiException,
+ OrmException, IOException, NoSuchChangeException {
patchSet = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
if (patchSet == null) {
return false; // Nothing to do.
@@ -148,7 +149,8 @@
}
private void deleteOrUpdateDraftChange(ChangeContext ctx)
- throws OrmException, RestApiException {
+ throws OrmException, RestApiException, IOException,
+ NoSuchChangeException {
Change c = ctx.getChange();
if (deletedOnlyPatchSet()) {
deleteChangeOp = deleteChangeOpProvider.get();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java
index 53ff0e3..5ead80f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java
@@ -73,6 +73,16 @@
}
@SafeVarargs
+ public static <V> Schema<V> schema(Schema<V> schema,
+ FieldDef<V, ?>... moreFields) {
+ return new Schema<>(
+ new ImmutableList.Builder<FieldDef<V, ?>>()
+ .addAll(schema.getFields().values())
+ .addAll(ImmutableList.copyOf(moreFields))
+ .build());
+ }
+
+ @SafeVarargs
public static <V> Schema<V> schema(FieldDef<V, ?>... fields) {
return schema(ImmutableList.copyOf(fields));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
index fdeb654..67694ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -576,6 +576,23 @@
}
};
+ /** Users who have starred this change. */
+ public static final FieldDef<ChangeData, Iterable<Integer>> STARREDBY =
+ new FieldDef.Repeatable<ChangeData, Integer>(
+ ChangeQueryBuilder.FIELD_STARREDBY, FieldType.INTEGER, true) {
+ @Override
+ public Iterable<Integer> get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return Iterables.transform(input.starredBy(),
+ new Function<Account.Id, Integer>() {
+ @Override
+ public Integer apply(Account.Id accountId) {
+ return accountId.get();
+ }
+ });
+ }
+ };
+
/** Opaque group identifiers for this change's patch sets. */
public static final FieldDef<ChangeData, Iterable<String>> GROUP =
new FieldDef.Repeatable<ChangeData, String>(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 48df562..0ca9922 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -186,14 +186,26 @@
/**
* Synchronously index a change.
*
- * @param change change to index.
* @param db review database.
+ * @param change change to index.
*/
public void index(ReviewDb db, Change change) throws IOException {
index(changeDataFactory.create(db, change));
}
/**
+ * Synchronously index a change.
+ *
+ * @param db review database.
+ * @param project the project to which the change belongs.
+ * @param changeId ID of the change to index.
+ */
+ public void index(ReviewDb db, Project.NameKey project, Change.Id changeId)
+ throws IOException {
+ index(changeDataFactory.create(db, project, changeId));
+ }
+
+ /**
* Start deleting a change.
*
* @param id change to delete.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index b39a0b7..9363f62 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -96,8 +96,11 @@
ChangeField.COMMITTER,
ChangeField.DRAFTBY);
+ @Deprecated
static final Schema<ChangeData> V27 = schema(V26.getFields().values());
+ static final Schema<ChangeData> V28 = schema(V27, ChangeField.STARREDBY);
+
public static final ChangeSchemaDefinitions INSTANCE =
new ChangeSchemaDefinitions();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index b244ec4..4e1891a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
@@ -305,10 +306,10 @@
// BCC anyone who has starred this change.
//
for (Account.Id accountId : args.starredChangesUtil
- .byChange(change.getId())) {
+ .byChangeFromIndex(change.getId())) {
super.add(RecipientType.BCC, accountId);
}
- } catch (OrmException err) {
+ } catch (OrmException | NoSuchChangeException err) {
// Just don't BCC everyone. Better to send a partial message to those
// we already have queued up then to fail deliver entirely to people
// who have a lower interest in the change.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 40fadb4..452d51f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -44,6 +44,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -296,7 +297,7 @@
public static ChangeData createForTest(Project.NameKey project, Change.Id id,
int currentPatchSetId) {
ChangeData cd = new ChangeData(null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, project, id);
+ null, null, null, null, null, null, null, null, project, id);
cd.currentPatchSet = new PatchSet(new PatchSet.Id(id, currentPatchSetId));
return cd;
}
@@ -315,6 +316,7 @@
private final PatchListCache patchListCache;
private final NotesMigration notesMigration;
private final MergeabilityCache mergeabilityCache;
+ private final StarredChangesUtil starredChangesUtil;
private final Change.Id legacyId;
private DataSource<ChangeData> returnedBySource;
private Project.NameKey project;
@@ -338,6 +340,7 @@
private Set<Account.Id> editsByUser;
private Set<Account.Id> reviewedBy;
private Set<Account.Id> draftsByUser;
+ private Set<Account.Id> starredByUser;
private PersonIdent author;
private PersonIdent committer;
@@ -356,6 +359,7 @@
PatchListCache patchListCache,
NotesMigration notesMigration,
MergeabilityCache mergeabilityCache,
+ StarredChangesUtil starredChangesUtil,
@Assisted ReviewDb db,
@Assisted Project.NameKey project,
@Assisted Change.Id id) {
@@ -373,6 +377,7 @@
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
this.mergeabilityCache = mergeabilityCache;
+ this.starredChangesUtil = starredChangesUtil;
this.project = project;
this.legacyId = id;
}
@@ -392,6 +397,7 @@
PatchListCache patchListCache,
NotesMigration notesMigration,
MergeabilityCache mergeabilityCache,
+ StarredChangesUtil starredChangesUtil,
@Assisted ReviewDb db,
@Assisted Change c) {
this.db = db;
@@ -408,6 +414,7 @@
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
this.mergeabilityCache = mergeabilityCache;
+ this.starredChangesUtil = starredChangesUtil;
legacyId = c.getId();
change = c;
project = c.getProject();
@@ -428,6 +435,7 @@
PatchListCache patchListCache,
NotesMigration notesMigration,
MergeabilityCache mergeabilityCache,
+ StarredChangesUtil starredChangesUtil,
@Assisted ReviewDb db,
@Assisted ChangeNotes cn) {
this.db = db;
@@ -444,6 +452,7 @@
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
this.mergeabilityCache = mergeabilityCache;
+ this.starredChangesUtil = starredChangesUtil;
legacyId = cn.getChangeId();
change = cn.getChange();
project = cn.getProjectName();
@@ -465,6 +474,7 @@
PatchListCache patchListCache,
NotesMigration notesMigration,
MergeabilityCache mergeabilityCache,
+ StarredChangesUtil starredChangesUtil,
@Assisted ReviewDb db,
@Assisted ChangeControl c) {
this.db = db;
@@ -481,6 +491,7 @@
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
this.mergeabilityCache = mergeabilityCache;
+ this.starredChangesUtil = starredChangesUtil;
legacyId = c.getId();
change = c.getChange();
changeControl = c;
@@ -503,6 +514,7 @@
PatchListCache patchListCache,
NotesMigration notesMigration,
MergeabilityCache mergeabilityCache,
+ StarredChangesUtil starredChangesUtil,
@Assisted ReviewDb db,
@Assisted Change.Id id) {
checkState(!notesMigration.readChanges(),
@@ -521,6 +533,7 @@
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
this.mergeabilityCache = mergeabilityCache;
+ this.starredChangesUtil = starredChangesUtil;
this.legacyId = id;
this.project = null;
}
@@ -1005,6 +1018,17 @@
this.reviewedBy = reviewedBy;
}
+ public Set<Account.Id> starredBy() throws OrmException {
+ if (starredByUser == null) {
+ starredByUser = starredChangesUtil.byChange(legacyId);
+ }
+ return starredByUser;
+ }
+
+ public void setStarredBy(Set<Account.Id> starredByUser) {
+ this.starredByUser = starredByUser;
+ }
+
@AutoValue
abstract static class ReviewedByEvent {
private static ReviewedByEvent create(ChangeMessage msg) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index cb27de4..2679f29 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -419,7 +419,7 @@
@Operator
public Predicate<ChangeData> has(String value) throws QueryParseException {
if ("star".equalsIgnoreCase(value)) {
- return new IsStarredByPredicate(args);
+ return starredby(self());
}
if ("draft".equalsIgnoreCase(value)) {
@@ -435,7 +435,7 @@
@Operator
public Predicate<ChangeData> is(String value) throws QueryParseException {
if ("starred".equalsIgnoreCase(value)) {
- return new IsStarredByPredicate(args);
+ return starredby(self());
}
if ("watched".equalsIgnoreCase(value)) {
@@ -648,17 +648,25 @@
@Operator
public Predicate<ChangeData> starredby(String who)
throws QueryParseException, OrmException {
- if ("self".equals(who)) {
- return new IsStarredByPredicate(args);
- }
- Set<Account.Id> m = parseAccount(who);
- List<IsStarredByPredicate> p = Lists.newArrayListWithCapacity(m.size());
- for (Account.Id id : m) {
- p.add(new IsStarredByPredicate(args.asUser(id)));
+ return starredby(parseAccount(who));
+ }
+
+ private Predicate<ChangeData> starredby(Set<Account.Id> who)
+ throws QueryParseException {
+ List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(who.size());
+ for (Account.Id id : who) {
+ p.add(starredby(id));
}
return Predicate.or(p);
}
+ private Predicate<ChangeData> starredby(Account.Id who)
+ throws QueryParseException {
+ return args.getSchema().hasField(ChangeField.STARREDBY)
+ ? new IsStarredByPredicate(who)
+ : new IsStarredByLegacyPredicate(args.asUser(who));
+ }
+
@Operator
public Predicate<ChangeData> watchedby(String who)
throws QueryParseException, OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index bb72a1b..37cdfaf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -28,6 +28,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
@@ -297,6 +298,10 @@
return query(and(project(project), or(groupPredicates)));
}
+ public List<ChangeData> byIsStarred(Account.Id id) throws OrmException {
+ return query(new IsStarredByPredicate(id));
+ }
+
public List<ChangeData> query(Predicate<ChangeData> p) throws OrmException {
try {
return qp.queryChanges(p).changes();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByLegacyPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByLegacyPredicate.java
new file mode 100644
index 0000000..718c3f6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByLegacyPredicate.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.query.OrPredicate;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
+
+import java.util.List;
+import java.util.Set;
+
+@Deprecated
+class IsStarredByLegacyPredicate extends OrPredicate<ChangeData> {
+ private static String describe(CurrentUser user) {
+ if (user.isIdentifiedUser()) {
+ return user.getAccountId().toString();
+ }
+ return user.toString();
+ }
+
+ private static List<Predicate<ChangeData>> predicates(Set<Change.Id> ids) {
+ List<Predicate<ChangeData>> r = Lists.newArrayListWithCapacity(ids.size());
+ for (Change.Id id : ids) {
+ r.add(new LegacyChangeIdPredicate(id));
+ }
+ return r;
+ }
+
+ private final CurrentUser user;
+
+ IsStarredByLegacyPredicate(Arguments args) throws QueryParseException {
+ super(predicates(args.getIdentifiedUser().getStarredChanges()));
+ this.user = args.getIdentifiedUser();
+ }
+
+ @Override
+ public boolean match(final ChangeData object) {
+ return user.getStarredChanges().contains(object.getId());
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ String val = describe(user);
+ if (val.indexOf(' ') < 0) {
+ return ChangeQueryBuilder.FIELD_STARREDBY + ":" + val;
+ } else {
+ return ChangeQueryBuilder.FIELD_STARREDBY + ":\"" + val + "\"";
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
index 5ff859a..50c54f6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsStarredByPredicate.java
@@ -14,57 +14,31 @@
package com.google.gerrit.server.query.change;
-import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.query.OrPredicate;
-import com.google.gerrit.server.query.Predicate;
-import com.google.gerrit.server.query.QueryParseException;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.change.ChangeField;
+import com.google.gwtorm.server.OrmException;
-import java.util.List;
-import java.util.Set;
+class IsStarredByPredicate extends IndexPredicate<ChangeData> {
+ private final Account.Id accountId;
-class IsStarredByPredicate extends OrPredicate<ChangeData> {
- private static String describe(CurrentUser user) {
- if (user.isIdentifiedUser()) {
- return user.getAccountId().toString();
- }
- return user.toString();
- }
-
- private static List<Predicate<ChangeData>> predicates(Set<Change.Id> ids) {
- List<Predicate<ChangeData>> r = Lists.newArrayListWithCapacity(ids.size());
- for (Change.Id id : ids) {
- r.add(new LegacyChangeIdPredicate(id));
- }
- return r;
- }
-
- private final CurrentUser user;
-
- IsStarredByPredicate(Arguments args) throws QueryParseException {
- super(predicates(args.getIdentifiedUser().getStarredChanges()));
- this.user = args.getIdentifiedUser();
+ IsStarredByPredicate(Account.Id accountId) {
+ super(ChangeField.STARREDBY, accountId.toString());
+ this.accountId = accountId;
}
@Override
- public boolean match(final ChangeData object) {
- return user.getStarredChanges().contains(object.getId());
+ public boolean match(ChangeData cd) throws OrmException {
+ return cd.starredBy().contains(accountId);
}
@Override
public int getCost() {
- return 0;
+ return 1;
}
@Override
public String toString() {
- String val = describe(user);
- if (val.indexOf(' ') < 0) {
- return ChangeQueryBuilder.FIELD_STARREDBY + ":" + val;
- } else {
- return ChangeQueryBuilder.FIELD_STARREDBY + ":\"" + val + "\"";
- }
+ return ChangeQueryBuilder.FIELD_STARREDBY + ":" + accountId;
}
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 129c155..6e2f9c9 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1173,6 +1173,23 @@
}
@Test
+ public void byStarredBy() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ Change change1 = insert(repo, newChange(repo));
+ Change change2 = insert(repo, newChange(repo));
+ insert(repo, newChange(repo));
+
+ gApi.accounts().self().starChange(change1.getId().toString());
+ gApi.accounts().self().starChange(change2.getId().toString());
+
+ int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
+ .getAccountId().get();
+
+ assertQuery("starredby:self", change2, change1);
+ assertQuery("starredby:" + user2);
+ }
+
+ @Test
public void byFrom() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));