如何在 MongoDB 中对`$or`和`$in`查询进行排序?
问题:如何在 MongoDB 中对$or和$in查询进行排序? 这是这个问题的后续行动 - 请参阅上下文。 这个问题涉及链接问题的几个特殊情况 - 即使用$in或$or运算符时 MongoDB 中的排序如何工作,以及如何确保使用索引进行排序与内存排序。 $in: 例如,假设我们有一个集合,其中文档结构是 {a: XXX, b: XXX} ...我们在a和b上有一个复合索引,并希望运行查询 {a:
问题:如何在 MongoDB 中对$or
和$in
查询进行排序?
这是这个问题的后续行动 - 请参阅上下文。
这个问题涉及链接问题的几个特殊情况 - 即使用$in
或$or
运算符时 MongoDB 中的排序如何工作,以及如何确保使用索引进行排序与内存排序。
$in:
例如,假设我们有一个集合,其中文档结构是
{a: XXX, b: XXX}
...我们在a
和b
上有一个复合索引,并希望运行查询
{a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}
如果它在a
或b
上,排序将如何进行?$in
是一种相等运算符,但在我看来,即使这样,在b
上进行带有索引的排序也是不可能的。我认为,只有首先对$in
值数组进行排序,才有可能使用索引对a
进行排序 - 但我不知道 MongoDB 是否这样做。
$或:
由于$or
查询(IIUC)被作为多个查询处理,并且可能使用它们各自的索引进行排序,排序后的结果是否以某种方式合并,或者$or
是否强制对所有结果进行内存排序?如果是前者,这个过程的时间复杂度是多少?
解答
注意: 此答案基于 MongoDB 3.2.4。
值得发现explain()
在 MongoDB 中的使用。查询的explain()
输出(例如db.collection.explain().find(...)
)允许您检查查询中使用了哪个索引,并且使用db.collection.explain('executionStats')
还会显示由于内存中 zwz1 的限制,查询是成功还是失败。
$in
可以将$in
查询视为一系列相等查询。例如,{a: {$in: [1,3,5]}}
可以被认为是{a:1}, {a:3}, {a:5}
。 MongoDB 在继续查询之前会对$in
数组进行排序,因此{$in: [3,5,1]}
与{$in: [1,3,5]}
没有什么不同。
假设集合的索引为
{a:1, b:1}
- 按
a
排序
db.coll.find({a: {$in: [1,3,5]}}).sort({a:1})
MongoDB 将能够使用{a:1,b:1}
索引,因为这个查询可以被认为是{a:1}, {a:3}, {a:5}
查询的联合。按{a:1}
排序允许使用索引前缀,因此 MongoDB 不需要执行内存排序。
同样的情况也适用于查询:
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({a:1})
由于sort({a:1})
也使用索引前缀(在这种情况下为a
),因此不需要内存中的SORT
阶段。
- 按
b
排序
与a
排序相比,这是一个更有趣的案例。例如:
db.coll.find({a: {$in: [1,3,5]}}).sort({b:1})
此查询的explain()
输出将有一个称为SORT_MERGE
的阶段。请记住,查询的find()
部分可以被认为是{a:1}, {a:3}, {a:5}
。
由于{a:1,b:1}
索引的性质,查询db.coll.find({a:1}).sort({b:1})
不需要内存中的SORT
阶段:即 MongoDB 在满足a
上的相等参数后,可以简单地遍历(排序的)索引并返回按b
排序的文档。例如,对于每个a
,有很多b
由于索引的原因已经按b
排序。
使用$in
,整体查询可以认为是:
- zoz100077
*db.coll.find({a:3}).sort({b:1})
*db.coll.find({a:5}).sort({b:1})
- 取上面的单个查询结果,并使用
b
的值进行合并。查询_不需要内存排序阶段_因为各个查询结果已经按b
排序。 MongoDB 只需要将(已排序的)子查询结果合并为一个结果。
同样,查询
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({b:1})
也使用了SORT_MERGE
阶段,与上面的查询非常相似。不同之处在于,对于每个a
(由于索引{a:1,b:1}
将按b
排序),单个查询基于_a range of_b
(而不是_every_b
)输出文档。因此,查询不需要内存中的排序阶段。
$或
对于要使用索引的$or
查询,$or
表达式中的每个子句都必须具有与之关联的索引。如果满足此要求,则查询可以像使用$in
查询一样使用SORT_MERGE
阶段。例如:
db.coll.explain().find({$or:[{a:1},{a:3},{a:5}]}).sort({b:1})
将具有与上面的$in
示例几乎相同的查询计划、索引使用和SORT_MERGE
阶段。本质上,查询可以被认为是:
-
db.coll.find({a:1}).sort({b:1})
-
db.coll.find({a:3}).sort({b:1})
-
db.coll.find({a:5}).sort({b:1})
-
取上面的单个查询结果,并使用
b
的值进行合并。
就像之前的$in
示例一样。
但是,此查询:
db.coll.explain().find({$or:[{a:1},{b:1}]}).sort({b:1})
不能使用任何索引(因为我们没有{b:1}
索引)。此查询将导致集合扫描,因此_将有一个内存排序阶段_,因为没有使用索引。
但是,如果我们创建索引{b:1}
,查询将按如下方式进行:
-
db.coll.find({a:1}).sort({b:1})
-
db.coll.find({b:1}).sort({b:1})
-
获取上面的单个查询结果,并使用
b
的值执行合并(由于索引{a:1,b:1}
和{b:1}
,它已经在两个子查询中排序)。
MongoDB 将结合{a:1}
和{b:1}
查询的结果,并对结果进行合并。合并过程是线性时间,例如O(n)
.
总之,在$or
查询中,每个词都必须有一个索引,包括sort()
阶段。否则,MongoDB 将不得不执行内存排序。
更多推荐
所有评论(0)