文档菜单
文档首页
/ / /
Kotlin 协程
/

索引

本页面内容

  • 概述
  • 查询覆盖率和性能
  • 运行时注意事项
  • 索引类型
  • 单字段和组合索引
  • 多键索引(数组字段的索引)
  • Atlas 搜索和向量搜索索引
  • 文本索引
  • 地理空间索引
  • 唯一索引
  • 聚集索引
  • 删除索引
  • 使用索引规范文档删除索引
  • 使用名称字段删除索引
  • 使用通配符字符删除索引

在此指南中,您可以学习如何使用 MongoDB Kotlin 驱动程序创建和管理 索引

索引支持MongoDB中查询的高效执行。没有索引,MongoDB必须扫描集合中的每个文档(称为集合扫描)以找到每个查询匹配的文档。这些集合扫描很慢,可能会影响您应用程序的性能。如果查询存在适当的索引,MongoDB可以使用该索引来限制查询必须检查的文档。

索引还具有以下优点

  • 索引允许高效排序。

  • 索引启用以下特殊功能地理空间查询.

  • 索引允许创建约束以确保字段值是唯一的。

有关更多信息,请参阅服务器手册中的索引

提示

更新操作在查找要更新的文档时使用索引,删除操作在查找要删除的文档时使用索引。聚合管道中的某些阶段也使用索引来提高性能。

当您对MongoDB执行查询时,您的命令可以包含各种元素

  • 查询条件,指定您要查找的字段和值

  • 选项,影响查询的执行,如读取关注点

  • 投影条件,指定MongoDB返回的字段(可选)

  • 排序条件,指定从MongoDB返回的文档的顺序(可选)

当查询、投影和排序中指定的所有字段都在同一个索引中时,MongoDB可以直接从索引返回结果,也称为覆盖查询

重要

排序顺序

排序条件必须与索引的顺序匹配或反转。

考虑一个按字段name升序(A-Z)和age降序(9-0)排序的索引

name_1_age_-1

MongoDB会在您按以下方式排序数据时使用此索引

  • name升序,age降序

  • name降序,age升序

指定nameage升序或nameage降序则需要内存排序。

有关如何确保您的索引覆盖查询条件和投影的更多信息,请参阅服务器手册中的查询覆盖文章。

以下指南描述了如何优化应用程序使用索引的方式

  • 为了提高查询性能,在经常出现在应用程序查询和返回排序结果的字段上建立索引。

  • 跟踪索引内存和磁盘使用情况,以便进行容量规划,因为每个索引在活动状态下都会消耗磁盘空间和内存。

  • 避免添加不常使用的索引。请注意,当写操作更新索引字段时,MongoDB会更新相关的索引。

由于MongoDB支持动态模式,应用程序可以查询那些名称无法提前知道或任意的字段。MongoDB 4.2引入了通配索引以帮助支持这些查询。通配索引并不是为了替换基于工作负载的索引规划。

有关设计数据模型和选择适合应用程序的索引的更多信息,请参阅MongoDB服务器文档中的索引策略数据建模和索引

MongoDB支持多种不同的索引类型,以支持查询数据。以下部分描述了最常用的索引类型,并为创建每种索引类型提供了示例代码。有关索引类型的完整列表,请参阅服务器手册中的索引

提示

Kotlin 驱动提供了用于创建和管理索引的 Indexes 类。此类包含静态工厂方法,用于为不同的 MongoDB 索引键类型创建索引规范文档。

以下示例使用 createIndex() 方法创建各种索引,以及以下数据类来模拟 MongoDB 中的数据

// Data class for the movies collection
data class Movie(
val title: String,
val year: Int,
val cast: List<String>,
val genres: List<String>,
val type: String,
val rated: String,
val plot: String,
val fullplot: String,
)
// Data class for the theaters collection
data class Theater(
val theaterId: Int,
val location: Location
) {
data class Location(
val address: Address,
val geo: Point
) {
data class Address(
val street1: String,
val city: String,
val state: String,
val zipcode: String
)
}
}

单字段索引是指在集合文档中的单个字段上引用的索引。它们可以提升单字段查询和排序性能,并支持TTL 索引,该索引可以在一定时间后或特定时钟时间自动从集合中删除文档。

注意

`_id` 索引是单字段索引的一个示例。当创建新集合时,此索引会自动在 `_id` 字段上创建。

以下示例在 title 字段上创建升序索引

val resultCreateIndex = moviesCollection.createIndex(Indexes.ascending(Movie::title.name))
println("Index created: $resultCreateIndex")
Index created: title_1

