索引
在此页面上
Overview
在本指南中,您可以了解如何使用 MongoDB Kotlin 驱动程序创建和管理索引。
索引支持在 MongoDB 中高效执行查询。 如果没有索引,MongoDB 必须扫描collection中的每个文档(collection扫描),才能找到与每个查询匹配的文档。这些collection扫描很慢,可能会对应用程序的性能产生负面影响。如果查询存在适当的索引,MongoDB 就可以使用该索引来限制查询必须检查的文档。
索引还具有以下优点:
要学习;了解更多信息,请参阅服务器手册中的索引。
提示
更新操作在查找要更新的文档时使用索引,删除操作在查找要删除的文档时使用索引。 聚合管道中的某些阶段也使用索引来提高性能。
查询覆盖和性能
对 MongoDB 执行查询时,您的命令可以包含各种元素:
用于指定您要查找的字段和值的查询条件
影响查询执行的选项,例如读关注(read concern)
用于指定 MongoDB 返回的字段的投影条件(可选)
排序标准,用于指定从 MongoDB 返回文档的顺序(可选)
当查询、投影和排序中指定的所有字段均位于同一索引时,MongoDB 会直接从此索引返回结果,而这也被称为覆盖查询。
重要
排序顺序
排序条件必须与索引的顺序一致或相反。
考虑字段上的索引, name
按升序 (AZ) 排列,而age
按降序 (9-0) 排列:
name_1_age_-1
当您通过以下任一方式对数据进行排序时,MongoDB 都会使用此索引:
name
升序,age
降序name
降序,age
升序
指定 name
和 age 升序或 name 和 age
降序排序需要进行内存中排序。
有关如何确保索引涵盖查询条件和投影的更多信息,请参阅MongoDB Server手册中有关查询覆盖的文章。
操作注意事项
以下指南描述了如何优化应用程序使用索引的方式:
若要提高查询性能,请对应用程序查询中经常出现的字段以及其他操作返回的排序结果中经常出现的字段构建索引。
追踪索引内存和磁盘使用情况以进行容量规划,因为您添加的每个索引在活动时都会消耗磁盘空间和内存。
避免添加不常用的索引。请注意,当写入操作更新索引的字段时,MongoDB 会更新相关索引。
由于 MongoDB 支持动态模式,因此应用程序可以查询事先无法知道名称或具有任意名称的字段。MongoDB 4.2 引入了通配符索引,以帮助支持这些查询。通配符索引并不是为了取代基于工作负载的索引规划而设计的。
有关设计数据模型和选择适合应用程序的索引的更多信息,请参阅有关“索引策略”和“数据建模和索引”的 MongoDB 服务器文档。
索引类型
MongoDB 支持多种不同的索引类型来支持数据查询。 以下部分描述了最常见的索引类型,并提供了创建每种索引类型的示例代码。 有关索引类型的完整列表,请参阅 MongoDB Server手册中的 索引 。
提示
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 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 。
Atlas Search和 Vector Search 索引
您可以使用Kotlin驾驶员以编程方式管理Atlas Search和Atlas Vector Search索引。
Atlas Search 功能使您能够对 MongoDB Atlas 上托管的集合执行全文搜索。如需了解有关 MongoDB Atlas Search 的更多信息,请参阅“Atlas Search 索引”文档。
Atlas Vector Search使您能够对存储在MongoDB Atlas中的向量嵌入执行语义搜索。要学习;了解有关Atlas Vector Search 的更多信息,请参阅 Atlas Vector Search指南。
您可以在集合上调用以下方法来管理Atlas Search和 Vector Search 索引:
createSearchIndex()
(仅对搜索索引有效)createSearchIndexes()
listSearchIndexes()
updateSearchIndex()
dropSearchIndex()
注意
Atlas Search 索引管理方法异步运行。驱动程序方法可以在确认成功运行之前返回。 要确定索引的当前状态,请调用listSearchIndexes()
方法。
以下各节将提供代码示例,演示如何使用上述每种方法。
创建搜索索引
您可以使用 createSearchIndex() 方法创建单个Atlas Search索引。您不能使用此方法创建 Vector Search索引。
您可以使用 createSearchIndexes() 方法创建多个Atlas Search或 Vector Search 索引。您必须为每个索引创建一个 SearchIndexModel实例,然后将SearchIndexModel
实例列表传递给createSearchIndexes()
方法。
以下代码示例展示了如何创建 Atlas Search 索引:
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() 方法返回集合上的 Atlas Search 索引列表。
以下代码示例演示如何打印collection上的搜索索引列表:
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");
Text Indexes
文本索引支持对字符串内容进行文本搜索查询。这些索引可以包括任何值为字符串或字符串元素数组的字段。MongoDB 还支持各种语言的文本搜索。在创建索引时,可以指定默认语言作为选项。
提示
MongoDB提供改进的全文搜索解决方案Atlas Search 。 要学习;了解有关Atlas Search索引及其使用方法的更多信息,请参阅本指南的Atlas Search和 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
索引,用于计算欧几里得平面上的距离,以及使用legacy coordinate pairs MongoDB2.2及更早版本中使用的“ ”语法。要了解更多信息,请参阅 MongoDB Server手册中的 地理空间查询 。
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 文档: