

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 Java 在 Amazon DocumentDB 中进行索引管理
使用 Java 进行索引管理

索引允许从 Amazon DocumentDB 集合中高效检索数据。如果没有索引，DocumentDB 必须扫描集合中的每个文档才能返回满足给定查询的结果。本主题提供有关如何使用 MongoDB Java 驱动程序创建、删除和列出索引的信息。还讨论了如何确定查询中是否使用了特定索引，以及如何向 Amazon DocumentDB 提供提示以使用特定索引。

**Topics**
+ [创建索引](#creating-indexes)
+ [删除索引](#dropping-indes)
+ [确定索引选择并提供索引提示](#w2aac43b9b7c17c13)

Amazon DocumentDB 支持多种类型的索引。有关所有受支持索引的全面概述，请参阅此[博客文章](https://www.amazonaws.cn/blogs/database/how-to-index-on-amazon-documentdb-with-mongodb-compatibility/)。

## 使用 Java 创建索引
创建索引

可通过两种机制使用 MongoDB Java 驱动程序在 Amazon DocumentDB 中创建索引：通过 `runCommand()`，以及通过 `createIndex()` 方法（创建单个索引）或 `createIndexes()` 方法（创建多个索引）。使用 `createIndex()` 和 `createIndexes()` 方法的原因之一是，通过捕获与创建索引相关的特定错误，您可以构建更好的错误处理。相较于 `runCommand()`，更推荐使用这些方法的另一个原因是，MongoDB Java 驱动程序为索引创建和操作提供了一组丰富的支持类。请注意，只有当您使用 `createIndex()` 或 `createIndexes()` 方法时才可以使用这些支持类。有三个支持类：
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html)** – 该类充当实用程序类，提供用于创建各种类型索引的静态工厂方法。该类简化了创建复杂索引定义的过程，通常和其他与索引相关的类一起使用。
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html)** – 这是一个基本类，同时封装了索引键定义及其选项。其代表了一个完整的索引规范，将要索引的内容（键）与索引方式（选项）相结合。该类在同时创建多个索引时尤为有用，因为允许您定义可以传递给 `createIndexes()` 方法的索引规范集合。
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html)** – 这是一个全面的配置类，为自定义索引行为提供了一组丰富的方法。包含唯一索引、稀疏索引、过期时间（TTL）和部分筛选条件表达式的设置。通过方法链接，您可以配置后台索引构建和唯一约束等多个选项。

**创建单个索引**

此示例显示了如何在后台使用 `createIndex(`) 方法创建单个索引。要了解后台和前台索引创建，请参阅 [索引构建类型](managing-indexes.md#index-build-types)。以下代码示例使用 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html) 在后台创建名为“unique\$1restaurantId\$1idx”的唯一索引。然后将此 `IndexOptions` 对象传递给 `createIndex()` 方法。

```
collection.createIndex(
    Indexes.ascending("restaurantId"),
    new IndexOptions()
        .unique(true)
        .name("unique_restaurantId_idx")
        .background(true));
```

**创建多个索引**

此示例使用 `createIndexes()` 方法创建多个索引。首先使用 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html) 对象为每个索引构建选项，然后将 `IndexModel` 对象列表传递给 `createIndexes()` 方法。以下代码示例显示了如何使用 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html) 实用程序类创建复合索引。该类还用于指定要使用升序还是降序排序顺序创建索引。在创建多个索引后，该类会通过调用 `listIndexes()` 方法来验证索引创建情况。

```
// Single Field Index on cuisine
IndexModel singleIndex = new IndexModel(
    Indexes.ascending("cuisine"),
    new IndexOptions().name("cuisine_idx"));

// Compound Index
IndexModel compoundIndex = new IndexModel(
    Indexes.compoundIndex(
        Indexes.ascending("address.state"),
        Indexes.ascending("priceRange")),
    new IndexOptions().name("location_price_idx"));

// Build a list of IndexModel for the indexes
List < IndexModel > indexes = Arrays.asList(
    singleIndex,
    compoundIndex
);

collection.createIndexes(indexes);

// Verify created indexes
collection.listIndexes().forEach(index - > System.out.println("Created index: " + index.toJson()));
```

**创建稀疏索引和部分索引**

此示例显示了通过为每种类型的索引创建 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html)，来创建稀疏索引和部分索引。

```
// Sparse Index Model, this will identify only those documents that have a
// michelin star rating
IndexModel sparseIndex = new IndexModel(
    Indexes.ascending("michelin.star"),
    new IndexOptions()
    .name("michelin_sparse_idx")
    .sparse(true));

// Partial Index Model where the restaurant is active and has a rating of 4 and above
IndexModel partialIndex = new IndexModel(
    Indexes.ascending("rating.average"),
    new IndexOptions()
    .name("high_rated_active_idx")
    .partialFilterExpression(
        Filters.and(
            Filters.eq("isActive", true),
            Filters.gte("rating.average", 4.0))));
```

**创建文本索引**

此示例显示了如何创建文本索引。一个集合上只允许有一个文本索引，但该文本索引可以是覆盖多个字段的复合索引。在文本索引中使用多个字段时，也可以为索引中的每个字段分配权重。Amazon DocumentDB 不支持数组字段上的文本索引，尽管复合文本索引中最多可以使用 30 个字段，但只能为三个字段分配权重。

```
IndexModel textIndex = new IndexModel(
    new Document()
    .append("name", "text")
    .append("description", "text")
    .append("cuisine", "text"),
    new IndexOptions()
    .name("restaurant_text_idx")
    .weights(new Document()
        .append("name", 10) // Restaurant name gets highest weight
        .append("description", 5) // Description get medium weight
        .append("cuisine", 2) // Cuisine type gets low weight
    ));

collection.createIndex(textIndex.getKeys(), textIndex.getOptions());
```

