Mongodb$lookup聚合查询需要11+;秒,我如何优化这一点

Mongodb$lookup聚合查询需要11+;秒,我如何优化这一点,mongodb,mongoose,mongodb-query,aggregation-framework,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,我有一个包含3个集合的股价警报应用程序 1) 警报 2) 以美元表示的资产价值每2分钟更新一次,目前拥有2000资产 { "_id" : "xyz", //asset id "1" : 997, //current price of the asset } { "_id" : "EUR", "1" : 0.811798 } 3) 美元的法定兑换率每小时更新一次,目前有160种货币 { "_id" : "xyz", //asset id "1"

我有一个包含3个集合的股价警报应用程序

1) 警报

2) 以美元表示的资产价值每2分钟更新一次,目前拥有2000资产

{
    "_id" : "xyz", //asset id
    "1" : 997, //current price of the asset
}
{
    "_id" : "EUR",
    "1" : 0.811798
}
3) 美元的法定兑换率每小时更新一次,目前有160种货币

{
    "_id" : "xyz", //asset id
    "1" : 997, //current price of the asset
}
{
    "_id" : "EUR",
    "1" : 0.811798
}

人员必须能够设置一个警报,说明“如果欧元xyz低于1000或高于1000,请提醒我”

对于测试数据,我设置了100000个警报,第一步是从我加入的资产表中查找资产xyz的当前价格

 Alert
        .aggregate([
            {
                $lookup: {
                    from: "assets",
                    localField: "2", //field containing asset id
                    foreignField: "_id", //foreign field with asset id
                    as: "s"
                }

            }
        ])

        .allowDiskUse(true)

        .exec((error, result) => {

            if (error) {
                console.log(error)
            }
            else {
                console.log("Got", result.length, "documents mongoose")
            }
            mongoose.connection.close()
        })
目前仅此一项就需要11秒

我希望能够将每个资产的当前价格乘以指定的货币,然后检查它是否高于或低于用户设置的水平,以便触发警报

例如,如果警报为xyz:EUR,我想得到xyz的资产价格(以美元为单位),美元对欧元的价格,并将两者相乘以得到最终价格xyz:EUR,然后检查该值是否大于或小于1000以触发警报

外部字段是一个_id字段,我假设默认情况下它是索引字段。我在localField 2:上设置了一个索引,它是我的资产id,3:是我的菲亚特符号

下面是对我的目标进行getIndexes()查询的结果

[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.alerts"
    },
    {
        "v" : 2,
        "key" : {
            "2" : 1
        },
        "name" : "2_1",
        "ns" : "test.alerts",
        "background" : true
    },
    {
        "v" : 2,
        "key" : {
            "3" : 1
        },
        "name" : "3_1",
        "ns" : "test.alerts",
        "background" : true
    }
]
我还运行了一个explain()来检查正在发生的事情,尽管设置了索引,但它仍然指示一个COLLSCAN

{
        "stages": [
                {
                        "$cursor": {
                                "query": {},
                                "queryPlanner": {
                                        "plannerVersion": 1,
                                        "namespace": "test.alerts",
                                        "indexFilterSet": false,
                                        "parsedQuery": {},
                                        "winningPlan": {
                                                "stage": "COLLSCAN",
                                                "direction": "forward"
                                        },
                                        "rejectedPlans": []
                                }
                        }
                },
                {
                        "$lookup": {
                                "from": "assets",
                                "as": "s",
                                "localField": "2",
                                "foreignField": "_id"
                        }
                }
        ],
        "ok": 1
}
任何建议、建议都会非常有用。多谢各位

备选方案

我真的不想做的另一个选择是计算代码中的所有价格,并向警报集合添加另一个名为price的字段,该字段每2分钟更新一次xyz:EUR和其他警报。潜在的1500项资产x 160辆菲亚特将意味着每2分钟就有大量的进入

更新1探查器输出

钥匙没有被使用!有什么想法吗

getmore test.e1_sources 89ms Tue Feb 27 2018 19:22:28
command:{
    "getMore" : NumberLong("6524439989055389783"),
    "collection" : "alerts",
    "batchSize" : 1000,
    "$readPreference" : {
        "mode" : "secondaryPreferred"
    },
    "$db" : "test"
} originatingCommand:{
    "aggregate" : "alerts",
    "pipeline" : [
        {
            "$lookup" : {
                "from" : "assets",
                "localField" : "2",
                "foreignField" : "_id",
                "as" : "s"
            }
        }
    ],
    "allowDiskUse" : true,
    "cursor" : {
        "batchSize" : 1000
    },
    "$db" : "test"
} cursorid:NumberLong("6524439989055389783") keysExamined:0 docsExamined:0 cursorExhausted numYield:2 locks:{
    "Global" : {
        "acquireCount" : {
            "r" : NumberLong(6000)
        }
    },
    "Database" : {
        "acquireCount" : {
            "r" : NumberLong(3000)
        }
    },
    "Collection" : {
        "acquireCount" : {
            "r" : NumberLong(2999)
        }
    }
} nreturned:1000 responseLength:164393 protocol:op_query planSummary:COLLSCAN client:127.0.0.1 allUsers:[ ] user: 

人员必须能够设置一个警报,说明“如果欧元xyz低于1000或高于1000,请提醒我”

根据你提到的这一行。添加匹配阶段以筛选该用户id的assetid。这将减少您必须加入的数据量

Alert.aggregate([
            {
                $match:{
                     "1"://user id,
                     "2"://asset id
                     }
            },
            {
                $lookup: {
                    from: "assets",
                    localField: "2", //field containing asset id
                    foreignField: "_id", //foreign field with asset id
                    as: "s"
                }

            }
        ])

        .allowDiskUse(true)

        .exec((error, result) => {

            if (error) {
                console.log(error)
            }
            else {
                console.log("Got", result.length, "documents mongoose")
            }
            mongoose.connection.close()
        })

感谢您的回答,但一个主要问题是,我需要每2分钟触发设置不同用户的所有警报,警报不仅仅针对单个用户,我注意到localField不使用任何索引,无论您是否设置了索引。使用查找时,这意味着左键连接,它不在本地字段上使用索引,因为您是基于一列将左表中的所有值连接到右表中。这就是为什么它会做一个乌尔斯坎。