MongoDB聚合查询

时间:2018-03-09 06:35:49

标签: mongodb mongoose aggregation-framework

我试图在我的数据库中查询一组值,但不是自己获取值,而是希望在日期最接近的另一个文档(具有不同要求)的值和值的乘积。以下是我目前使用n + 1查询的方式:

Model.find({
  user: user1,
  date: { $gte: start, $lte: end }
}, 'date value', (err, results) => {
  results.forEach(e => {
    Model.find({
      user: user2,
      date: { $gte: e.date }
    }, 'value').sort({ date: 1 }).limit(1).exec((err, matched) => {
      e.value *= matched[0].value;
    });
  });
});

所以我想要在与user1相关联的日期范围内的值,每个日期乘以与user2关联的最近文档(日期)的值。我们无法保证user1user2的文档日期完全相同,因此我使用$gte的组合,排序和限制来获取最接近(我知道它并不精确,因为可能比它早得多,但这已经足够了)。

我认为必须有办法用aggregate来做到这一点。我正在考虑使用$lookup来加入每个用户的相应文档中的值,但如果没有完全匹配的字段,我就不知道如何做到这一点。

我是否在正确的轨道上?必须有更好的方法来实现这一目标。

2 个答案:

答案 0 :(得分:1)

您可以在3.6

中尝试以下聚合查询

使用$lookup let语法。

Model.aggregate([
  {"$match":{
      "user": user1,
        "date": {"$gte":start, "$lte":end}
  }}, 
  {"$lookup":{
    "from": collectionname, use collection name here
    "let": {"date":"$date"},
    "pipeline":[
      {"$match":{ 
        "user": user2, 
        "$expr":{"$gte":["$date","$$date"]}
      }},
     {"$sort":{"date": 1}},
     {"$limit":1},
     {"$project":{"value":1}}
    ],
    "as": "lookup-data"
  }},
  {"$project":{
    "date":1, 
    "value":{
     "$multiply":[
       "$value", {
         "$let":{
           "vars":{"lookupdata":{"$arrayElemAt":["$lookup-data",0]}},
            "in":"$$lookupdata.value"
          }
        }
      ]
    }
  }}
])

答案 1 :(得分:1)

使用3.2查找语法是可能的,效率非常低:

db.collection.aggregate([
    {$match: {user: user1, date:{$gte: start, $lte: end)}}},
    {$addFields: {anotherUser: user2}},
    {$lookup: {
       from: "collection",
       localField: "anotherUser",
       foreignField: "user",
       as: "anotherUser"
    }},     
    {$project: {
        user:1,
        date:1,
        value: {$let: {
            vars: {         
                anotherUser: {$let: {
                    vars: {
                        all: {$filter: { 
                            input: "$anotherUser", 
                            as: "au", 
                            cond: {$gte: ["$$au.date", "$date"]} 
                        }}
                    },
                    in: { $let: {
                        vars: {
                            minDate: {$min:  "$$all.date"}
                        },
                        in: { $arrayElemAt: [ 
                            {$filter: { 
                                input: "$$all", 
                                as: "su", 
                                cond: {$eq: ["$$su.date", "$$minDate"]} 
                            }},
                            0
                        ]}
                    }}
                }}
            },
            in: {$multiply: ["$value", "$$anotherUser.value"] }
        }}
     }}     
])

$lookup阶段会为user2个文档的anotherUser字段添加所有user1个文档。然后按日期过滤,最终解析为单个值,但它仅在下一个$project阶段发生。在大型数据集上$lookup阶段很容易耗尽100MB limit

我没有测试性能。如果按用户和日期有适当的索引,那么原始的多查询方法可能比这种管道更快。它绝对比聚合使用更少的内存,并且更具可读性/可测试性/可维护性。