Merge "Part9: Migrate ChangeIndex FieldDefs to the new format SchemaFields"
diff --git a/java/com/google/gerrit/index/SchemaUtil.java b/java/com/google/gerrit/index/SchemaUtil.java
index 8f47cf5..079f8be 100644
--- a/java/com/google/gerrit/index/SchemaUtil.java
+++ b/java/com/google/gerrit/index/SchemaUtil.java
@@ -103,6 +103,17 @@
   }
 
   public static <V> Schema<V> schema(
+      int version,
+      ImmutableList<IndexedField<V, ?>> indexedFields,
+      ImmutableList<IndexedField<V, ?>.SearchSpec> searchSpecs) {
+    return new Schema.Builder<V>()
+        .version(version)
+        .addIndexedFields(indexedFields)
+        .addSearchSpecs(searchSpecs)
+        .build();
+  }
+
+  public static <V> Schema<V> schema(
       Schema<V> schema,
       ImmutableList<FieldDef<V, ?>> fieldDefs,
       ImmutableList<IndexedField<V, ?>> indexedFields,
@@ -116,6 +127,17 @@
   }
 
   public static <V> Schema<V> schema(
+      Schema<V> schema,
+      ImmutableList<IndexedField<V, ?>> indexedFields,
+      ImmutableList<IndexedField<V, ?>.SearchSpec> searchSpecs) {
+    return new Schema.Builder<V>()
+        .add(schema)
+        .addIndexedFields(indexedFields)
+        .addSearchSpecs(searchSpecs)
+        .build();
+  }
+
+  public static <V> Schema<V> schema(
       ImmutableList<FieldDef<V, ?>> fieldDefs,
       ImmutableList<IndexedField<V, ?>> indexFields,
       ImmutableList<IndexedField<V, ?>.SearchSpec> searchSpecs) {
diff --git a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
index 5838ff1..d60be14 100644
--- a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
+++ b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
@@ -268,7 +268,8 @@
       ChangeData cd =
           changeDataFactory.create(
               Project.nameKey((String) doc.get(ChangeField.PROJECT_SPEC.getName())),
-              Change.id(Integer.valueOf((String) doc.get(ChangeField.LEGACY_ID_STR.getName()))));
+              Change.id(
+                  Integer.valueOf((String) doc.get(ChangeField.NUMERIC_ID_STR_SPEC.getName()))));
       for (SchemaField<ChangeData, ?> field : getSchema().getSchemaFields().values()) {
         boolean isProtoField = SchemaFieldDefs.isProtoField(field);
         field.setIfPossible(cd, new FakeStoredValue(doc.get(field.getName()), isProtoField));
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index f7b1f2c..84cc83a 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -119,10 +119,10 @@
   void add(Document doc, Values<ChangeData> values) {
     // Add separate DocValues fields for those fields needed for sorting.
     SchemaField<ChangeData, ?> f = values.getField();
-    if (f == ChangeField.LEGACY_ID_STR) {
+    if (f == ChangeField.NUMERIC_ID_STR_SPEC) {
       String v = (String) getOnlyElement(values.getValues());
       doc.add(new NumericDocValuesField(ID_STR_SORT_FIELD, Integer.valueOf(v)));
-    } else if (f == ChangeField.UPDATED) {
+    } else if (f == ChangeField.UPDATED_SPEC) {
       long t = ((Timestamp) getOnlyElement(values.getValues())).getTime();
       doc.add(new NumericDocValuesField(UPDATED_SORT_FIELD, t));
     } else if (f == ChangeField.MERGED_ON_SPEC) {
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index de4b26f..e16adf5 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -17,7 +17,7 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.gerrit.lucene.AbstractLuceneIndex.sortFieldName;
 import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
-import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
+import static com.google.gerrit.server.index.change.ChangeField.NUMERIC_ID_STR_SPEC;
 import static com.google.gerrit.server.index.change.ChangeField.PROJECT_SPEC;
 import static com.google.gerrit.server.index.change.ChangeIndexRewriter.CLOSED_STATUSES;
 import static com.google.gerrit.server.index.change.ChangeIndexRewriter.OPEN_STATUSES;
@@ -103,21 +103,21 @@
 public class LuceneChangeIndex implements ChangeIndex {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  static final String UPDATED_SORT_FIELD = sortFieldName(ChangeField.UPDATED);
+  static final String UPDATED_SORT_FIELD = sortFieldName(ChangeField.UPDATED_SPEC);
   static final String MERGED_ON_SORT_FIELD = sortFieldName(ChangeField.MERGED_ON_SPEC);
-  static final String ID_STR_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID_STR);
+  static final String ID_STR_SORT_FIELD = sortFieldName(ChangeField.NUMERIC_ID_STR_SPEC);
 
   private static final String CHANGES = "changes";
   private static final String CHANGES_OPEN = "open";
   private static final String CHANGES_CLOSED = "closed";
-  private static final String CHANGE_FIELD = ChangeField.CHANGE.getName();
+  private static final String CHANGE_FIELD = ChangeField.CHANGE_SPEC.getName();
 
   static Term idTerm(ChangeData cd) {
     return idTerm(cd.getId());
   }
 
   static Term idTerm(Change.Id id) {
-    return QueryBuilder.stringTerm(LEGACY_ID_STR.getName(), Integer.toString(id.get()));
+    return QueryBuilder.stringTerm(NUMERIC_ID_STR_SPEC.getName(), Integer.toString(id.get()));
   }
 
   private final ListeningExecutorService executor;
@@ -501,7 +501,7 @@
         ImmutableList.Builder<ChangeData> result =
             ImmutableList.builderWithExpectedSize(docs.size());
         for (Document doc : docs) {
-          result.add(toChangeData(fields(doc, fields), fields, LEGACY_ID_STR.getName()));
+          result.add(toChangeData(fields(doc, fields), fields, NUMERIC_ID_STR_SPEC.getName()));
         }
         return result.build();
       } catch (InterruptedException e) {
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 0c8bccd..fd08fa8 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -312,7 +312,7 @@
     List<ChangeData> changeData =
         queryProvider
             .get()
-            .setRequestedFields(ChangeField.ID, ChangeField.STAR_SPEC)
+            .setRequestedFields(ChangeField.CHANGE_ID_SPEC, ChangeField.STAR_SPEC)
             .byLegacyChangeId(changeId);
     if (changeData.size() != 1) {
       throw new NoSuchChangeException(changeId);
diff --git a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
index 727aab3..cfeec70 100644
--- a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
+++ b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
@@ -162,7 +162,7 @@
         List<ChangeData> cds =
             queryProvider
                 .get()
-                .setRequestedFields(ChangeField.CHANGE, ChangeField.REVIEWER_SPEC)
+                .setRequestedFields(ChangeField.CHANGE_SPEC, ChangeField.REVIEWER_SPEC)
                 .byProject(key);
         Map<Change.Id, CachedChange> result = new HashMap<>(cds.size());
         for (ChangeData cd : cds) {
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
index 5667c28..7c22bd8 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
@@ -111,7 +111,7 @@
               .get()
               .setRequestedFields(
                   // Required for ChangeIsVisibleToPrdicate.
-                  ChangeField.CHANGE,
+                  ChangeField.CHANGE_SPEC,
                   ChangeField.REVIEWER_SPEC,
                   // Required during advertiseOpenChanges.
                   ChangeField.PATCH_SET_SPEC)
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index 7d40c00..213094e 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.index;
 
-import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
-import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
+import static com.google.gerrit.server.index.change.ChangeField.CHANGE_SPEC;
+import static com.google.gerrit.server.index.change.ChangeField.NUMERIC_ID_STR_SPEC;
 import static com.google.gerrit.server.index.change.ChangeField.PROJECT_SPEC;
 
 import com.google.common.collect.ImmutableSet;
@@ -77,14 +77,14 @@
     // change ID and project, which can either come via the Change field or
     // separate fields.
     Set<String> fs = opts.fields();
-    if (fs.contains(CHANGE.getName())) {
+    if (fs.contains(CHANGE_SPEC.getName())) {
       // A Change is always sufficient.
       return fs;
     }
-    if (fs.contains(PROJECT_SPEC.getName()) && fs.contains(LEGACY_ID_STR.getName())) {
+    if (fs.contains(PROJECT_SPEC.getName()) && fs.contains(NUMERIC_ID_STR_SPEC.getName())) {
       return fs;
     }
-    return Sets.union(fs, ImmutableSet.of(LEGACY_ID_STR.getName(), PROJECT_SPEC.getName()));
+    return Sets.union(fs, ImmutableSet.of(NUMERIC_ID_STR_SPEC.getName(), PROJECT_SPEC.getName()));
   }
 
   /**
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 46b1a7b..1c0b368 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -18,10 +18,6 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.index.FieldDef.exact;
-import static com.google.gerrit.index.FieldDef.prefix;
-import static com.google.gerrit.index.FieldDef.storedOnly;
-import static com.google.gerrit.index.FieldDef.timestamp;
 import static com.google.gerrit.server.util.AttentionSetUtil.additionsOnly;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.joining;
@@ -59,14 +55,12 @@
 import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
 import com.google.gerrit.entities.converter.PatchSetProtoConverter;
 import com.google.gerrit.entities.converter.ProtoConverter;
-import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.IndexedField;
 import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.SchemaFieldDefs;
 import com.google.gerrit.index.SchemaUtil;
 import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.proto.Entities;
-import com.google.gerrit.proto.Protos;
 import com.google.gerrit.server.ReviewerByEmailSet;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.StarredChangesUtil;
@@ -128,12 +122,28 @@
 
   // TODO: Rename LEGACY_ID to NUMERIC_ID
   /** Legacy change ID. */
-  public static final FieldDef<ChangeData, String> LEGACY_ID_STR =
-      exact("legacy_id_str").stored().build(cd -> String.valueOf(cd.getId().get()));
+  public static final IndexedField<ChangeData, String> NUMERIC_ID_STR_FIELD =
+      IndexedField.<ChangeData>stringBuilder("NumericIdStr")
+          .stored()
+          .required()
+          // The numeric change id is integer in string form
+          .size(10)
+          .build(cd -> String.valueOf(cd.getId().get()));
+
+  public static final IndexedField<ChangeData, String>.SearchSpec NUMERIC_ID_STR_SPEC =
+      NUMERIC_ID_STR_FIELD.exact("legacy_id_str");
 
   /** Newer style Change-Id key. */
-  public static final FieldDef<ChangeData, String> ID =
-      prefix(ChangeQueryBuilder.FIELD_CHANGE_ID).build(changeGetter(c -> c.getKey().get()));
+  public static final IndexedField<ChangeData, String> CHANGE_ID_FIELD =
+      IndexedField.<ChangeData>stringBuilder("ChangeId")
+          .stored()
+          .required()
+          // The new style key is in form Isha1
+          .size(41)
+          .build(changeGetter(c -> c.getKey().get()));
+
+  public static final IndexedField<ChangeData, String>.SearchSpec CHANGE_ID_SPEC =
+      CHANGE_ID_FIELD.prefix(ChangeQueryBuilder.FIELD_CHANGE_ID);
 
   /** Change status string, in the same format as {@code status:}. */
   public static final IndexedField<ChangeData, String> STATUS_FIELD =
@@ -196,11 +206,14 @@
 
   /** Last update time since January 1, 1970. */
   // TODO(issue-15518): Migrate type for timestamp index fields from Timestamp to Instant
-  public static final FieldDef<ChangeData, Timestamp> UPDATED =
-      timestamp("updated2")
+  public static final IndexedField<ChangeData, Timestamp> UPDATED_FIELD =
+      IndexedField.<ChangeData>timestampBuilder("LastUpdated")
           .stored()
           .build(changeGetter(change -> Timestamp.from(change.getLastUpdatedOn())));
 
+  public static final IndexedField<ChangeData, Timestamp>.SearchSpec UPDATED_SPEC =
+      UPDATED_FIELD.timestamp("updated2");
+
   /** When this change was merged, time since January 1, 1970. */
   // TODO(issue-15518): Migrate type for timestamp index fields from Timestamp to Instant
   public static final IndexedField<ChangeData, Timestamp> MERGED_ON_FIELD =
@@ -975,11 +988,23 @@
       EXACT_COMMITTER_FIELD.exact(ChangeQueryBuilder.FIELD_EXACTCOMMITTER);
 
   /** Serialized change object, used for pre-populating results. */
-  public static final FieldDef<ChangeData, byte[]> CHANGE =
-      storedOnly("_change")
+  private static final TypeToken<Entities.Change> CHANGE_TYPE_TOKEN =
+      new TypeToken<Entities.Change>() {
+        private static final long serialVersionUID = 1L;
+      };
+
+  public static final IndexedField<ChangeData, Entities.Change> CHANGE_FIELD =
+      IndexedField.<ChangeData, Entities.Change>builder("Change", CHANGE_TYPE_TOKEN)
+          .stored()
+          .required()
+          .protoConverter(Optional.of(ChangeProtoConverter.INSTANCE))
           .build(
-              changeGetter(change -> toProto(ChangeProtoConverter.INSTANCE, change)),
-              (cd, field) -> cd.setChange(parseProtoFrom(field, ChangeProtoConverter.INSTANCE)));
+              changeGetter(change -> entityToProto(ChangeProtoConverter.INSTANCE, change)),
+              (cd, value) ->
+                  cd.setChange(decodeProtoToEntity(value, ChangeProtoConverter.INSTANCE)));
+
+  public static final IndexedField<ChangeData, Entities.Change>.SearchSpec CHANGE_SPEC =
+      CHANGE_FIELD.storedOnly("_change");
 
   /** Serialized approvals for the current patch set, used for pre-populating results. */
   private static final TypeToken<Iterable<Entities.PatchSetApproval>> APPROVAL_TYPE_TOKEN =
@@ -1631,9 +1656,10 @@
    *
    * <p>Emitted as UTF-8 encoded strings of the form {@code project:ref/name:[hex sha]}.
    */
-  public static final FieldDef<ChangeData, Iterable<byte[]>> REF_STATE =
-      storedOnly("ref_state")
-          .buildRepeatable(
+  public static final IndexedField<ChangeData, Iterable<byte[]>> REF_STATE_FIELD =
+      IndexedField.<ChangeData>iterableByteArrayBuilder("RefState")
+          .stored()
+          .build(
               cd -> {
                 List<byte[]> result = new ArrayList<>();
                 cd.getRefStates()
@@ -1643,15 +1669,19 @@
               },
               (cd, field) -> cd.setRefStates(RefState.parseStates(field)));
 
+  public static final IndexedField<ChangeData, Iterable<byte[]>>.SearchSpec REF_STATE_SPEC =
+      REF_STATE_FIELD.storedOnly("ref_state");
+
   /**
    * All ref wildcard patterns that were used in the course of indexing this document.
    *
    * <p>Emitted as UTF-8 encoded strings of the form {@code project:ref/name/*}. See {@link
    * RefStatePattern} for the pattern format.
    */
-  public static final FieldDef<ChangeData, Iterable<byte[]>> REF_STATE_PATTERN =
-      storedOnly("ref_state_pattern")
-          .buildRepeatable(
+  public static final IndexedField<ChangeData, Iterable<byte[]>> REF_STATE_PATTERN_FIELD =
+      IndexedField.<ChangeData>iterableByteArrayBuilder("RefStatePattern")
+          .stored()
+          .build(
               cd -> {
                 Change.Id id = cd.getId();
                 Project.NameKey project = cd.change().getProject();
@@ -1670,6 +1700,9 @@
               },
               (cd, field) -> cd.setRefStatePatterns(field));
 
+  public static final IndexedField<ChangeData, Iterable<byte[]>>.SearchSpec REF_STATE_PATTERN_SPEC =
+      REF_STATE_PATTERN_FIELD.storedOnly("ref_state_pattern");
+
   @Nullable
   private static String getTopic(ChangeData cd) {
     Change c = cd.change();
@@ -1679,10 +1712,6 @@
     return firstNonNull(c.getTopic(), "");
   }
 
-  private static <T> byte[] toProto(ProtoConverter<?, T> converter, T object) {
-    return Protos.toByteArray(converter.toProto(object));
-  }
-
   private static <V extends MessageLite, T> V entityToProto(
       ProtoConverter<V, T> converter, T object) {
     return converter.toProto(object);
@@ -1707,12 +1736,6 @@
     return converter.fromProto(proto);
   }
 
-  private static <P extends MessageLite, T> T parseProtoFrom(
-      byte[] bytes, ProtoConverter<P, T> converter) {
-    P message = Protos.parseUnchecked(converter.getParser(), bytes, 0, bytes.length);
-    return converter.fromProto(message);
-  }
-
   private static <T> SchemaFieldDefs.Getter<ChangeData, T> changeGetter(Function<Change, T> func) {
     return in -> in.change() != null ? func.apply(in.change()) : null;
   }
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index 840cf59..5dc1500 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -34,13 +34,7 @@
   static final Schema<ChangeData> V74 =
       schema(
           /* version= */ 74,
-          ImmutableList.of(
-              ChangeField.CHANGE,
-              ChangeField.ID,
-              ChangeField.LEGACY_ID_STR,
-              ChangeField.REF_STATE,
-              ChangeField.REF_STATE_PATTERN,
-              ChangeField.UPDATED),
+          ImmutableList.of(),
           ImmutableList.<IndexedField<ChangeData, ?>>of(
               ChangeField.ADDED_LINES_FIELD,
               ChangeField.APPROVAL_FIELD,
@@ -49,6 +43,8 @@
               ChangeField.ATTENTION_SET_USERS_COUNT_FIELD,
               ChangeField.ATTENTION_SET_USERS_FIELD,
               ChangeField.AUTHOR_PARTS_FIELD,
+              ChangeField.CHANGE_FIELD,
+              ChangeField.CHANGE_ID_FIELD,
               ChangeField.CHERRY_PICK_FIELD,
               ChangeField.CHERRY_PICK_OF_CHANGE_FIELD,
               ChangeField.CHERRY_PICK_OF_PATCHSET_FIELD,
@@ -76,6 +72,7 @@
               ChangeField.MERGEABLE_FIELD,
               ChangeField.MERGED_ON_FIELD,
               ChangeField.MERGE_FIELD,
+              ChangeField.NUMERIC_ID_STR_FIELD,
               ChangeField.ONLY_EXTENSIONS_FIELD,
               ChangeField.OWNER_FIELD,
               ChangeField.PATCH_SET_FIELD,
@@ -85,6 +82,8 @@
               ChangeField.PRIVATE_FIELD,
               ChangeField.PROJECT_FIELD,
               ChangeField.REF_FIELD,
+              ChangeField.REF_STATE_FIELD,
+              ChangeField.REF_STATE_PATTERN_FIELD,
               ChangeField.REVERT_OF_FIELD,
               ChangeField.REVIEWEDBY_FIELD,
               ChangeField.REVIEWER_BY_EMAIL_FIELD,
@@ -98,13 +97,14 @@
               ChangeField.STORED_SUBMIT_REQUIREMENTS_FIELD,
               ChangeField.SUBMISSIONID_FIELD,
               ChangeField.SUBMIT_RECORD_FIELD,
+              ChangeField.SUBMIT_RULE_RESULT_FIELD,
               ChangeField.TOPIC_FIELD,
               ChangeField.TOTAL_COMMENT_COUNT_FIELD,
               ChangeField.TR_FIELD,
               ChangeField.UNRESOLVED_COMMENT_COUNT_FIELD,
+              ChangeField.UPDATED_FIELD,
               ChangeField.UPLOADER_FIELD,
-              ChangeField.WIP_FIELD,
-              ChangeField.SUBMIT_RULE_RESULT_FIELD),
+              ChangeField.WIP_FIELD),
           ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
               ChangeField.ADDED_LINES_SPEC,
               ChangeField.APPROVAL_SPEC,
@@ -113,6 +113,8 @@
               ChangeField.ATTENTION_SET_USERS,
               ChangeField.ATTENTION_SET_USERS_COUNT,
               ChangeField.AUTHOR_PARTS_SPEC,
+              ChangeField.CHANGE_ID_SPEC,
+              ChangeField.CHANGE_SPEC,
               ChangeField.CHERRY_PICK_OF_CHANGE,
               ChangeField.CHERRY_PICK_OF_PATCHSET,
               ChangeField.CHERRY_PICK_SPEC,
@@ -144,6 +146,7 @@
               ChangeField.MERGEABLE_SPEC,
               ChangeField.MERGED_ON_SPEC,
               ChangeField.MERGE_SPEC,
+              ChangeField.NUMERIC_ID_STR_SPEC,
               ChangeField.ONLY_EXTENSIONS_SPEC,
               ChangeField.OWNER_SPEC,
               ChangeField.PATCH_SET_SPEC,
@@ -154,6 +157,8 @@
               ChangeField.PROJECTS_SPEC,
               ChangeField.PROJECT_SPEC,
               ChangeField.REF_SPEC,
+              ChangeField.REF_STATE_PATTERN_SPEC,
+              ChangeField.REF_STATE_SPEC,
               ChangeField.REVERT_OF,
               ChangeField.REVIEWEDBY_SPEC,
               ChangeField.REVIEWER_BY_EMAIL,
@@ -167,12 +172,13 @@
               ChangeField.STORED_SUBMIT_REQUIREMENTS_SPEC,
               ChangeField.SUBMISSIONID_SPEC,
               ChangeField.SUBMIT_RECORD_SPEC,
+              ChangeField.SUBMIT_RULE_RESULT_SPEC,
               ChangeField.TOTAL_COMMENT_COUNT_SPEC,
               ChangeField.TR_SPEC,
               ChangeField.UNRESOLVED_COMMENT_COUNT_SPEC,
+              ChangeField.UPDATED_SPEC,
               ChangeField.UPLOADER_SPEC,
-              ChangeField.WIP_SPEC,
-              ChangeField.SUBMIT_RULE_RESULT_SPEC));
+              ChangeField.WIP_SPEC));
 
   /**
    * Added new field {@link ChangeField#PREFIX_HASHTAG} and {@link ChangeField#PREFIX_TOPIC} to
diff --git a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
index 76610f3..26477a4 100644
--- a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
+++ b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.index.change;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
+import static com.google.gerrit.server.index.change.ChangeField.CHANGE_SPEC;
 import static com.google.gerrit.server.index.change.ChangeField.PROJECT_SPEC;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -69,7 +69,7 @@
       int limit,
       Set<String> fields) {
     // Always include project since it is needed to load the change from NoteDb.
-    if (!fields.contains(CHANGE.getName()) && !fields.contains(PROJECT_SPEC.getName())) {
+    if (!fields.contains(CHANGE_SPEC.getName()) && !fields.contains(PROJECT_SPEC.getName())) {
       fields = new HashSet<>(fields);
       fields.add(PROJECT_SPEC.getName());
     }
@@ -176,6 +176,6 @@
 
   @Override
   public boolean hasChange() {
-    return index.getSchema().hasField(ChangeField.CHANGE);
+    return index.getSchema().hasField(ChangeField.CHANGE_SPEC);
   }
 }
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index ad5cc2b..eb4af01 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -56,9 +56,9 @@
 
   public static final ImmutableSet<String> FIELDS =
       ImmutableSet.of(
-          ChangeField.CHANGE.getName(),
-          ChangeField.REF_STATE.getName(),
-          ChangeField.REF_STATE_PATTERN.getName());
+          ChangeField.CHANGE_SPEC.getName(),
+          ChangeField.REF_STATE_SPEC.getName(),
+          ChangeField.REF_STATE_PATTERN_SPEC.getName());
 
   private final ChangeIndexCollection indexes;
   private final GitRepositoryManager repoManager;
@@ -82,8 +82,8 @@
       return StalenessCheckResult
           .notStale(); // No index; caller couldn't do anything if it is stale.
     }
-    if (!i.getSchema().hasField(ChangeField.REF_STATE)
-        || !i.getSchema().hasField(ChangeField.REF_STATE_PATTERN)) {
+    if (!i.getSchema().hasField(ChangeField.REF_STATE_SPEC)
+        || !i.getSchema().hasField(ChangeField.REF_STATE_PATTERN_SPEC)) {
       return StalenessCheckResult.notStale(); // Index version not new enough for this check.
     }
 
diff --git a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
index 34b40f5..9463b39 100644
--- a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
@@ -264,7 +264,7 @@
               .changeIndexQuery(
                   "projectsConsistencyCheckerQueryChanges",
                   q ->
-                      q.setRequestedFields(ChangeField.CHANGE, ChangeField.PATCH_SET_SPEC)
+                      q.setRequestedFields(ChangeField.CHANGE_SPEC, ChangeField.PATCH_SET_SPEC)
                           .query(and(basePredicate, or(predicates))))
               .call();
 
diff --git a/java/com/google/gerrit/server/query/change/AgePredicate.java b/java/com/google/gerrit/server/query/change/AgePredicate.java
index c1138bd..8a9ba2e 100644
--- a/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -27,7 +27,7 @@
   protected final Instant cut;
 
   public AgePredicate(String value) {
-    super(ChangeField.UPDATED, ChangeQueryBuilder.FIELD_AGE, value);
+    super(ChangeField.UPDATED_SPEC, ChangeQueryBuilder.FIELD_AGE, value);
 
     long s = ConfigUtil.getTimeUnit(getValue(), 0, SECONDS);
     long ms = MILLISECONDS.convert(s, SECONDS);
diff --git a/java/com/google/gerrit/server/query/change/ChangePredicates.java b/java/com/google/gerrit/server/query/change/ChangePredicates.java
index e96db3d..84ceb3d 100644
--- a/java/com/google/gerrit/server/query/change/ChangePredicates.java
+++ b/java/com/google/gerrit/server/query/change/ChangePredicates.java
@@ -128,7 +128,7 @@
    */
   public static Predicate<ChangeData> idStr(Change.Id id) {
     return new ChangeIndexCardinalPredicate(
-        ChangeField.LEGACY_ID_STR, ChangeQueryBuilder.FIELD_CHANGE, id.toString(), 1);
+        ChangeField.NUMERIC_ID_STR_SPEC, ChangeQueryBuilder.FIELD_CHANGE, id.toString(), 1);
   }
 
   /**
@@ -302,7 +302,7 @@
 
   /** Returns a predicate that matches changes whose ID starts with the provided {@code id}. */
   public static Predicate<ChangeData> idPrefix(String id) {
-    return new ChangeIndexCardinalPredicate(ChangeField.ID, id, 5);
+    return new ChangeIndexCardinalPredicate(ChangeField.CHANGE_ID_SPEC, id, 5);
   }
 
   /**
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 8262e58..83c348c 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -520,7 +520,7 @@
 
   @Operator
   public Predicate<ChangeData> before(String value) throws QueryParseException {
-    return new BeforePredicate(ChangeField.UPDATED, ChangeQueryBuilder.OPERATOR_BEFORE, value);
+    return new BeforePredicate(ChangeField.UPDATED_SPEC, ChangeQueryBuilder.OPERATOR_BEFORE, value);
   }
 
   @Operator
@@ -530,7 +530,7 @@
 
   @Operator
   public Predicate<ChangeData> after(String value) throws QueryParseException {
-    return new AfterPredicate(ChangeField.UPDATED, ChangeQueryBuilder.OPERATOR_AFTER, value);
+    return new AfterPredicate(ChangeField.UPDATED_SPEC, ChangeQueryBuilder.OPERATOR_AFTER, value);
   }
 
   @Operator
diff --git a/javatests/com/google/gerrit/index/IndexUpgradeValidatorTest.java b/javatests/com/google/gerrit/index/IndexUpgradeValidatorTest.java
index aa2605e..7f98a9d 100644
--- a/javatests/com/google/gerrit/index/IndexUpgradeValidatorTest.java
+++ b/javatests/com/google/gerrit/index/IndexUpgradeValidatorTest.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.index.SchemaFieldDefs.Getter;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -35,23 +34,42 @@
   // SchemaFields.
   @Test
   public void valid() {
-    IndexUpgradeValidator.assertValid(schema(1, ChangeField.ID), schema(2, ChangeField.ID));
     IndexUpgradeValidator.assertValid(
-        schema(1, ChangeField.ID),
+        schema(
+            1,
+            ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+            ImmutableList.of(ChangeField.CHANGE_ID_SPEC)),
         schema(
             2,
-            ImmutableList.<FieldDef<ChangeData, ?>>of(ChangeField.ID),
-            ImmutableList.<IndexedField<ChangeData, ?>>of(ChangeField.OWNER_FIELD),
-            ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(ChangeField.OWNER_SPEC)));
+            ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+            ImmutableList.of(ChangeField.CHANGE_ID_SPEC)));
     IndexUpgradeValidator.assertValid(
-        schema(1, ChangeField.ID),
+        schema(
+            1,
+            ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+            ImmutableList.of(ChangeField.CHANGE_ID_SPEC)),
         schema(
             2,
-            ImmutableList.<FieldDef<ChangeData, ?>>of(ChangeField.ID),
+            ImmutableList.<FieldDef<ChangeData, ?>>of(),
             ImmutableList.<IndexedField<ChangeData, ?>>of(
-                ChangeField.OWNER_FIELD, ChangeField.COMMITTER_PARTS_FIELD),
+                ChangeField.OWNER_FIELD, ChangeField.CHANGE_ID_FIELD),
             ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
-                ChangeField.OWNER_SPEC, ChangeField.COMMITTER_PARTS_SPEC)));
+                ChangeField.OWNER_SPEC, ChangeField.CHANGE_ID_SPEC)));
+    IndexUpgradeValidator.assertValid(
+        schema(
+            1,
+            ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+            ImmutableList.of(ChangeField.CHANGE_ID_SPEC)),
+        schema(
+            2,
+            ImmutableList.<IndexedField<ChangeData, ?>>of(
+                ChangeField.CHANGE_ID_FIELD,
+                ChangeField.OWNER_FIELD,
+                ChangeField.COMMITTER_PARTS_FIELD),
+            ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
+                ChangeField.CHANGE_ID_SPEC,
+                ChangeField.OWNER_SPEC,
+                ChangeField.COMMITTER_PARTS_SPEC)));
   }
 
   @Test
@@ -61,10 +79,12 @@
             AssertionError.class,
             () ->
                 IndexUpgradeValidator.assertValid(
-                    schema(1, ChangeField.ID),
+                    schema(
+                        1,
+                        ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+                        ImmutableList.of(ChangeField.CHANGE_ID_SPEC)),
                     schema(
                         2,
-                        ImmutableList.<FieldDef<ChangeData, ?>>of(),
                         ImmutableList.<IndexedField<ChangeData, ?>>of(ChangeField.OWNER_FIELD),
                         ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
                             ChangeField.OWNER_SPEC))));
@@ -76,32 +96,38 @@
   @Test
   public void invalid_modify() {
     // Change value type from String to Integer.
-    FieldDef<ChangeData, Integer> ID_MODIFIED =
-        new FieldDef.Builder<>(FieldType.INTEGER, ChangeQueryBuilder.FIELD_CHANGE_ID)
-            .build(cd -> 42);
+    IndexedField<ChangeData, Integer> ID_MODIFIED =
+        IndexedField.<ChangeData>integerBuilder(ChangeField.CHANGE_ID_FIELD.name()).build(cd -> 42);
     AssertionError e =
         assertThrows(
             AssertionError.class,
             () ->
                 IndexUpgradeValidator.assertValid(
-                    schema(1, ChangeField.ID), schema(2, ID_MODIFIED)));
+                    schema(
+                        1,
+                        ImmutableList.of(ChangeField.CHANGE_ID_FIELD),
+                        ImmutableList.of(ChangeField.CHANGE_ID_SPEC)),
+                    schema(2, ImmutableList.of(ID_MODIFIED), ImmutableList.of())));
     assertThat(e).hasMessageThat().contains("Fields may not be modified");
-    assertThat(e).hasMessageThat().contains(ChangeQueryBuilder.FIELD_CHANGE_ID);
+    assertThat(e).hasMessageThat().contains(ChangeField.CHANGE_ID_FIELD.name());
   }
 
   @Test
   public void invalid_modify_referenceEquality() {
     // Comparison uses Object.equals(), i.e. reference equality.
     Getter<ChangeData, String> getter = cd -> cd.change().getKey().get();
-    FieldDef<ChangeData, String> ID_1 =
-        new FieldDef.Builder<>(FieldType.PREFIX, ChangeQueryBuilder.FIELD_CHANGE_ID).build(getter);
-    FieldDef<ChangeData, String> ID_2 =
-        new FieldDef.Builder<>(FieldType.PREFIX, ChangeQueryBuilder.FIELD_CHANGE_ID).build(getter);
+    IndexedField<ChangeData, String> ID_1 =
+        IndexedField.<ChangeData>stringBuilder(ChangeField.CHANGE_ID_FIELD.name()).build(getter);
+    IndexedField<ChangeData, String> ID_2 =
+        IndexedField.<ChangeData>stringBuilder(ChangeField.CHANGE_ID_FIELD.name()).build(getter);
     AssertionError e =
         assertThrows(
             AssertionError.class,
-            () -> IndexUpgradeValidator.assertValid(schema(1, ID_1), schema(2, ID_2)));
+            () ->
+                IndexUpgradeValidator.assertValid(
+                    schema(1, ImmutableList.of(ID_1), ImmutableList.of()),
+                    schema(2, ImmutableList.of(ID_2), ImmutableList.of())));
     assertThat(e).hasMessageThat().contains("Fields may not be modified");
-    assertThat(e).hasMessageThat().contains(ChangeQueryBuilder.FIELD_CHANGE_ID);
+    assertThat(e).hasMessageThat().contains(ChangeField.CHANGE_ID_FIELD.name());
   }
 }
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index 2aa9ca4..95e8aa5 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -42,11 +42,11 @@
   static final Schema<ChangeData> V2 =
       schema(
           2,
-          ImmutableList.<FieldDef<ChangeData, ?>>of(ChangeField.UPDATED),
+          ImmutableList.of(),
           ImmutableList.<IndexedField<ChangeData, ?>>of(
-              ChangeField.PATH_FIELD, ChangeField.STATUS_FIELD),
+              ChangeField.PATH_FIELD, ChangeField.STATUS_FIELD, ChangeField.UPDATED_FIELD),
           ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
-              ChangeField.PATH_SPEC, ChangeField.STATUS_SPEC));
+              ChangeField.PATH_SPEC, ChangeField.STATUS_SPEC, ChangeField.UPDATED_SPEC));
 
   private static class Source implements ChangeDataSource {
     private final Predicate<ChangeData> p;