Overview
在本指南中,您可以了解如何使用 MongoDB Kotlin 驱动程序创建和管理索引。
索引支持在 MongoDB 中高效执行查询。 如果没有索引,MongoDB 必须扫描collection中的每个文档(collection扫描),才能找到与每个查询匹配的文档。这些collection扫描很慢,可能会对应用程序的性能产生负面影响。如果查询存在适当的索引,MongoDB 就可以使用该索引来限制查询必须检查的文档。
索引还具有以下优点:
要学习;了解更多信息,请参阅服务器手册中的索引。
提示
更新操作在查找要更新的文档时使用索引,删除操作在查找要删除的文档时使用索引。 聚合管道中的某些阶段也使用索引来提高性能。
查询覆盖和性能
对 MongoDB 执行查询时,您的命令可以包含各种元素:
用于指定您要查找的字段和值的查询条件
影响查询执行的选项,例如读关注(read concern)
用于指定 MongoDB 返回的字段的投影条件(可选)
排序标准,用于指定从 MongoDB 返回文档的顺序(可选)
当查询、投影和排序中指定的所有字段均位于同一索引时,MongoDB 会直接从此索引返回结果,而这也被称为覆盖查询。
重要
排序顺序
排序条件必须与索引的顺序一致或相反。
举例来说,以下索引指定字段 name 按升序 (AZ) 排序而 {2} 按降序 (9-0) 排序:
name_1_age_-1
当您通过以下任一方式对数据进行排序时,MongoDB 都会使用此索引:
name升序,age降序name降序,age升序
指定 name 和 age 升序或 name 和 age 降序排序需要进行内存中排序。
有关如何确保索引涵盖查询条件和投影的更多信息,请参阅服务器手册中有关查询覆盖的文章。
操作注意事项
以下指南描述了如何优化应用程序使用索引的方式:
若要提高查询性能,请对应用程序查询中经常出现的字段以及其他操作返回的排序结果中经常出现的字段构建索引。
追踪索引内存和磁盘使用情况以进行容量规划,因为您添加的每个索引在活动时都会消耗磁盘空间和内存。
避免添加不常用的索引。请注意,当写入操作更新索引的字段时,MongoDB 会更新相关索引。
由于 MongoDB 支持动态模式,因此应用程序可以查询事先无法知道名称或具有任意名称的字段。MongoDB 4.2 引入了通配符索引,以帮助支持这些查询。通配符索引并不是为了取代基于工作负载的索引规划而设计的。
有关设计数据模型和选择适合您应用程序的索引的更多信息,请参阅 MongoDB 服务器文档中的索引策略和数据建模和索引部分。
索引类型
MongoDB 支持多种不同的索引类型来支持数据查询。 以下部分描述了最常见的索引类型,并提供了创建每种索引类型的示例代码。 有关索引类型的完整列表,请参阅 MongoDB Server手册中的 索引 。
提示
Kotlin 驱动程序提供索引类来创建和管理索引。此类包含静态工厂方法,用于为不同的 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 Server 手册中有关单字段索引的部分。
复合索引
复合索引包含对集合文档中多个字段的引用,从而提高查询和排序性能。
提示
在此阅读有关复合索引、索引前缀和排序顺序的更多信息。
以下示例在 type 和 rated 字段上创建复合索引:
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 服务器手册中有关“复合索引”的部分。
多键索引(数组字段索引)
多键索引可提高使用包含数组值的索引指定字段的查询性能。您可以使用与单字段或复合索引相同的语法定义多键索引。
以下示例在 rated、genres(字符串数组)和 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) }
多键索引在查询覆盖、索引绑定计算和排序行为方面的行为与其他索引不同。 要了解有关多键索引的更多信息,包括对其行为和限制的讨论,请参阅 手册中的多键 索引MongoDB Server 。
MongoDB 搜索 和 MongoDB Vector 搜索索引
您可以使用Kotlin驱动程序以编程方式管理MongoDB Search 和MongoDB Vector Search 索引。
MongoDB Search 功能使您能够对托管在 MongoDB 上的集合执行全文搜索。要学习有关 MongoDB 搜索的更多信息,请参阅MongoDB 搜索索引文档。
MongoDB Vector Search 使您能够对MongoDB中存储的向量嵌入执行语义搜索。要学习有关MongoDB Vector Search 的更多信息,请参阅MongoDB Vector Search指南。
您可以在集合上调用以下方法来管理MongoDB Search 和MongoDB Vector Search 索引:
createSearchIndex()(仅对MongoDB 搜索索引有效)createSearchIndexes()listSearchIndexes()updateSearchIndex()dropSearchIndex()
注意
MongoDB Search 索引管理方法异步运行。驱动程序方法可以在确认成功运行之前返回。要确定索引的当前状态,请调用 listSearchIndexes() 方法。
以下各节将提供代码示例,演示如何使用上述每种方法。
创建搜索索引
您可以使用 createSearchIndex() 方法创建单个MongoDB Search索引。您不能使用此方法创建MongoDB Vector Search索引。
You can use the createSearchIndexes() method to create multiple MongoDB Search or MongoDB Vector Search indexes. You must create a SearchIndexModel instance for each index, then pass a list of SearchIndexModel instances to the createSearchIndexes() method.
以下代码示例展示了如何创建MongoDB搜索索引:
val searchIdx = Document( "mappings", Document("dynamic", true) ) val resultCreateIndex = moviesCollection.createSearchIndex("myIndex", searchIdx)
要创建多个 Search 或 Vector Search 索引,您必须为每个索引创建一个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() 方法返回集合上的MongoDB Search 索引列表。
以下代码示例演示如何打印collection上的搜索索引列表:
val searchIndexesList = moviesCollection.listSearchIndexes().toList()
更新搜索索引
您可以使用 updateSearchIndex() 方法更新 MongoDB 搜索索引。
以下代码展示如何更新搜索索引:
moviesCollection.updateSearchIndex( "myIndex", Document("analyzer", "lucene.simple").append( "mappings", Document("dynamic", false) .append( "fields", Document( "title", Document("type", "string") ) ) ) )
删除搜索索引
您可以使用 dropSearchIndex() 方法删除 MongoDB 搜索索引。
以下代码展示了如何从集合中删除搜索索引:
moviesCollection.dropSearchIndex("myIndex");
Text Indexes
文本索引支持对字符串内容进行文本查询。这些索引可以包括值为字符串或字符串元素大量的任何字段。 MongoDB支持各种语言的文本查询。创建索引时,您可以指定默认语言作为选项。
提示
MongoDB提供改进的全文搜索解决方案MongoDB Search。要学习;了解有关MongoDB搜索索引及其使用方法的更多信息,请参阅本指南的MongoDB搜索和MongoDB Vector 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) }
多个字段
一个集合只能包含一个文本索引。如果要为多个文本字段创建文本索引,则必须创建复合索引。对复合索引中的所有文本字段运行文本查询。
以下代码段为 title 和 genre 字段创建复合文本索引:
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 支持使用2 dsphere 索引查询地理空间坐标数据。 通过2dsphere索引,您可以查询地理空间数据的包含、相交和邻近范围。 有关查询地理空间数据的更多信息,请参阅 MongoDB Server手册中的 地理空间查询 。
要创建2dsphere索引,您必须指定仅包含GeoJSON 对象的字段。 要了解有关此类型的更多信息,请参阅GeoJSON MongoDB Server手册中的 对象 。
以下样本文档中的 location.geo 字段来自 sample_mflix 数据库中的 theaters 集合,且为用于描述影院坐标的 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还支持使用 2d 索引来计算欧几里得平面上的距离。要学习;了解更多信息,请参阅服务器手册中的地理空间查询。
Unique Indexes
唯一索引可确保索引字段不存储重复值。默认情况下,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 Server 手册部分:
删除索引
您可以删除针对 _id 字段的所有未使用索引,但默认唯一索引除外。
以下几节将介绍了删除索引的方法:
使用索引规范文档
使用索引名称字段
使用通配符删除所有索引
使用索引规范文档删除索引
将索引规范文档传递给 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 文档: