管理索引

为保证查询性能,Firestore 要求每个查询都要有索引。它会自动为您创建最基本的查询所需的索引。在您使用和测试应用的过程中,Cloud Firestore 会生成错误消息,帮助您创建您的应用所需的额外索引。本页介绍了如何管理单字段复合和 [向量][向量] 索引。

通过错误消息创建缺少的索引

如果您尝试使用未映射到现有索引的范围子句进行复合查询,则会收到错误。错误消息中包含一个直接链接,用于在 Firebase 控制台中创建缺失的索引。

通过生成的链接转到 Firebase 控制台,查看自动填充的信息,然后点击创建

如果需要向量索引,错误消息将包含一个 Google Cloud CLI 命令,用于创建缺失的向量索引。运行该命令可以创建缺失的索引。

角色与权限

要在 Firestore 中创建索引,您必须分配有以下角色之一:

  • roles/datastore.owner
  • roles/datastore.indexAdmin
  • roles/editor
  • roles/owner

如果您定义了自定义角色,则必须为其分配以下所有权限才能创建索引:

  • datastore.indexes.create
  • datastore.indexes.delete
  • datastore.indexes.get
  • datastore.indexes.list
  • datastore.indexes.update

使用 Google Cloud Platform Console

在 Google Cloud Platform Console 中,您可以管理单字段索引例外项和复合索引。

创建复合索引

要从 GCP Console 手动创建新的复合索引,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到数据库页面。

    前往“数据库”

  2. 从数据库列表中选择所需的数据库。

  3. 在导航菜单中,点击索引,然后点击复合标签页。

  4. 点击创建索引

  5. 输入集合 ID。添加要编制索引的字段的名称,并为每个字段添加索引模式。 点击保存索引

您的新索引将显示在复合索引列表中,并且 Firestore 将开始创建索引。创建索引后,您会在索引旁边看到一个绿色的对勾标记。

删除复合索引

要删除复合索引,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到数据库页面。

    前往“数据库”

  2. 从数据库列表中选择所需的数据库。

  3. 在导航菜单中,点击索引,然后点击复合标签页。

  4. 在复合索引列表中,针对要删除的索引点击更多按钮 。点击删除

  5. 点击提醒中的删除索引,确认您要删除此索引。

添加单字段索引例外项

通过单字段索引例外项,您可以替换集合中特定字段的自动索引设置。您可以从控制台添加单字段例外项:

  1. 在 Google Cloud 控制台中,转到数据库页面。

    前往“数据库”

  2. 从数据库列表中选择所需的数据库。

  3. 在导航菜单中,点击索引,然后点击单个字段标签页。

  4. 点击添加豁免项

  5. 输入集合 ID字段路径

  6. 为此字段选择新的编制索引设置。为此字段启用或停用自动更新的升序、降序和包含单字段索引的数组。

  7. 点击保存例外项

添加合集级豁免

若要定义适用于某个集合 ID 下的所有字段的单字段索引例外项,请执行以下操作:

  1. 点击添加豁免项
  2. 为集合组输入集合 ID,并将字段路径设置为 *

    选择要豁免的字段

  3. 选择要为集合组中的所有字段应用的索引编制例外情况。

  4. 点击保存例外项

删除单字段索引例外项

要删除单字段索引例外项,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到数据库页面。

    前往“数据库”

  2. 从数据库列表中选择所需的数据库。

  3. 在导航菜单中,点击索引,然后点击单个字段标签页。

  4. 在单字段索引例外项列表中,针对要删除的例外项单击更多按钮 。点击删除

  5. 通过点击提醒中的删除,确认您想要删除此例外项。

删除单字段例外项后,指定的字段或子字段将使用继承的编制索引设置。文档字段将恢复为数据库的自动索引设置。映射中的子字段会在先继承父字段的所有例外项,再继承自动索引设置。

使用 Firebase CLI

您还可以使用 Firebase CLI 部署索引。如需开始使用,请在您的项目目录中运行 firebase init firestore。在设置过程中,Firebase CLI 会以正确的格式生成一个包含默认索引的 JSON 文件。修改该文件以添加更多索引,并使用 firebase deploy 命令部署该文件。

如需仅部署 Firestore 索引和规则,请添加 --only firestore 标志。

如果您使用 Firebase 控制台对索引进行了修改,请务必同时更新本地索引文件。请参阅 JSON 索引定义参考文档

使用 Terraform

在数据库中创建索引

Firestore 数据库可以同时包含单字段索引和复合索引。您可以修改 Terraform 配置文件,为数据库创建索引。 单字段索引和复合索引使用不同的 Terraform 资源类型(google_firestore_indexgoogle_firestore_field)。

支持 Firestore 原生模式和 Datastore 模式索引。

单字段索引

以下示例 Terraform 配置文件会在 chatrooms 集合的 name 字段中创建单字段索引:

