如何在MongoDb $或查询中使用带排序的索引?

时间:2017-10-28 03:53:33

标签: node.js mongodb

我的 mailCollection 集合包含。发件人和。收件人值的索引。当我执行$或查询并按。 timestamp 排序时,将扫描整个集合。如何索引此集合或重写查询以获取文档。 sender 或。收件人匹配特定值,排序和限制?

mailCollection 索引:

{recipient: 1}
{sender: 1}

慢代码:

email = <some email address I want to query>;
cursor = mailCollection.find({$or: [{sender: email}, {recipient: email}]});
cursor.sort({timestamp: 1}).limit(100).toArray(function(error, result) {
  //yikes, full collection scan
});

如果它有所作为,我使用MongoDb node.js驱动程序。

1 个答案:

答案 0 :(得分:1)

狡猾。三向索引交叉点不起作用。所以你需要与复合索引相交。但是,您需要确保在复合索引中有正确的前缀,以便正确使用它并使其有效。

数据

> db.indextest.find()
{ "_id" : ObjectId("59f401893e9fcadcbf2b1694"), "sender" : "me@example.com", "recipient" : "you@example.com", "timestamp" : ISODate("2017-10-28T04:03:21.468Z") }
{ "_id" : ObjectId("59f405d93e9fcadcbf2b1695"), "sender" : "me@example.com", "recipient" : "somebody@example.com", "timestamp" : ISODate("2017-10-28T04:21:45.573Z") }
{ "_id" : ObjectId("59f408413e9fcadcbf2b1699"), "sender" : "you@example.com", "recipient" : "somebody@example.com", "timestamp" : ISODate("2017-10-28T04:32:01.651Z") }
{ "_id" : ObjectId("59f408563e9fcadcbf2b169a"), "sender" : "you@example.com", "recipient" : "me@example.com", "timestamp" : ISODate("2017-10-28T04:32:22.376Z") }
{ "_id" : ObjectId("59f408763e9fcadcbf2b169b"), "sender" : "somebody@example.com", "recipient" : "you@example.com", "timestamp" : ISODate("2017-10-28T04:32:54.268Z") }
{ "_id" : ObjectId("59f4087e3e9fcadcbf2b169c"), "sender" : "somebody@example.com", "recipient" : "me@example.com", "timestamp" : ISODate("2017-10-28T04:33:02.615Z") }

指数

我决定在senderrecipient上创建一个索引,并在timestamp上添加一个附加键。这应该为您提供有关最常见用例的有效查询:

  • 给定用户收到哪些消息,按日期排序?
  • 给定用户发送了哪些消息,按日期排序?
  • 和您的查询;)

这给你带来最大的开销(一个索引中的一个字段)。

给出指数

> db.indextest.getIndices()
[
  {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.indextest"
    },
    {
        "v" : 1,
        "key" : {
            "recipient" : 1,
            "timestamp" : 1
        },
        "name" : "recipient_1_timestamp_1",
        "ns" : "test.indextest"
    },
    {
        "v" : 1,
        "key" : {
            "sender" : 1,
            "timestamp" : 1
        },
        "name" : "sender_1_timestamp_1",
        "ns" : "test.indextest"
    }
]

结果

运行查询:

> db.indextest.find({$or:[{sender:"you@example.com"},{recipient:"you@example.com"}]}).sort({timestamp:1}).explain()

给出预期结果(为简洁起见编辑):

> db.indextest.find({$or:[{sender:"you@example.com"},{recipient:"you@example.com"}]}).sort({timestamp:1}).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.indextest",
        "indexFilterSet" : false,
...
        "winningPlan" : {
            "stage" : "SUBPLAN",
            "inputStage" : {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "SORT_MERGE",
                    "sortPattern" : {
                        "timestamp" : 1
                    },
                    "inputStages" : [
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "recipient" : 1,
                                "timestamp" : 1
                            },
                            "indexName" : "recipient_1_timestamp_1",
                            "isMultiKey" : false,
...
                            "direction" : "forward",
                            "indexBounds" : {
                                "recipient" : [
                                    "[\"you@example.com\", \"you@example.com\"]"
                                ],
                                "timestamp" : [
                                    "[MinKey, MaxKey]"
                                ]
                            }
                        },
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "sender" : 1,
                                "timestamp" : 1
                            },
                            "indexName" : "sender_1_timestamp_1",
                            "isMultiKey" : false,
...
                            "direction" : "forward",
                            "indexBounds" : {
                                "sender" : [
                                    "[\"you@example.com\", \"you@example.com\"]"
                                ],
                                "timestamp" : [
                                    "[MinKey, MaxKey]"
                                ]
                            }
                        }
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
...
    "ok" : 1
}

编辑:根据您的收藏大小,排序合并可能并不理想。