以下是一个查询示例,该查询由前面代码片段中创建的索引覆盖

val filter = Filters.eq(Movie::title.name, "The Dark Knight")
val sort = Sorts.ascending(Movie::title.name)
val projection = Projections.fields(
Projections.include(Movie::title.name),
Projections.excludeId()
)
data class Results(val title: String)
val resultsFlow = moviesCollection.find<Results>(filter).sort(sort).projection(projection)
resultsFlow.collect { println(it) }

有关更多信息,请参阅 MongoDB 服务器手册中关于单字段索引的部分。

组合索引包含集合文档中多个字段的引用,从而提升查询和排序性能。

提示

有关组合索引、索引前缀和排序顺序的更多信息,请参阅此处

以下示例在 typerated 字段上创建组合索引

val resultCreateIndex = moviesCollection.createIndex(Indexes.ascending(Movie::type.name, Movie::rated.name))
println("Index created: $resultCreateIndex")
Index created: type_1_rated_1

以下是一个查询示例,该查询由前面代码片段中创建的索引覆盖

val filter = Filters.and(
Filters.eq(Movie::type.name, "movie"),
Filters.eq(Movie::rated.name, "G")
)
val sort = Sorts.ascending(Movie::type.name, Movie::rated.name)
val projection = Projections.fields(
Projections.include(Movie::type.name, Movie::rated.name),
Projections.excludeId()
)
val resultsFlow = moviesCollection.find(filter).sort(sort).projection(projection)
resultsFlow.collect { println(it) }

有关更多信息,请参阅 MongoDB 服务器手册中关于组合索引的部分。

多键索引是一种提高指定包含数组值的索引字段的查询性能的索引。您可以使用与单个字段或组合索引相同的语法定义多键索引。

以下示例在ratedgenres(字符串数组)和title字段上创建了一个组合多键索引。

val resultCreateIndex =
moviesCollection.createIndex(Indexes.ascending(Movie::rated.name, Movie::genres.name, Movie::title.name))
println("Index created: $resultCreateIndex")
Index created: rated_1_genres_1_title_1

以下是一个查询示例,该查询由前面代码片段中创建的索引覆盖

val filter = Filters.and(
Filters.eq(Movie::genres.name, "Animation"),
Filters.eq(Movie::rated.name, "G")
)
val sort = Sorts.ascending(Movie::title.name)
val projection = Projections.fields(
Projections.include(Movie::title.name, Movie::rated.name),
Projections.excludeId()
)
val resultsFlow = moviesCollection.find(filter).sort(sort).projection(projection)
resultsFlow.collect { println(it) }

多键索引在查询覆盖、索引边界计算和排序行为方面与其他索引的行为不同。有关多键索引的更多信息,包括对其行为和限制的讨论,请参阅服务器手册中的多键索引

您可以使用 Kotlin 驱动程序以编程方式管理您的 Atlas 搜索和 Atlas 向量搜索索引。

Atlas 搜索功能使您能够在 MongoDB Atlas 上托管的数据集中执行全文搜索。有关 MongoDB Atlas 搜索的更多信息,请参阅Atlas 搜索索引文档。

Atlas 向量搜索使您能够对存储在 MongoDB Atlas 中的向量嵌入执行语义搜索。有关 Atlas 向量搜索的更多信息,请参阅聚合构建器指南中的 Atlas 向量搜索部分。

您可以在集合上调用以下方法来管理您的 Atlas 搜索和向量搜索索引

  • createSearchIndex() (仅适用于搜索索引)

  • createSearchIndexes()

  • listSearchIndexes()

  • updateSearchIndex()

  • dropSearchIndex()

注意

Atlas Search索引管理方法异步运行。驱动方法可以在确认它们运行成功之前返回。要确定索引的当前状态,请调用listSearchIndexes()方法。

以下各节提供了代码示例,演示了如何使用上述每种方法。

您可以使用createSearchIndex()方法创建单个Atlas Search索引。您不能使用此方法创建向量搜索索引。

您可以使用 createSearchIndexes() 方法来创建多个 Atlas Search 或 Vector Search 索引。您必须为每个索引创建一个 SearchIndexModel 实例,然后将 SearchIndexModel 实例的列表传递给 createSearchIndexes() 方法。

以下代码示例展示了如何创建一个 Atlas Search 索引

val searchIdx = Document(
"mappings",
Document("dynamic", true)
)
val resultCreateIndex = moviesCollection.createSearchIndex("myIndex", searchIdx)

