在我看来,以下两个查询应该具有完全相同的“explain”输出:
问题1:
{
$and: [
{ $or: [
{ Foo: "123" },
{ Bar: "456" }
] },
{ Baz: { $in: ["abc", "def"] } }
]
}
问题2:
{
$or: [
{ Foo: "123" },
{ Bar: "456" }
],
Baz: { $in: ["abc", "def"] } }
}
注意,我在
{ Foo: -1, Baz: -1 }
和{ Bar: -1, Baz: -1 }
上有索引,因此这是针对$or
运算符优化的。实际上,在查询2的版本中,在explain输出中,我看到了两个clauses
,它们都有适当的索引界限,一个用于(Foo, Baz)
,另一个用于(Bar, Baz)
。MongoDB正在做它应该做的事情。但在第一个版本(查询1)中,不再有
clauses
。它给了我一个没有指定索引界限的BasicCursor
。这两个查询有什么区别?为什么Mongo似乎能够优化2而不是1?
现在我正在使用mongovue测试这些查询,因此我可以控制json,但最终我将使用c驱动程序,我非常确定它将始终以1而不是2的形式发出语法,因此了解发生了什么很重要……
最佳答案
这似乎是MongoDB中的某种错误。你用的是什么版本?
根据that bug report问题在2.5.3
中得到解决。
在我们转到更高版本(我在2.4.6
)之前,我们必须小心$and
运算符。
我也要在2.6
里试试。
更新:
事实上,在2.6.3中,我现在是固定的。
> db.test.find()
{ "_id" : 1, "Fields" : { "K1" : 123, "K2" : 456 } }
{ "_id" : 2, "Fields" : { "K1" : 456, "K2" : 123 } }
> db.test.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.test"
},
{
"v" : 1,
"key" : {
"Fields.K1" : 1
},
"name" : "Fields.K1_1",
"ns" : "test.test"
},
{
"v" : 1,
"key" : {
"Fields.K2" : 1
},
"name" : "Fields.K2_1",
"ns" : "test.test"
}
]
> db.test.find({"$and" : [{ "Fields.K1" : 123, "Fields.K2" : 456}]}).explain()
{
"cursor" : "BtreeCursor Fields.K1_1",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 2,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"Fields.K1" : [
[
123,
123
]
]
},
"server" : "benihime:27017",
"filterSet" : false
}
> db.test.find({ "Fields.K1" : 123, "Fields.K2" : 456}).explain()
{
"cursor" : "BtreeCursor Fields.K1_1",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 2,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"Fields.K1" : [
[
123,
123
]
]
},
"server" : "benihime:27017",
"filterSet" : false
}