假设我有一个具有以下结构的文档:
_id: ObjectId,
user_id: int,
deleted: bool,
'additional.id': string, // optional field
synced_at: Date //optional field
示例文档将是:
{
"_id" : ObjectId("5dce551d6ad5bb1fd829bd77"),
"user_id" : NumberInt(1),
"additional" : {
"id" : "hahahah"
},
"deleted" : false,
"synced_at" : ISODate("2023-12-19T19:21:26.678+0000")
}
我需要获取与此查询匹配的所有文档的计数:
aggregate(
[
{
$match: {
user_id: 1,
deleted: false,
"additional.id" : {$exists : true},
synced_at : {
$gte: new Date(new Date() - 7 * 60 * 60 * 24 * 1000)
},
}
}
,
{
$count : "productsCount"
}
]
)
所以我创建了一个这样的索引:
createIndex(
{
"user_id": 1,
"deleted": 1,
"additional.id": 1,
"synced_at": -1,
},
{
partialFilterExpression: {
"deleted" : false,
"additional.id" : {
"$exists" : true
},
"synced_at" : {
"$exists" : true
}
}
)
假设查询结果是 20000。当我运行查询时,.explain("executionStats")
我可以看到totalKeysExamined
和totalDocsExamined
都等于 20000,如果我没记错的话,这意味着从索引中取出了 20k 个产品,另外 mongo 还进行了查找所有这 20k 产品。更深入地检查执行统计数据,我可以看到Fetch
索引顶部的阶段:
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"additiona.id" : {
"$exists" : true
}
,
]
},
所以,如果我没记错的话,这意味着 Mongo 从索引中获取所有需要的文档(20k),但出于某种原因,还会检查 20k 个产品中的每一个,即使 "additiona.id" : {"$exists" : true}
它已经在partialFilterExpression
我可以以某种方式避免这个获取阶段吗?我在 Mongo 5 上。
MongoDB 索引是类似 b 树的结构。文档树中的值是每个索引字段的值的列表。
当文档不包含索引中的字段之一时,没有可以插入的“值”,因此
null
使用。这意味着 的索引条目
{user_id:1, deleted: false, additional:{id:null}}
将与 的索引相同
{user_id:1, deleted: false}
使用 $exists 运算符时,第一个文档将匹配,而第二个则不匹配。
这意味着索引无法在不读取其中一些文档的情况下确定哪些文档匹配。
在 MongoDB 查询语言中,检查
null
值将匹配显式设置为 null 和不存在的情况。这意味着如果你改变
到
查询执行器可以识别匹配项,而无需检查文档。
请参阅https://www.mongodb.com/docs/manual/tutorial/query-for-null-fields/#query-for-null-or-missing-fields
根据版本的不同,测试
null
也可能导致查询不被覆盖。如果您可以确定“additional.id”的任何现有值都将是字符串,则可以利用 MongoDB 查询运算符的类型敏感性,并进行测试:
这将匹配任何字符串,避免null bug和存在检查。