firestore.tf

resource "random_id" "variable"{
  byte_length = 8
}

resource "google_firestore_field" "single-index" {
  project = "project-id"
  database = "database-id"
  collection = "chatrooms_${random_id.variable.hex}"
  field = "name"

  index_config {
    indexes {
        order = "ASCENDING"
        query_scope = "COLLECTION_GROUP"
    }
    indexes {
        array_config = "CONTAINS"
    }
  }

  ttl_config {}
}
  • project-id 替换为项目 ID。项目 ID 不得重复。
  • database-id 替换为您的数据库 ID。

复合索引

以下示例 Terraform 配置文件会为 chatrooms 集合中的 name 字段和 description 字段创建一个复合索引:

firestore.tf

resource "google_firestore_index" "composite-index" {
  project = "project-id"
  database = "database-id"

  collection = "chatrooms"

  fields {
    field_path = "name"
    order      = "ASCENDING"
  }

  fields {
    field_path = "description"
    order      = "DESCENDING"
  }

}
  • project-id 替换为项目 ID。项目 ID 不得重复。
  • database-id 替换为您的数据库 ID。

向量索引

以下示例 Terraform 配置文件会在 chatrooms 集合的 embedding 字段中创建向量索引:

firestore.tf

resource "google_firestore_index" "vector-index" {
  project = "project-id"
  database = "database-id"
  collection = "chatrooms"

  fields {
    field_path = "__name__"
    order = "ASCENDING"
  }

  fields {
    field_path = "embedding"
    vector_config {
      dimension = 128
      flat {}
    }
  }
}
  • project-id 替换为项目 ID。项目 ID 不得重复。
  • database-id 替换为您的数据库 ID。

Datastore 模式索引

您还可以使用 Terraform 创建数据存储空间模式索引。

datastore.tf

resource "google_firestore_index" "datastore-mode-index" {
  project = "project-id"
  database = "database-id"

  collection = "chatrooms"

  fields {
    field_path = "name"
    order      = "ASCENDING"
  }

  fields {
    field_path = "description"
    order      = "DESCENDING"
  }

  query_scope = "COLLECTION_GROUP"
  api_scope   = "DATASTORE_MODE_API"
}
从 google_datastore_index 迁移

google_datastore_index 资源已废弃,在 terraform-provider-google 6.0.0 及更高版本中将不可用。

如果您之前使用的是 google_datastore_index 资源,则可以迁移到 google_firestore_index。您可以通过执行以下操作进行迁移:

  1. 编写等效的 google_firestore_index 资源。
  2. 将现有的 Datastore 模式索引导入到新资源中。
  3. 移除对旧 google_datastore_index 资源的引用。
  4. 从 Terraform 的状态中移除旧的 google_datastore_index 资源。
  5. 运行 terraform apply 以应用任何更改。

