如何在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
}