如何在MongoDB中为此特定查询创建索引
我需要为该查询编制索引:如何在MongoDB中为此特定查询创建索引,mongodb,Mongodb,我需要为该查询编制索引: db.messages.find({ $or: [ { $and: [ { receiverFbId: 1 }, { senderFbId: 2 } ]}, { $and: [ { receiverFbId: 2 }, { senderFbId: 1 } ]} ] }).sort({ timestamp: -1 }); 我创建了索引: db.messages.ensureIndex
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 });
前两个索引用于不按时间戳排序的查询。第三个索引应该可以用于带有sort的查询,但它不能。函数的query with explain()返回BasicCursor
那么,我应该创建什么索引来为这个查询建立按时间戳排序的索引呢?我通过
db.messages.ensureIndex({receiverFbId:1,senderbid:1,timestamp:-1},{name:“rst”})进行了测试代码>。
该索引用于MongoDB V2.6.4,但未用于V2.4.8。
因此,如果您的MongoDB版本低于2.6,那么您可能运气不佳
否则,我想说的是,即使在V2.6.4上,也几乎不可能使用index成功地完成这个查询并完全按照时间戳进行排序。
这里我举一个例子
在mongo shell上运行以下代码(均为V2.6.4)
从以上输出来看,基本符合预期。但我们也可以找到别的东西
- 这两个组(
第1组
和第2组
)都已分别被选定并按索引排序
- 但是,这两个组在时间戳上有交集。
为了提供正确的结果,需要在内存中进行额外的
global
排序。
这种排序应该非常快,因为每个组都已排序
我理解.explain()
中的最后一行“scanander”:false
它几乎实现了排序,但是并不完全
实现了排序,而没有额外的内存排序
---------------编辑------------------ 澄清 我以前对
.explain()
中最后一行扫描者:false
的理解是错误的。$或
可以完美地合并来自这些索引的结果,而无需额外的缓冲
感谢Asya Kamsky的帮助。似乎排序上的索引实际上只是一个方向,这意味着它可以在同一方向(升序或降序)上索引排序,请参阅。并且不需要为同一前缀放置多个索引。 因此,为了让您的分类发挥最佳效果,应该如下所示:
db.messages.ensureIndex({receiverFbId:1,senderBid:1})代码>
排序必须为其所有键指定与索引键模式相同的排序方向(即升序/降序),或为其所有键指定与索引键模式相反的排序方向。例如,索引键模式{a:1,b:1}可以支持对{a:1,b:1}和{a:-1,b:-1}进行排序,但对{a:-1,b:1}不支持排序
所以db.messages.ensureIndex({receiverFbId:1,senderbid:1,timestamp:-1})
根本不起作用
当您尝试排序时,请使用以下命令:
db.messages.find({…}).sort({timestamp:-1})代码>
回答您的问题,不,目前它不支持对您建议的排序查询进行索引。如果你真的坚持要做到这一点,你必须将时间戳修改成类似于(future-timestamp)
的东西,使其在升序时可索引,以便它们可以按相同的方向排序。据我所知,排序时的索引只是一个方向。哦,你只需要一个索引(receiverFbId:1,senderbid:1)你使用的是什么版本@我不知道你的评论是什么意思,但对我来说似乎不正确。根据我的理解,它应该是有效的。你能告诉我仅仅{receiverFbId:1,senderbid:1}
是否有效吗?我在MongoDB V2.6.4版上试过,这三个索引中的任何一个都会被使用,最后一个索引有优先权。但是,当在V2.4.8上试用时,将不使用任何一个。似乎V2.4.8在索引选择方面不够智能。@AsyaKamsky,请看下面我的答案。无论mongo版本如何,您对mongo索引的假设都是错误的。您是错误的。引用的段落仅指按多个字段排序。在给定的查询中,OP只希望按单个字段“timestamp”排序,而不是按多个字段排序。索引的前导部分用于过滤,最后一部分用于2.6之前的排序(在2.6中),因为$or子句限制了索引的使用。既然OP在receiverFbId
和senderbid
字段上都使用了$or和$and,索引怎么会受到限制?它等于“请返回两个字段都存在的所有记录”,并且所涉及的序列是一个排序,否则它将只是`ensureIndex({timestamp:-1})。你完全是wrong@AsyaKamsky,请重新阅读操作问题,他想索引此db.messages.ensureIndex({receiverFbId:1,SenderBid:1,时间戳:-1})代码>这是多个;另外,我确实有一个解决方案,使时间戳
在同一方向上排序
,使其完全可索引索引索引有三个字段。他的查询通过相等对前两个字段进行过滤,然后使用第三个字段进行排序,这可以使用我同意的索引按升序或降序进行。然而,要在带有sort的查询上建立索引,仍然需要在三个字段上建立索引。他的查询涉及$或$,在2个字段上清楚地显示OP想要排序,我不知道你说的ScanAndder是什么意思,它表明不需要内存中的排序,索引被充分利用了。没有全局排序-它是两个排序列表的合并,非常有效。除此之外,您的答案是100%正确的,对于2.6或更高版本,早期版本无法做到这一点。@Asya Kamsky,我完全理解手册中的描述:如果Scanander为false,MongoDB可以使用索引中文档的顺序来返回排序结果。
我的第一个想法是它直接返回最终的排序结果。但现在我发现它可能是,只有从该索引中按文本排序的结果。当$or合并两个排序列表时,比较和输入的行为
// 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
}