以下是更详细的说明:

  1. 根据现有的 google_datastore_index 资源编写替换 google_firestore_index。如需了解必要的更改,请参阅下文
  2. 确定索引的 Firestore 资源路径:

    export INDEX_RESOURCE_PATH=$(echo '"projects/${google_datastore_index.datastore-index-resource-name.project}/databases/(default)/collectionGroups/${google_datastore_index.datastore-index-resource-name.kind}/indexes/${google_datastore_index.datastore-index-resource-name.index_id}"' | terraform console | tr -d '"')
    

    datastore-index-resource-name 替换为现有资源的 Terraform 名称。

  3. 将现有的 Datastore 模式索引导入到您在上文中创建的 google_firestore_index 资源:

    terraform import google_firestore_index.firestore-index-resource-name $INDEX_RESOURCE_PATH
    

    firestore-index-resource-name 替换为现有资源的 Terraform 名称。

    如需详细了解如何导入 Firestore 索引资源,请参阅 google_firestore_index 参考文档

  4. 从 Terraform 配置文件中删除现有的 google_datastore_index 资源。
  5. 从 Terraform 状态中移除现有的 google_datastore_index 资源:

    terraform state rm google_datastore_index.datastore-index-resource-name
    

    如需详细了解如何移除资源,请参阅 Terraform 页面上的移除资源部分。

  6. 运行 terraform plan。验证输出,确认您既没有创建任何资源,也没有销毁任何资源。

    检查输出,确保导入操作已成功完成。如果输出显示有字段发生更改,请确保这些更改符合您的预期。如果输出包含类似于以下内容的行:

    google_firestore_index.firestore-index-resource-name must be replaced
    

    然后检查您的 Terraform 配置文件,看看是否有任何错误。

  7. 对 Terraform 方案输出满意后,请运行以下命令:

    terraform apply
    

  8. 翻译您的索引

    如需将 google_datastore_index 资源转换为等效的 google_firestore_index 资源,请复制该资源并进行以下更改:

    • google_datastore_index 替换为 google_firestore_index
    • 将实参名称 kind 替换为 collection,但保持实参值不变。
    • 将参数名称 ancestor 替换为 query_scope。将参数值 ALL_ANCESTORS 替换为 COLLECTION_RECURSIVE,将任何其他值替换为 COLLECTION_GROUP。如果没有 ancestor 实参,请添加值为 COLLECTION_GROUPquery_scope 实参。
    • 添加值为 DATASTORE_MODE_API 的实参 api_scope
    • 对于 properties 的每个实例,请将其替换为相应的 fields 实例。将 name 的每个实例替换为 field_path,并将 direction 的每个实例替换为 order

    例如,请考虑以下 google_datastore_index 资源:

    datastore.tf

    resource "google_datastore_index" "legacy" {
      kind = "foo"
    
      properties {
        name = "property_a"
        direction = "ASCENDING"
      }
    
      properties {
        name = "property_b"
        direction = "ASCENDING"
      }
    }
    

    等效的 google_firestore_index 资源为:

    resource "google_firestore_index" "new" {
      // note: defaults to the provider project
      project = project
    
      // note: defaults to the (default) database
      database = "(default)"
    
      collection = "foo"
    
      api_scope = "DATASTORE_MODE_API"
    
      // since there was no "ancestor" property set above, use COLLECTION_GROUP here
      query_scope = "COLLECTION_GROUP"
    
      fields {
        field_path = "property_a"
        order  = "ASCENDING"
      }
    
      fields {
        field_path = "property_b"
        order = "ASCENDING"
      }
    }
    

    索引构建时间

    要构建索引,Firestore 必须设置索引,然后使用现有数据回填索引。索引构建时间是设置时间和回填时间的总和:

    • 设置索引需要几分钟时间。索引的最短构建时间为几分钟,即使空数据库也是如此。

    • 回填时间取决于属于新索引的现有数据的数量。与索引定义匹配的字段值越多,回填索引所需的时间就越长。

    索引构建是长时间运行的操作。

    在您启动索引构建后,Firestore 会为操作分配唯一的名称。操作名称的前缀为 projects/[PROJECT_ID]/databases/(default)/operations/,例如:

    projects/project-id/databases/(default)/operations/ASA1MTAwNDQxNAgadGx1YWZlZAcSeWx0aGdpbi1zYm9qLW5pbWRhEgopEg
    

    不过,您可以在指定 describe 命令的操作名称时省略前缀。

    列出所有长时间运行的操作

    如需列出长时间运行的操作,请使用 gcloud firestore operations list 命令。此命令会列出正在进行和最近完成的操作。最近几天内完成的操作都会列出:

    gcloud firestore operations list
    

    查看操作状态

    您可以列出单个长时间运行的操作的详细信息,而不是列出所有长时间运行的操作:

    gcloud firestore operations describe operation-name

    估计完成时间

    操作运行时,查看 state 字段的值可了解操作的总体状态。

    用于获取长时间运行的操作的状态的请求也会返回指标 workEstimatedworkCompleted。这些返回的指标包含文档数量。workEstimated 表示操作将处理的预估文档总数。workCompleted 表示目前已处理的文档数。操作完成后,workCompleted 会反映实际处理的文档总数,可能与 workEstimated 的值不同。

    workCompleted 除以 workEstimated 可得出粗略的进度估算值。该估计可能不准确,因为它取决于统计信息收集是否存在延迟。

    例如,以下是一个索引构建的进度状态:

    {
      "operations": [
        {
          "name": "projects/project-id/operations/AyAyMDBiM2U5NTgwZDAtZGIyYi0zYjc0LTIzYWEtZjg1ZGdWFmZWQHEjF0c2Flc3UtcmV4ZWRuaS1uaW1kYRUKSBI",
          "metadata": {
            "@type": "type.googleapis.com/google.firestore.admin.v1.IndexOperationMetadata",
            "common": {
              "operationType": "CREATE_INDEX",
              "startTime": "2020-06-23T16:52:25.697539Z",
              "state": "PROCESSING"
            },
            "progressDocuments": {
              "workCompleted": "219327",
              "workEstimated": "2198182"
            }
           },
        },
        ...
    

    操作完成后,操作说明将包含 "done": true。查看 state 字段的值,了解操作的结果。如果没有在响应中设置 done 字段,则其值为 false。对于进行中的操作,不要依赖 done 值是否存在。

    索引构建错误

    在管理复合索引和单字段索引豁免时,您可能会遇到索引构建错误。如果 Firestore 为之编制索引的数据出现问题,索引操作可能会失败。通常这表示您达到了索引限制。例如,操作可能已达到每个文档的索引条目数量上限。

    如果索引创建失败,您会在控制台中看到错误消息。确认您没有达到任何索引限制后,请重新尝试您的索引操作。