// Copyright 2026 Google LLC
//
// 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.

syntax = "proto3";

package google.firestore.admin.v1;

import "google/api/field_behavior.proto";
import "google/api/resource.proto";

option csharp_namespace = "Google.Cloud.Firestore.Admin.V1";
option go_package = "cloud.google.com/go/firestore/apiv1/admin/adminpb;adminpb";
option java_multiple_files = true;
option java_outer_classname = "IndexProto";
option java_package = "com.google.firestore.admin.v1";
option objc_class_prefix = "GCFS";
option php_namespace = "Google\\Cloud\\Firestore\\Admin\\V1";
option ruby_package = "Google::Cloud::Firestore::Admin::V1";

// Cloud Firestore indexes enable simple and complex queries against
// documents in a database.
message Index {
  option (google.api.resource) = {
    type: "firestore.googleapis.com/Index"
    pattern: "projects/{project}/databases/{database}/collectionGroups/{collection}/indexes/{index}"
  };

  // Query Scope defines the scope at which a query is run. This is specified on
  // a StructuredQuery's `from` field.
  enum QueryScope {
    // The query scope is unspecified. Not a valid option.
    QUERY_SCOPE_UNSPECIFIED = 0;

    // Indexes with a collection query scope specified allow queries
    // against a collection that is the child of a specific document, specified
    // at query time, and that has the collection ID specified by the index.
    COLLECTION = 1;

    // Indexes with a collection group query scope specified allow queries
    // against all collections that has the collection ID specified by the
    // index.
    COLLECTION_GROUP = 2;

    // Include all the collections's ancestor in the index. Only available for
    // Datastore Mode databases.
    COLLECTION_RECURSIVE = 3;
  }

  // API Scope defines the APIs (Firestore Native, or Firestore in
  // Datastore Mode) that are supported for queries.
  enum ApiScope {
    // The index can only be used by the Firestore Native query API.
    // This is the default.
    ANY_API = 0;

    // The index can only be used by the Firestore in Datastore Mode query API.
    DATASTORE_MODE_API = 1;

    // The index can only be used by the MONGODB_COMPATIBLE_API.
    MONGODB_COMPATIBLE_API = 2;
  }

  // A field in an index.
  // The field_path describes which field is indexed, the value_mode describes
  // how the field value is indexed.
  message IndexField {
    // The supported orderings.
    enum Order {
      // The ordering is unspecified. Not a valid option.
      ORDER_UNSPECIFIED = 0;

      // The field is ordered by ascending field value.
      ASCENDING = 1;

      // The field is ordered by descending field value.
      DESCENDING = 2;
    }

    // The supported array value configurations.
    enum ArrayConfig {
      // The index does not support additional array queries.
      ARRAY_CONFIG_UNSPECIFIED = 0;

      // The index supports array containment queries.
      CONTAINS = 1;
    }

    // The index configuration to support vector search operations
    message VectorConfig {
      // An index that stores vectors in a flat data structure, and supports
      // exhaustive search.
      message FlatIndex {}

      // Required. The vector dimension this configuration applies to.
      //
      // The resulting index will only include vectors of this dimension, and
      // can be used for vector search with the same dimension.
      int32 dimension = 1 [(google.api.field_behavior) = REQUIRED];

      // The type of index used.
      oneof type {
        // Indicates the vector index is a flat index.
        FlatIndex flat = 2;
      }
    }

    // Can be __name__.
    // For single field indexes, this must match the name of the field or may
    // be omitted.
    string field_path = 1;

    // How the field value is indexed.
    oneof value_mode {
      // Indicates that this field supports ordering by the specified order or
      // comparing using =, !=, <, <=, >, >=.
      Order order = 2;

      // Indicates that this field supports operations on `array_value`s.
      ArrayConfig array_config = 3;

      // Indicates that this field supports nearest neighbor and distance
      // operations on vector.
      VectorConfig vector_config = 4;
    }
  }

  // The state of an index. During index creation, an index will be in the
  // `CREATING` state. If the index is created successfully, it will transition
  // to the `READY` state. If the index creation encounters a problem, the index
  // will transition to the `NEEDS_REPAIR` state.
  enum State {
    // The state is unspecified.
    STATE_UNSPECIFIED = 0;

    // The index is being created.
    // There is an active long-running operation for the index.
    // The index is updated when writing a document.
    // Some index data may exist.
    CREATING = 1;

    // The index is ready to be used.
    // The index is updated when writing a document.
    // The index is fully populated from all stored documents it applies to.
    READY = 2;

    // The index was being created, but something went wrong.
    // There is no active long-running operation for the index,
    // and the most recently finished long-running operation failed.
    // The index is not updated when writing a document.
    // Some index data may exist.
    // Use the google.longrunning.Operations API to determine why the operation
    // that last attempted to create this index failed, then re-create the
    // index.
    NEEDS_REPAIR = 3;
  }

  // The density configuration for the index.
  enum Density {
    // Unspecified. It will use database default setting. This value is input
    // only.
    DENSITY_UNSPECIFIED = 0;

    // An index entry will only exist if ALL fields are present in the document.
    //
    // This is both the default and only allowed value for Standard Edition
    // databases (for both Cloud Firestore `ANY_API` and Cloud Datastore
    // `DATASTORE_MODE_API`).
    //
    // Take for example the following document:
    //
    // ```
    // {
    //   "__name__": "...",
    //   "a": 1,
    //   "b": 2,
    //   "c": 3
    // }
    // ```
    //
    // an index on `(a ASC, b ASC, c ASC, __name__ ASC)` will generate an index
    // entry for this document since `a`, 'b', `c`, and `__name__` are all
    // present but an index of `(a ASC, d ASC, __name__ ASC)` will not generate
    // an index entry for this document since `d` is missing.
    //
    // This means that such indexes can only be used to serve a query when the
    // query has either implicit or explicit requirements that all fields from
    // the index are present.
    SPARSE_ALL = 1;

    // An index entry will exist if ANY field are present in the document.
    //
    // This is used as the definition of a sparse index for Enterprise Edition
    // databases.
    //
    // Take for example the following document:
    //
    // ```
    // {
    //   "__name__": "...",
    //   "a": 1,
    //   "b": 2,
    //   "c": 3
    // }
    // ```
    //
    // an index on `(a ASC, d ASC)` will generate an index entry for this
    // document since `a` is present, and will fill in an `unset` value for `d`.
    // An index on `(d ASC, e ASC)` will not generate any index entry as neither
    // `d` nor `e` are present.
    //
    // An index that contains `__name__` will generate an index entry for all
    // documents since Firestore guarantees that all documents have a `__name__`
    // field.
    SPARSE_ANY = 2;

    // An index entry will exist regardless of if the fields are present or not.
    //
    // This is the default density for an Enterprise Edition database.
    //
    // The index will store `unset` values for fields that are not present in
    // the document.
    DENSE = 3;
  }

  // Output only. A server defined name for this index.
  // The form of this name for composite indexes will be:
  // `projects/{project_id}/databases/{database_id}/collectionGroups/{collection_id}/indexes/{composite_index_id}`
  // For single field indexes, this field will be empty.
  string name = 1;

  // Indexes with a collection query scope specified allow queries
  // against a collection that is the child of a specific document, specified at
  // query time, and that has the same collection ID.
  //
  // Indexes with a collection group query scope specified allow queries against
  // all collections descended from a specific document, specified at query
  // time, and that have the same collection ID as this index.
  QueryScope query_scope = 2;

  // The API scope supported by this index.
  ApiScope api_scope = 5;

  // The fields supported by this index.
  //
  // For composite indexes, this requires a minimum of 2 and a maximum of 100
  // fields. The last field entry is always for the field path `__name__`. If,
  // on creation, `__name__` was not specified as the last field, it will be
  // added automatically with the same direction as that of the last field
  // defined. If the final field in a composite index is not directional, the
  // `__name__` will be ordered ASCENDING (unless explicitly specified).
  //
  // For single field indexes, this will always be exactly one entry with a
  // field path equal to the field path of the associated field.
  repeated IndexField fields = 3;

  // Output only. The serving state of the index.
  State state = 4;

  // Immutable. The density configuration of the index.
  Density density = 6 [(google.api.field_behavior) = IMMUTABLE];

  // Optional. Whether the index is multikey. By default, the index is not
  // multikey. For non-multikey indexes, none of the paths in the index
  // definition reach or traverse an array, except via an explicit array index.
  // For multikey indexes, at most one of the paths in the index definition
  // reach or traverse an array, except via an explicit array index. Violations
  // will result in errors.
  //
  // Note this field only applies to index with MONGODB_COMPATIBLE_API ApiScope.
  bool multikey = 7 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The number of shards for the index.
  int32 shard_count = 8 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Whether it is an unique index. Unique index ensures all values
  // for the indexed field(s) are unique across documents.
  bool unique = 10 [(google.api.field_behavior) = OPTIONAL];
}
