Sparse Indexes
稀疏索引仅包含具有索引字段的文档的条目,即使索引字段包含 null 值也是如此。该索引将跳过缺少索引字段的所有文档。索引是“稀疏”的,因为它不包括集合的所有文档。相比之下,非稀疏索引包含集合中的所有文档,为那些不包含索引字段的文档存储 null 值。
重要
MongoDB 提供了创建部分索引的选项。部分索引可提供比稀疏索引更多的功能。部分索引应优先于稀疏索引。
创建稀疏索引
要创建稀疏索引,请使用 db.collection.createIndex()
方法,并将 sparse
选项设置为 true
。
例如,mongosh
中的以下操作在 addresses
集合的 xmpp_id
字段上创建稀疏索引:
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
该索引不会索引不包含 xmpp_id
字段的文档。
注意
请勿将 MongoDB 中的稀疏索引与其他数据库中的块级索引混淆。请将它们视为带有特定过滤器的密集索引。
行为
索引稀疏,结果不完整
如果稀疏索引会导致查询和排序操作的结果集不完整,则除非 hint()
显式指定该索引,否则 MongoDB 不会使用该索引。
例如,除非显式提示,否则查询 { x: { $exists: false } }
不会在 x
字段上使用稀疏索引。有关详细行为的示例,请参阅集合上的稀疏索引无法返回完整结果。
当您对集合中所有文档执行 count()
时(即,采用空查询谓词),您要纳入指定稀疏索引的 hint()
,即便该稀疏索引产生错误计数也要使用。
db.collection.insertOne( { _id: 1, y: 1 } ); db.collection.createIndex( { x: 1 }, { sparse: true } ); db.collection.find().hint( { x: 1 } ).count();
如需获得正确的计数,在对集合中的所有文档进行计数时,请勿在使用 hint()
时指定稀疏索引。
db.collection.find().count(); db.collection.createIndex( { y: 1 } ); db.collection.find().hint( { y: 1 } ).count();
默认情况下稀疏的索引
以下索引类型始终是稀疏的:
稀疏复合索引
复合索引可以包含不同类型的稀疏索引。索引类型的组合决定了复合索引与文档的匹配方式。
本表汇总了包含不同类型稀疏索引的复合索引的行为:
复合索引组件 | 复合索引行为 |
---|---|
Ascending indexes Descending indexes | 仅对至少包含一个键值的文档进行索引。 |
仅当文档包含一个 geospatial 字段的值时,才为文档编制索引。不在升序或降序索引中索引文档。 | |
仅当文档与一个 text 字段匹配时,才为文档编制索引。不在升序或降序索引中索引文档。 |
稀疏和唯一属性
既稀疏又唯一的索引可防止集合的文档具有重复的字段值,但允许多个省略该键的文档。
示例
在集合上创建稀疏索引
考虑包含以下文档的集合 scores
:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 } { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
该集合在字段 score
上有一个稀疏索引:
db.scores.createIndex( { score: 1 } , { sparse: true } )
然后,以下对 scores
集合的查询使用该稀疏索引返回 score
字段小于 ($lt
) 90
的文档:
db.scores.find( { score: { $lt: 90 } } )
由于用户 ID "newbie"
的文档不包含 score
字段,因此不满足查询条件,因此该查询可以使用稀疏索引返回结果:
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
集合上的稀疏索引无法返回完整结果
考虑包含以下文档的集合 scores
:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 } { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
该集合在字段 score
上有一个稀疏索引:
db.scores.createIndex( { score: 1 } , { sparse: true } )
由于用户 ID "newbie"
的文档不包含 score
字段,因此该稀疏索引不包含该文档的条目。
以下查询返回 scores
集合中的所有文档(按 score
字段排序):
db.scores.find().sort( { score: -1 } )
即使按索引字段排序,MongoDB 也不会选择稀疏索引来完成查询以返回完整结果:
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 } { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }
要使用稀疏索引,请用 hint()
显式指定该索引:
db.scores.find().sort( { score: -1 } ).hint( { score: 1 } )
使用该索引会导致仅返回具有 score
字段的文档:
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
具有唯一性约束的稀疏索引
考虑包含以下文档的集合 scores
:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 } { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
可以使用以下操作在 score
字段上创建具有唯一性约束和稀疏筛选器的索引:
db.scores.createIndex( { score: 1 } , { sparse: true, unique: true } )
该索引允许插入具有唯一 score
字段值或不包含 score
字段的文档。因此,鉴于 scores
集合中的现有文档,该索引允许以下插入操作:
db.scores.insertMany( [ { "userid": "newbie", "score": 43 }, { "userid": "abby", "score": 34 }, { "userid": "nina" } ] )
但是,该索引不允许添加以下文档,因为 score
值为 82
和 90
的文档已存在:
db.scores.insertMany( [ { "userid": "newbie", "score": 82 }, { "userid": "abby", "score": 90 } ] )
稀疏和非稀疏唯一索引
从 MongoDB 5.0 开始,具有相同键模式的唯一稀疏和唯一非稀疏索引可以存在于同一个集合中。
唯一和稀疏索引创建
此示例将使用相同的键模式和不同的 sparse
选项来创建多个索引:
db.scoreHistory.createIndex( { score : 1 }, { name: "unique_index", unique: true } ) db.scoreHistory.createIndex( { score : 1 }, { name: "unique_sparse_index", unique: true, sparse: true } )
基本索引和稀疏索引创建
还可以使用和不使用稀疏选项创建具有相同键模式的基本索引:
db.scoreHistory.createIndex( { score : 1 }, { name: "sparse_index", sparse: true } ) db.scoreHistory.createIndex( { score : 1 }, { name: "basic_index" } )