Merge changes Icff51096,Iba2284a7

* changes:
  Migrate stars index queries to read from All-Users repository
  Migrate has:draft to read changes from All-Users repository
diff --git a/java/com/google/gerrit/entities/RefNames.java b/java/com/google/gerrit/entities/RefNames.java
index 2263aba..349b67e 100644
--- a/java/com/google/gerrit/entities/RefNames.java
+++ b/java/com/google/gerrit/entities/RefNames.java
@@ -489,7 +489,7 @@
     return Integer.parseInt(rest.substring(0, ie));
   }
 
-  static Integer parseRefSuffix(String name) {
+  public static Integer parseRefSuffix(String name) {
     if (name == null) {
       return null;
     }
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index ba9f6d6..1d38877 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -52,9 +52,11 @@
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
@@ -465,10 +467,35 @@
     }
   }
 
+  /** returns all changes that contain draft comments of {@code accountId}. */
+  public Collection<Change.Id> getChangesWithDrafts(Account.Id accountId) {
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      return getChangesWithDrafts(repo, accountId);
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
+  }
+
   private Collection<Ref> getDraftRefs(Repository repo, Change.Id changeId) throws IOException {
     return repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftCommentsPrefix(changeId));
   }
 
+  private Collection<Change.Id> getChangesWithDrafts(Repository repo, Account.Id accountId)
+      throws IOException {
+    Set<Change.Id> changes = new HashSet<>();
+    for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_DRAFT_COMMENTS)) {
+      Integer accountIdFromRef = RefNames.parseRefSuffix(ref.getName());
+      if (accountIdFromRef != null && accountIdFromRef == accountId.get()) {
+        Change.Id changeId = Change.Id.fromAllUsersRef(ref.getName());
+        if (changeId == null) {
+          continue;
+        }
+        changes.add(changeId);
+      }
+    }
+    return changes;
+  }
+
   private static <T extends Comment> List<T> sort(List<T> comments) {
     comments.sort(COMMENT_ORDER);
     return comments;
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 5dcbd01..7c61c92 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -289,6 +289,35 @@
     }
   }
 
+  public ImmutableSet<Change.Id> byAccountId(Account.Id accountId, String label) {
+    try (Repository repo = repoManager.openRepository(allUsers)) {
+      ImmutableSet.Builder<Change.Id> builder = ImmutableSet.builder();
+      for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_STARRED_CHANGES)) {
+        Account.Id currentAccountId = Account.Id.fromRef(ref.getName());
+        // Skip all refs that don't correspond with accountId.
+        if (currentAccountId == null || !currentAccountId.equals(accountId)) {
+          continue;
+        }
+        // Skip all refs that don't contain the required label.
+        StarRef starRef = readLabels(repo, ref.getName());
+        if (!starRef.labels().contains(label)) {
+          continue;
+        }
+
+        // Skip invalid change ids.
+        Change.Id changeId = Change.Id.fromAllUsersRef(ref.getName());
+        if (changeId == null) {
+          continue;
+        }
+        builder.add(changeId);
+      }
+      return builder.build();
+    } catch (IOException e) {
+      throw new StorageException(
+          String.format("Get starred changes for account %d failed", accountId.get()), e);
+    }
+  }
+
   public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(Change.Id changeId) {
     List<ChangeData> changeData =
         queryProvider
diff --git a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
index 1486559..65f8f2d 100644
--- a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
+++ b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
@@ -45,6 +45,13 @@
       GERRIT_BACKEND_REQUEST_FEATURE_ENABLE_SUBMIT_REQUIREMENTS_BACKFILLING_ON_DASHBOARD =
           "GerritBackendRequestFeature__enable_submit_requirements_backfilling_on_dashboard";
 
+  /**
+   * When set, we compute information from All-Users repository if able, instead of computing it
+   * from the change index.
+   */
+  public static final String GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY =
+      "GerritBackendRequestFeature__compute_from_all_users_repository";
+
   /** Features, enabled by default in the current release. */
   public static final ImmutableSet<String> DEFAULT_ENABLED_FEATURES =
       ImmutableSet.of(UI_FEATURE_PATCHSET_COMMENTS);
diff --git a/java/com/google/gerrit/server/query/change/ChangePredicates.java b/java/com/google/gerrit/server/query/change/ChangePredicates.java
index 043b37d..3afdcdd 100644
--- a/java/com/google/gerrit/server/query/change/ChangePredicates.java
+++ b/java/com/google/gerrit/server/query/change/ChangePredicates.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.query.change;
 
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
 import com.google.common.base.CharMatcher;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
@@ -21,12 +23,15 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.git.ObjectIds;
 import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.change.HashtagsUtil;
 import com.google.gerrit.server.index.change.ChangeField;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /** Predicates that match against {@link ChangeData}. */
 public class ChangePredicates {
@@ -76,8 +81,34 @@
    * Returns a predicate that matches changes where the provided {@link
    * com.google.gerrit.entities.Account.Id} has a pending draft comment.
    */
-  public static Predicate<ChangeData> draftBy(Account.Id id) {
-    return new ChangeIndexPredicate(ChangeField.DRAFTBY, id.toString());
+  public static Predicate<ChangeData> draftBy(
+      boolean computeFromAllUsersRepository, CommentsUtil commentsUtil, Account.Id id) {
+    if (!computeFromAllUsersRepository) {
+      return new ChangeIndexPredicate(ChangeField.DRAFTBY, id.toString());
+    }
+    Set<Predicate<ChangeData>> changeIdPredicates =
+        commentsUtil.getChangesWithDrafts(id).stream()
+            .map(ChangePredicates::idStr)
+            .collect(toImmutableSet());
+    return Predicate.or(changeIdPredicates);
+  }
+
+  /**
+   * Returns a predicate that matches changes where the provided {@link
+   * com.google.gerrit.entities.Account.Id} has starred changes with {@code label}.
+   */
+  public static Predicate<ChangeData> starBy(
+      boolean computeFromAllUsersRepository,
+      StarredChangesUtil starredChangesUtil,
+      Account.Id id,
+      String label) {
+    if (!computeFromAllUsersRepository) {
+      return new StarPredicate(id, label);
+    }
+    return Predicate.or(
+        starredChangesUtil.byAccountId(id, label).stream()
+            .map(ChangePredicates::idStr)
+            .collect(toImmutableSet()));
   }
 
   /**
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index da36633..57191c5 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -17,6 +17,7 @@
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.gerrit.entities.Change.CHANGE_ID_PATTERN;
 import static com.google.gerrit.server.account.AccountResolver.isSelf;
+import static com.google.gerrit.server.experiments.ExperimentFeaturesConstants.GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY;
 import static com.google.gerrit.server.query.change.ChangeData.asChanges;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
@@ -70,6 +71,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.HasOperandAliasConfig;
 import com.google.gerrit.server.config.OperatorAliasConfig;
+import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndex;
@@ -253,6 +255,7 @@
     final OperatorAliasConfig operatorAliasConfig;
     final boolean indexMergeable;
     final boolean conflictsPredicateEnabled;
+    final ExperimentFeatures experimentFeatures;
     final HasOperandAliasConfig hasOperandAliasConfig;
     final PluginSetContext<SubmitRule> submitRules;
 
@@ -288,6 +291,7 @@
         GroupMembers groupMembers,
         OperatorAliasConfig operatorAliasConfig,
         @GerritServerConfig Config gerritConfig,
+        ExperimentFeatures experimentFeatures,
         HasOperandAliasConfig hasOperandAliasConfig,
         ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory,
         PluginSetContext<SubmitRule> submitRules) {
@@ -320,6 +324,7 @@
           operatorAliasConfig,
           MergeabilityComputationBehavior.fromConfig(gerritConfig).includeInIndex(),
           gerritConfig.getBoolean("change", null, "conflictsPredicateEnabled", true),
+          experimentFeatures,
           hasOperandAliasConfig,
           changeIsVisbleToPredicateFactory,
           submitRules);
@@ -354,6 +359,7 @@
         OperatorAliasConfig operatorAliasConfig,
         boolean indexMergeable,
         boolean conflictsPredicateEnabled,
+        ExperimentFeatures experimentFeatures,
         HasOperandAliasConfig hasOperandAliasConfig,
         ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory,
         PluginSetContext<SubmitRule> submitRules) {
@@ -386,6 +392,7 @@
       this.operatorAliasConfig = operatorAliasConfig;
       this.indexMergeable = indexMergeable;
       this.conflictsPredicateEnabled = conflictsPredicateEnabled;
+      this.experimentFeatures = experimentFeatures;
       this.hasOperandAliasConfig = hasOperandAliasConfig;
       this.submitRules = submitRules;
     }
@@ -420,6 +427,7 @@
           operatorAliasConfig,
           indexMergeable,
           conflictsPredicateEnabled,
+          experimentFeatures,
           hasOperandAliasConfig,
           changeIsVisbleToPredicateFactory,
           submitRules);
@@ -1089,15 +1097,29 @@
   }
 
   private Predicate<ChangeData> ignoredBySelf() throws QueryParseException {
-    return new StarPredicate(self(), StarredChangesUtil.IGNORE_LABEL);
+    return ChangePredicates.starBy(
+        args.experimentFeatures.isFeatureEnabled(
+            GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY),
+        args.starredChangesUtil,
+        self(),
+        StarredChangesUtil.IGNORE_LABEL);
   }
 
   private Predicate<ChangeData> starredBySelf() throws QueryParseException {
-    return new StarPredicate(self(), StarredChangesUtil.DEFAULT_LABEL);
+    return ChangePredicates.starBy(
+        args.experimentFeatures.isFeatureEnabled(
+            GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY),
+        args.starredChangesUtil,
+        self(),
+        StarredChangesUtil.DEFAULT_LABEL);
   }
 
   private Predicate<ChangeData> draftBySelf() throws QueryParseException {
-    return ChangePredicates.draftBy(self());
+    return ChangePredicates.draftBy(
+        args.experimentFeatures.isFeatureEnabled(
+            GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY),
+        args.commentsUtil,
+        self());
   }
 
   @Operator
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index 1485a6e56..ad3c56b 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.account;
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.gerrit.server.experiments.ExperimentFeaturesConstants.GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY;
 
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Strings;
@@ -39,6 +40,7 @@
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangePredicates;
@@ -75,6 +77,7 @@
   private final Provider<CommentJson> commentJsonProvider;
   private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
+  private final ExperimentFeatures experimentFeatures;
 
   @Inject
   DeleteDraftComments(
@@ -86,7 +89,8 @@
       ChangeJson.Factory changeJsonFactory,
       Provider<CommentJson> commentJsonProvider,
       CommentsUtil commentsUtil,
-      PatchSetUtil psUtil) {
+      PatchSetUtil psUtil,
+      ExperimentFeatures experimentFeatures) {
     this.userProvider = userProvider;
     this.batchUpdateFactory = batchUpdateFactory;
     this.queryBuilderProvider = queryBuilderProvider;
@@ -96,6 +100,7 @@
     this.commentJsonProvider = commentJsonProvider;
     this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
+    this.experimentFeatures = experimentFeatures;
   }
 
   @Override
@@ -146,7 +151,12 @@
 
   private Predicate<ChangeData> predicate(Account.Id accountId, DeleteDraftCommentsInput input)
       throws BadRequestException {
-    Predicate<ChangeData> hasDraft = ChangePredicates.draftBy(accountId);
+    Predicate<ChangeData> hasDraft =
+        ChangePredicates.draftBy(
+            experimentFeatures.isFeatureEnabled(
+                GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY),
+            commentsUtil,
+            accountId);
     if (CharMatcher.whitespace().trimFrom(Strings.nullToEmpty(input.query)).isEmpty()) {
       return hasDraft;
     }
diff --git a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index 66a98e8..67b0342 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -56,6 +56,7 @@
             new Config(),
             null,
             null,
+            null,
             null));
   }
 
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 332548a..3b671aa 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -111,6 +111,7 @@
 import com.google.gerrit.server.change.NotifyResolver;
 import com.google.gerrit.server.change.PatchSetInserter;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.experiments.ExperimentFeaturesConstants;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
@@ -2358,7 +2359,21 @@
   }
 
   @Test
-  public void byHasDraft() throws Exception {
+  public void byHasDraft_draftsComputedFromIndex() throws Exception {
+    byHasDraft();
+  }
+
+  @Test
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  public void byHasDraft_draftsComputedFromAllUsersRepository() throws Exception {
+    byHasDraft();
+  }
+
+  private void byHasDraft() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChange(repo));
     Change change2 = insert(repo, newChange(repo));
@@ -2386,7 +2401,11 @@
     assertQuery("has:draft");
   }
 
-  @Test
+  /**
+   * This test does not have a test about drafts computed from All-Users Repository because zombie
+   * drafts can't be filtered when computing from All-Users repository. TODO(paiking): During
+   * rollout, we should find a way to fix zombie drafts.
+   */
   public void byHasDraftExcludesZombieDrafts() throws Exception {
     Project.NameKey project = Project.nameKey("repo");
     TestRepository<Repo> repo = createProject(project.get());
@@ -2424,8 +2443,62 @@
     assertQuery("has:draft");
   }
 
+  public void byHasDraftWithManyDrafts_draftsComputedFromIndex() throws Exception {
+    byHasDraftWithManyDrafts();
+  }
+
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  public void byHasDraftWithManyDrafts_draftsComputedFromAllUsersRepository() throws Exception {
+    byHasDraftWithManyDrafts();
+  }
+
+  private void byHasDraftWithManyDrafts() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Change[] changesWithDrafts = new Change[30];
+
+    // unrelated change not shown in the result.
+    insert(repo, newChange(repo));
+
+    for (int i = 0; i < changesWithDrafts.length; i++) {
+      // put the changes in reverse order since this is the order we receive them from the index.
+      changesWithDrafts[changesWithDrafts.length - 1 - i] = insert(repo, newChange(repo));
+      DraftInput in = new DraftInput();
+      in.line = 1;
+      in.message = "nit: trailing whitespace";
+      in.path = Patch.COMMIT_MSG;
+      gApi.changes()
+          .id(changesWithDrafts[changesWithDrafts.length - 1 - i].getId().get())
+          .current()
+          .createDraft(in);
+    }
+    assertQuery("has:draft", changesWithDrafts);
+
+    Account.Id user2 =
+        accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
+    requestContext.setContext(newRequestContext(user2));
+    assertQuery("has:draft");
+  }
+
   @Test
-  public void byStarredBy() throws Exception {
+  public void byStarredBy_starsComputedFromIndex() throws Exception {
+    byStarredBy();
+  }
+
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  @Test
+  public void byStarredBy_starsComputedFromAllUsersRepository() throws Exception {
+    byStarredBy();
+  }
+
+  private void byStarredBy() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChange(repo));
     Change change2 = insert(repo, newChange(repo));
@@ -2446,7 +2519,21 @@
   }
 
   @Test
-  public void byStar() throws Exception {
+  public void byStar_starsComputedFromIndex() throws Exception {
+    byStar();
+  }
+
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  @Test
+  public void byStar_starsComputedFromAllUsersRepository() throws Exception {
+    byStar();
+  }
+
+  private void byStar() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChangeWithStatus(repo, Change.Status.MERGED));
     Change change2 = insert(repo, newChangeWithStatus(repo, Change.Status.MERGED));
@@ -2472,7 +2559,21 @@
   }
 
   @Test