要创建多个搜索或向量搜索索引,您必须为每个索引创建一个 SearchIndexModel 实例。

以下代码示例展示了如何在一次调用中创建搜索和向量搜索索引。

val searchIdxMdl = SearchIndexModel(
"searchIdx",
Document("analyzer", "lucene.standard").append(
"mappings", Document("dynamic", true)
),
SearchIndexType.search()
)
val vectorSearchIdxMdl = SearchIndexModel(
"vsIdx",
Document(
"fields",
listOf(
Document("type", "vector")
.append("path", "embeddings")
.append("numDimensions", 1536)
.append("similarity", "dotProduct")
)
),
SearchIndexType.vectorSearch()
)
val resultCreateIndexes = moviesCollection.createSearchIndexes(
listOf(searchIdxMdl, vectorSearchIdxMdl)
)

您可以使用listSearchIndexes()方法来返回集合上的Atlas搜索索引列表。

以下代码示例展示了如何打印集合上的搜索索引列表。

val searchIndexesList = moviesCollection.listSearchIndexes().toList()

您可以使用updateSearchIndex()方法来更新Atlas Search索引。

以下代码展示了如何更新搜索索引

moviesCollection.updateSearchIndex(
"myIndex",
Document("analyzer", "lucene.simple").append(
"mappings",
Document("dynamic", false)
.append(
"fields",
Document(
"title",
Document("type", "string")
)
)
)
)

您可以使用dropSearchIndex()方法来移除Atlas Search索引。

以下代码展示了如何从集合中删除搜索索引

moviesCollection.dropSearchIndex("myIndex");

文本索引 支持对字符串内容进行文本搜索查询。这些索引可以包括任何值是字符串或字符串元素数组的字段。MongoDB 支持多种语言的文本搜索。您可以在创建索引时指定默认语言作为选项。

提示

MongoDB 提供了一个改进的全文搜索解决方案,Atlas Search。有关 Atlas Search 索引及其使用方法的更多信息,请参阅本指南的Atlas Search 和矢量搜索索引部分。

以下示例在 plot 字段上创建文本索引

try {
val resultCreateIndex = moviesCollection.createIndex(Indexes.text(Movie::plot.name))
println("Index created: $resultCreateIndex")
} catch (e: MongoCommandException) {
if (e.errorCodeName == "IndexOptionsConflict") {
println("there's an existing text index with different options")
}
}
Index created: plot_text

以下是一个查询示例,该查询由前面代码片段中创建的索引覆盖。请注意,省略了 sort,因为文本索引不包含排序顺序。

val filter = Filters.text("Batman")
val projection = Projections.fields(
Projections.include(Movie::fullplot.name),
Projections.excludeId()
)
data class Results(val fullplot: String)
val resultsFlow = moviesCollection.find<Results>(filter).projection(projection)
resultsFlow.collect { println(it) }

集合只能包含一个文本索引。如果您想为多个文本字段创建文本索引,必须创建组合索引。文本搜索在组合索引内的所有文本字段上运行。

以下代码片段创建了一个针对 titlegenre 字段的组合文本索引

try {
val resultCreateIndex = moviesCollection.createIndex(
Indexes.compoundIndex(
Indexes.text(Movie::title.name), Indexes.text(Movie::genres.name)
)
)
println("Index created: $resultCreateIndex")
} catch (e: MongoCommandException) {
if (e.errorCodeName == "IndexOptionsConflict") {
println("there's an existing text index with different options")
}
}
Index created: title_text_genre_text

有关更多信息,请参阅以下服务器手册条目

MongoDB支持使用2dsphere索引查询地理空间坐标数据。使用2dsphere索引,您可以查询包含、相交和邻近的地理空间数据。有关查询地理空间数据的更多信息,请参阅服务器手册中的地理空间查询

要创建2dsphere索引,您必须指定一个仅包含GeoJSON对象的字段。有关此类型的更多信息,请参阅服务器手册中的GeoJSON对象

以下示例文档中sample_mflix数据库中theaters集合中的location.geo字段是一个描述剧院坐标的GeoJSON点对象。

{
"_id" : ObjectId("59a47286cfa9a3a73e51e75c"),
"theaterId" : 104,
"location" : {
"address" : {
"street1" : "5000 W 147th St",
"city" : "Hawthorne",
"state" : "CA",
"zipcode" : "90250"
},
"geo" : {
"type" : "Point",
"coordinates" : [
-118.36559,
33.897167
]
}
}
}

