我需要为此查询编制索引:
db.messages.find({
$or: [
{ $and: [
{ receiverFbId: 1 },
{ senderFbId: 2 }
]},
{ $and: [
{ receiverFbId: 2 },
{ senderFbId: 1 }
]}
]
}).sort({ timestamp: -1 });
我创建了索引:
db.messages.ensureIndex({ receiverFbId: 1 });
db.messages.ensureIndex({ senderFbId: 1 });
db.messages.ensureIndex({ receiverFbId: 1, senderFbId: 1, timestamp: -1 });
前两个索引适用于按时间戳排序的查询。第三个索引应该适用于带有排序的查询,但它不适用。使用explain()函数的查询返回BasicCursor。
那么我应该使用按时间戳排序来为此查询编制索引?
答案 0 :(得分:1)
我通过db.messages.ensureIndex({ receiverFbId: 1, senderFbId: 1, timestamp: -1 }, {name:"rst"});
进行了测试。
该索引用于MongoDB V2.6.4,但未在V2.4.8上使用
所以,如果你的MongoDB版本低于2.6,也许你运气不好。 :)
否则,我想说,几乎不可能使用索引来成功查询此查询并完全按时间戳排序,即使在V2.6.4上也是如此。 我在这里举个例子。
在mongo shell上运行以下代码,(均为V2.6.4)
// initialize data
var docs = [
// group 1
{
_id : 1,
receiverFbId : 1,
senderFbId : 2,
timestamp : new Date("2014-10-09")
}, {
_id : 2,
receiverFbId : 1,
senderFbId : 2,
timestamp : new Date("2014-10-08")
}, {
_id : 3,
receiverFbId : 1,
senderFbId : 2,
timestamp : new Date("2014-10-07")
},
// group 2
{
_id : 4,
receiverFbId : 2,
senderFbId : 1,
timestamp : new Date("2014-10-08")
}, {
_id : 5,
receiverFbId : 2,
senderFbId : 1,
timestamp : new Date("2014-10-07")
}, {
_id : 6,
receiverFbId : 2,
senderFbId : 1,
timestamp : new Date("2014-10-09")
},
// group 3
{
_id : 7,
receiverFbId : 1,
senderFbId : 8,
timestamp : new Date("2014-10-09")
}, {
_id : 8,
receiverFbId : 2,
senderFbId : 6,
timestamp : new Date("2014-10-01")
} ];
var c = db["messages"];
c.drop();
c.insert(docs);
c.ensureIndex({ receiverFbId: 1, senderFbId: 1, timestamp: -1 }, {name: "rst"});
// make an output test
c.find({
$or: [
{ $and: [
{ receiverFbId: 1 },
{ senderFbId: 2 }
]},
{ $and: [
{ receiverFbId: 2 },
{ senderFbId: 1 }
]}
]
}).sort({ timestamp: -1 });
// result
{ "_id" : 1, "receiverFbId" : 1, "senderFbId" : 2, "timestamp" : ISODate("2014-10-09T00:00:00Z") }
{ "_id" : 6, "receiverFbId" : 2, "senderFbId" : 1, "timestamp" : ISODate("2014-10-09T00:00:00Z") }
{ "_id" : 2, "receiverFbId" : 1, "senderFbId" : 2, "timestamp" : ISODate("2014-10-08T00:00:00Z") }
{ "_id" : 4, "receiverFbId" : 2, "senderFbId" : 1, "timestamp" : ISODate("2014-10-08T00:00:00Z") }
{ "_id" : 3, "receiverFbId" : 1, "senderFbId" : 2, "timestamp" : ISODate("2014-10-07T00:00:00Z") }
{ "_id" : 5, "receiverFbId" : 2, "senderFbId" : 1, "timestamp" : ISODate("2014-10-07T00:00:00Z") }
// make an explain
c.find({
$or: [
{ $and: [
{ receiverFbId: 1 },
{ senderFbId: 2 }
]},
{ $and: [
{ receiverFbId: 2 },
{ senderFbId: 1 }
]}
]
}).sort({ timestamp: -1 }).explain();
// result
{
"clauses" : [ {
"cursor" : "BtreeCursor rst",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 3,
"nscanned" : 3,
"scanAndOrder" : false, // Attention on this line
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"receiverFbId" : [ [ 1, 1 ] ],
"senderFbId" : [ [ 2, 2 ] ],
"timestamp" : [ [ {
"$maxElement" : 1
}, {
"$minElement" : 1
} ] ]
}
}, {
"cursor" : "BtreeCursor rst",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 3,
"nscanned" : 3,
"scanAndOrder" : false, // Attention on this line
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"receiverFbId" : [ [ 2, 2 ] ],
"senderFbId" : [ [ 1, 1 ] ],
"timestamp" : [ [ {
"$maxElement" : 1
}, {
"$minElement" : 1
} ] ]
}
} ],
"cursor" : "QueryOptimizerCursor",
"n" : 6,
"nscannedObjects" : 6,
"nscanned" : 6,
"nscannedObjectsAllPlans" : 6,
"nscannedAllPlans" : 6,
"scanAndOrder" : false, // Attention on this line
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"server" : "Duke-PC:27017",
"filterSet" : false
}
根据上述输出,它几乎符合预期。但我们也可以找到别的东西,
group 1
和group 2
)已分别被选中并按索引排序。timestamp
上有交集。
为了提供正确的结果,需要在内存中进行额外的global
排序。
这种排序应该非常快,因为每个组都已订购。 我理解"scanAndOrder" : false
中.explain()
的最后一行
它差不多但是not completely
实现排序而没有额外的内存排序。
<强>澄清强>
我之前对scanAndOrder : false
中.explain()
的最后一行的理解是错误的
$or
可以完美地合并这些索引的结果,而无需额外的负载缓冲。
感谢Asya Kamsky的帮助。
答案 1 :(得分:0)
似乎排序上的索引只是一个方向,这意味着它可以在同一方向(升序或降序)上对索引进行索引,请参阅this。而且您不需要为同一个前缀放置多个索引。 所以,为了让你的工作做得最好,它应该是这样的:
db.messages.ensureIndex({ receiverFbId: 1, senderFbId: 1 });
排序必须为其所有键指定相同的排序方向(即升序/降序)作为索引键模式,或者为其所有键指定反向排序方向作为索引键模式。例如,索引键模式{a:1,b:1}可以支持对{a:1,b:1}和{a:-1,b:-1}的排序,但不支持{a:-1上的排序,b:1}。
因此db.messages.ensureIndex({receiverFbId:1, senderFbId:1, timestamp:-1})
根本无效。
当您尝试排序时,请使用以下命令:
db.messages.find({...}).sort({timestamp: -1});
要回答您的问题,不,目前它不支持对您建议的排序查询进行索引。如果你真的坚持要做这项工作,你必须将你的时间戳修改为类似(future - timestamp)
之类的东西,以使其在升序时可转位,这样它们就可以按相同的方向排序。