**使用 `runCommand()` 创建索引**

Amazon DocumentDB 支持并行索引创建，以缩短创建索引所需要的时间。并行索引使用多个并发工作程序。用于创建索引的默认工作程序有两个。此篇[博客文章](https://www.amazonaws.cn/blogs/database/unlock-the-power-of-parallel-indexing-in-amazon-documentdb/)对并行索引进行了深入讨论。当前，MongDB Java 驱动程序不支持您在使用 `createIndex()` 或 `createIndexes()` 时指定 worker 选项，因此指定工作程序的唯一方法是通过 `runCommand`。以下代码示例演示了如何使用 `runCommand` 创建可将工作程序增加到四个的索引：

```
Document command = new Document("createIndexes", "Restaurants")
    .append("indexes", Arrays.asList(
        new Document("key", new Document("name", 1))
        .append("name", "restaurant_name_idx")
        .append("workers", 4) // Specify number of workers
    ));

Document commendResult = connectedDB.runCommand(command);
```

## 删除索引
删除索引

MongoDB Java 驱动程序提供了多个可删除索引的方法，可满足不同的场景和您的首选项。您可以按名称、按键规范删除索引，也可以一次性删除所有索引。可以在集合对象上调用 `dropIndex()` 和 `dropIndexes()` 方法来删除索引。按名称删除索引时，应确保使用正确的索引名称，索引名称可能并不总是直观的，特别是对于复合索引或自动生成的索引而言。尝试删除不存在的索引将导致 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html)。不能删除 `default _id` 索引，因为该索引可以确保集合中文档的唯一性。

以下代码示例演示了如何通过提供创建索引的字段名称或通过删除所有索引来删除索引：

```
String indexName = "unique_restaurantId_idx";
Document keys = new Document("cuisine", 1);
// Drop index by name
collection.dropIndex(indexName);
            
// Drop index by keys
collection.dropIndex(keys);
            
// Drop all indexes
collection.dropIndexes();
```

使用多个键删除索引时，请确保存在一个包含所有指定键的复合索引，并且键的顺序正确。上面的索引创建示例代码显示了“cuisine”和 features 的复合键。如果您尝试删除该复合键，但顺序不是创建时使用的顺序，则会出现如下 MongoCommnadException 错误：

```
Document keys = new Document("features", 1)
    .append("cuisine", 1);
try {
    // Drop index by keys
    collection.dropIndex(keys);
    System.out.println("Successfully dropped index with keys: " + keys.toJson());

} catch (MongoCommandException commErr) {
    System.out.println("Error dropping index: " + commErr.getErrorMessage());
    throw new RuntimeException("MongoCommandException was thrown while dropping index", commErr);
}
```

将显示以下错误：

```
Error dropping index: Cannot drop index: index not found.
Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.819 sec <<< FAILURE!
com.amazon.docdb.guide.DocDBGuideTest.testindexGuide()  Time elapsed: 0.817 sec  <<< FAILURE!
org.opentest4j.AssertionFailedError: Unexpected exception thrown: java.lang.RuntimeException: MongoCommandException was thrown while dropping index
```

## 确定索引选择并提供索引提示
确定索引选择并提供索引提示

使用 Amazon DocumentDB 中的解释功能对于您了解查询性能和索引使用情况而言至关重要。执行查询时，您可以附加 `explain()` 方法以获取有关查询计划的详细信息，包括正在使用的索引（如果有）。`explain()` 输出提供了对查询执行阶段、检查的文档数量以及每个阶段所花费时间的洞察。这些信息对于确定特定索引是否得到有效使用或查询是否可以从不同的索引结构中获益而言非常有用。

`explain()` 方法可以与 `find()` 方法链接。`explain()` 方法可以采用可选的 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html) 枚举来确定 `explain()` 返回的详细程度级别。目前，DocumentDB 仅支持 `EXECUTION_STATS` 和 `QUERY_PLANNER` 枚举器。以下代码示例显示了如何获取特定查询的查询计划程序：

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));


Document allPlansExplain = collection.find(query).explain(ExplainVerbosity.QUERY_PLANNER);
System.out.println("All Plans Explain:\n" + allPlansExplain.toJson());
```

针对查询计划程序详细程度级别返回以下 JSON 文档：

```
{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "ProgGuideData.Restaurants",
    "winningPlan": {
      "stage": "IXSCAN",
      "indexName": "cuisine_idx",
      "direction": "forward"
    }
  },
  "serverInfo": {
    "host": "guidecluster3",
    "port": 27017,
    "version": "5.0.0"
  },
  "ok": 1,
  "operationTime": {
    "$timestamp": {
      "t": 1739221668,
      "i": 1
    }
  }
}
```

您可以通过多种方式来控制或强制 Amazon DocumentDB 使用特定索引。`hint()` 和 `hintString()` 方法允许您通过显式指定查询应使用的索引来覆盖查询优化程序的默认索引选择行为。虽然 DocumentDB 的查询优化程序通常能做出合理的索引选择，但在某些场景下，通过 `hint()` 或 `hintString()` 强制使用特定索引可能有益，例如在处理偏斜数据或测试索引性能时。

以下代码示例强制使用复合索引“cuisine\$1features\$1idx”来处理在上述代码中运行的相同查询：

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));

List < Document > queryDocs = new ArrayList < > ();
collection.find(query).hintString("cuisine_features_idx").forEach(doc - > queryDocs.add(doc));
```