以下示例在location.geo字段上创建一个2dsphere索引。

val resultCreateIndex = theatersCollection.createIndex(
Indexes.geo2dsphere("${Theater::location.name}.${Theater.Location::geo.name}")
)
println("Index created: $resultCreateIndex")
Index created: location.geo_2dsphere

重要

尝试在已由地理空间索引覆盖的字段上创建地理空间索引会导致错误。

以下是一个示例地理空间查询,该查询受先前代码片段中创建的索引的保护。

// MongoDB Headquarters in New York, NY.
val refPoint = Point(Position(-73.98456, 40.7612))
val filter = Filters.near(
"${Theater::location.name}.${Theater.Location::geo.name}",
refPoint, 1000.0, 0.0
)
val resultsFlow = theatersCollection.find(filter)
resultsFlow.collect { println(it) }

MongoDB还支持用于在欧几里得平面上计算距离和用于处理MongoDB 2.2及更早版本中使用的“旧坐标对”语法的2d索引。有关更多信息,请参阅服务器手册中的地理空间查询

唯一索引确保索引字段不存储重复值。默认情况下,MongoDB在创建集合时会为_id字段创建一个唯一索引。要创建唯一索引,请指定要防止重复的字段或字段组合,并将_unique选项设置为true。

以下示例在theaterId字段上创建了一个唯一、降序索引

try {
val indexOptions = IndexOptions().unique(true)
val resultCreateIndex = theatersCollection.createIndex(
Indexes.descending(Theater::theaterId.name), indexOptions
)
println("Index created: $resultCreateIndex")
} catch (e: DuplicateKeyException) {
println("duplicate field values encountered, couldn't create index: \t${e.message}")
}
Index created: theaterId_-1

重要

如果您执行了一个存储违反唯一索引的重复值的写操作,驱动程序将引发DuplicateKeyException异常,MongoDB将抛出一个类似以下错误

E11000 duplicate key error index

有关更多信息,请参阅MongoDB服务器手册中的唯一索引页面

聚集索引指示集合按键值存储文档。要创建聚集索引,请指定聚集索引选项,以_id字段作为键,并在创建集合时将唯一字段设置为true。

以下示例在vendors集合的_id字段上创建了一个聚集索引

val clusteredIndexOptions = ClusteredIndexOptions(Document("_id", 1), true)
val createCollectionOptions = CreateCollectionOptions().clusteredIndexOptions(clusteredIndexOptions)
database.createCollection("vendors", createCollectionOptions)

有关更多信息,请参阅MongoDB服务器手册的相关章节

您可以删除除_id字段上的默认唯一索引之外的所有未使用索引。

以下部分展示了删除索引的方法

  • 使用索引规范文档

  • 使用索引名称字段

  • 使用通配符字符删除所有索引

将一个index specification document传递给dropIndex()方法,以从集合中删除索引。索引规范文档是一个指定特定字段上索引类型的Bson实例。

以下代码示例从集合中删除了title字段的升序索引

moviesCollection.dropIndex(Indexes.ascending(Movie::title.name));

重要

如果您想删除文本索引,必须使用索引名称。有关详细信息,请参阅使用名称字段删除索引部分。

将索引的name字段传递给dropIndex()方法,以从集合中删除索引。

如果您必须找到索引的名称,请使用listIndexes()方法查看索引中name字段的值。

以下代码示例检索并打印集合中所有的索引

val indexes = moviesCollection.listIndexes()
indexes.collect { println(it.toJson()) }

如果您在包含文本索引的集合上调用listIndex(),输出可能如下所示

{ "v": 2, "key": {"_id": 1}, "name": "_id_" }
{ "v": 2, "key": {"_fts": "text", "_ftsx": 1}, "name": "title_text", "weights": {"title": 1},
"default_language": "english", "language_override": "language", "textIndexVersion": 3 }

此输出告诉我们现有索引的名称是 "_id" 和 "title_text"。

以下代码示例从集合中删除了 "title_text" 索引

moviesCollection.dropIndex("title_text")

注意

您不能从复合文本索引中删除单个字段。您必须删除整个索引并创建一个新的索引来更新索引字段。

从 MongoDB 4.2 版本开始,您可以通过在集合上调用 dropIndexes() 方法来删除所有索引

moviesCollection.dropIndexes()

对于 MongoDB 的早期版本,将 "*" 作为参数传递给您的集合上的 dropIndex() 调用

moviesCollection.dropIndex("*")

有关本节中方法的更多信息,请参阅以下 API 文档

返回

聚合表达式