查找每种工具的价值变化的日期

时间:2019-05-18 17:08:47

标签: mongodb aggregation-framework

我在mongodb集合中有以下一系列订单:

id date       value
A  1 Jan 18   1
A  2 Jan 18   0
A  3 Jan 18   0
B  14 Jan 18  4
B  15 Jan 18  5
B  16 Jan 18  0

使用mongodb聚合管道(mongo 3.4),我试图找出每个id,值的日期从非零变为0,以及这些记录的“ id组”。 >

第二次更新:19年5月26日

我已经更新了问题,以使第一个,最后一个实现看起来更清晰:

{
    "$addFields": {
      "date": {
        "$dateFromString": {
          "dateString": "$date"
        }
      }
    }
  },
  {
    $group: {
      _id: {
        "id": "$id",
        "value": "$value"
      },
      "first": {
        "$first": "$date"
      },
      "last": {
        "$last": "$date"
      }
    }
  },
  {
    "$match": {
      "_id.value": 0
    }
  }

https://mongoplayground.net/p/moBRI2Q7aGu

这给了我

id value   first      last
A  0       2 Jan 18   3 Jan 18
B  0       16 Jan 18  16 Jan 18

如果我查看“第一个”日期,那是那些值首先从非零变为0的日期。

但是,我希望看到在某个时间点从非零变为0的那些值的整个“ id组”。所以:

id value   first      last
A  1       1 Jan 18   1 Jan 18
A  0       2 Jan 18   3 Jan 18
B  4       14 Jan 18  14 Jan 18
B  5       15 Jan 18  15 Jan 18
B  0       16 Jan 18  16 Jan 18

要获取此信息,我需要在上述管道匹配之前访问group阶段,所以https://mongoplayground.net/p/YTP-NBJtO4R,并使用第一个聚合管道的结果集对此进行过滤。我是通过第一个结果集上的左联接在熊猫中执行此操作的,但这似乎不太好。

所以现在我有两个不同的管道,这似乎有点不方便。理想情况下,最后一个结果集将来自单个聚合管道。

2 个答案:

答案 0 :(得分:2)

您可以使用以下汇总

db.collection.aggregate([
  { "$addFields": {
    "date": { "$dateFromString": { "dateString": "$date" }}
  }},
  { "$sort": { "date": 1 }},
  { "$match": { "value": 0 }},
  { "$group": {
    "_id": "$id",
    "date": { "$first": "$date" },
    "value": { "$first": "$value" }
  }}
])

MongoPlayground

更多聚合技巧

db.collection.aggregate([
  { "$match": { "value": "0" }},
  { "$addFields": {
    "date": { "$dateFromString": { "dateString": "$date" }}
  }},
  { "$sort": { "date": 1 }},
  { "$group": {
    "_id": "$id",
    "data": {
      "$push": {
        "value": "$value",
        "date": "$date"
      }
    }
  }},
  { "$project": {
    "data": {
      "$arrayElemAt": [
        { "$filter": {
          "input": "$data",
          "cond": { "$eq": ["$$this.value", "0"] }
        }},
        0
      ]
    }
  }},
  { "$replaceRoot": {
    "newRoot": { "$mergeObjects": [{ "id": "$_id" }, "$data"] }
  }}
])

如果文档已经包含日期格式(而不是上面显示的方式)的日期,则可以删除第一阶段$addFields

MongoPlayground

答案 1 :(得分:1)

首先按ID分组,以便我们可以单独处理每个“订单”:

{
  $group: {
     _id: "$id",
     date_x_value: {$push: {date: "$date", value: "$value"}},
     sum: {$sum: "$value"}
  }
}

现在仅匹配相关文档:

{
  $match: {
      $and: [ {"date_x_value.value": 0}, {sum: {$gt: 0}}]
   }
}

按照日期排序:

{
  $sort: {
    "date_x_value.date": 1
  }
}

现在进行实际查询:

{ $addFields:
    {
        matches: { 
            $reduce: {
               input: "$date_x_value", 
               initialValue: {"last_value": 0, "dates": []},
               in: { 
                   last_value: "$$this.value",
                   dates: { $concatArrays : [
                       {
                          $cond:{
                             if: {$and: [{$gt: ["$$value.last_value", 0]}, {$eq: ["$$this.value", 0]}]},
                             then: ["$$this.date"],
                             else: []
                          }
                      }, "$$value.dates"] 
                   }
               }
          }
       } 
    }
}

这将返回一个日期数组,请注意,日期是0以后的日期。 这两个文档的含义:

date: Jan 1,  value: 4
date: Jan 2,  value: 0

该数组将包含1月2日。

编辑**:请注意,我并没有完全尝试优化查询 ,以提高可读性,如果这是一个问题,则应优化第一个根据集合的索引在主查询之前的部分。