-  public void byIgnore() throws Exception {
+  public void byIgnore_starsComputedFromIndex() throws Exception {
+    byIgnore();
+  }
+
+  @Test
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  public void byIgnore_starsComputedFromAllUsersRepository() throws Exception {
+    byIgnore();
+  }
+
+  private void byIgnore() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     Account.Id user2 =
         accountManager.authenticate(authRequestFactory.createForUser("anotheruser")).getAccountId();
@@ -2492,6 +2593,42 @@
     assertQuery("-star:ignore", change2, change1);
   }
 
+  public void byStarWithManyStars_starsComputedFromIndex() throws Exception {
+    byStarWithManyStars();
+  }
+
+  @GerritConfig(
+      name = "experiments.enabled",
+      value =
+          ExperimentFeaturesConstants
+              .GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY)
+  public void byStarWithManyStars_starsComputedFromAllUsersRepository() throws Exception {
+    byStarWithManyStars();
+  }
+
+  private void byStarWithManyStars() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Change[] changesWithDrafts = new Change[30];
+    for (int i = 0; i < changesWithDrafts.length; i++) {
+      // put the changes in reverse order since this is the order we receive them from the index.
+      changesWithDrafts[changesWithDrafts.length - 1 - i] = insert(repo, newChange(repo));
+
+      // star the change
+      gApi.accounts()
+          .self()
+          .starChange(changesWithDrafts[changesWithDrafts.length - 1 - i].getId().toString());
+
+      // ignore the change
+      gApi.changes()
+          .id(changesWithDrafts[changesWithDrafts.length - 1 - i].getId().toString())
+          .ignore(true);
+    }
+
+    // all changes are both starred and ignored.
+    assertQuery("is:ignored", changesWithDrafts);
+    assertQuery("is:starred", changesWithDrafts);
+  }
+
   @Test
   public void byFrom() throws Exception {
     TestRepository<Repo> repo = createProject("repo");