有条件地使用没有elemMatch的数组过滤进行聚合投射?

时间:2016-03-14 16:23:29

标签: mongodb aggregation-framework

事实证明$ project不支持聚合中的$ elemMatch。在3.2中,他们引入了filter等,这似乎并没有解决我的问题。

让我解释一下我正在尝试做什么,假设我在数据库中有以下文件。

db.test.insert(
{
  "ad_account_id": 150,
  "internal_id": 1,
  "daily": [{
    "timestamp": "2016-12-01",
    "impressions": 5
  }, {
    "timestamp": "2016-12-06",
    "impressions": 7
  }]
})

db.test.insert(
{
  "ad_account_id": 150,
  "internal_id": 2,
  "daily": [{
    "timestamp": "2016-12-03",
    "impressions": 6
  }] 
})

db.test.insert({
  "ad_account_id": 150,
  "internal_id": 3,
  "daily": []
})


db.test.insert({
  "ad_account_id": 16,
  "internal_id": 3,
  "daily": []
})

现在假设用户查询ad_account_id:150,并按开始和结束日期范围过滤为“2016-12-01”至“2016-12-02”。

我的聚合查询如下所示(跳过排序,限制等)

db.getCollection('test').aggregate({
        "$match" : {
          "ad_account_id" : 150,
          "daily" : {
            "$elemMatch" : {
              "timestamp" : {
                "$lte" : "2016-12-02",
                "$gte" : "2016-12-01"
              }
            }
          }
        }
      },
      {
        "$unwind" : "$daily"
      },
      {
        "$match" : {
          "daily.timestamp" : {
            "$lte" : "2016-12-02",
            "$gte" : "2016-12-01"
          }
        }
      },
      {
        "$group" : {
          "impressions" : {
            "$sum" : "$daily.impressions"
          },
          "ad_account_id" : {
            "$first" : "$ad_account_id"
          },
          "_id" : "$internal_id"
        }
      },
      {
        "$project" : {
          "impressions" : 1,
          "ga_transactions" : 1,
          "ad_account_id" : 1
        }
      }
);

当前结果

{ "_id" : 1, "impressions" : 5, "ad_account_id" : 150 }

在我们的本地开发中,它最初似乎没问题。即使有一百万份文件,查询也很快,我们很高兴。

但是我们很快意识到了我们的用例,即使每天的数据不在开始日期和结束日期之间,我们也需要显示行。其中的印象数等可以用0表示,但必须要显示它们。

所以我们想要的期望的结果就是这个

 { "_id" : 1, "impressions" : 5, "ad_account_id" : 150 }
 { "_id" : 2, "impressions" : 0, "ad_account_id" : 150 }
 { "_id" : 3, "impressions" : 0, "ad_account_id" : 150 }

在过去的几个小时里,我一直在努力解决这个问题,因为我似乎无法在单个mongo查询中得到这个。我以为我会将我的匹配限制为广告帐户ID,然后执行$项目,如果该数据范围之间没有数据,我只会添加一个示例条目到每日,并将起始数据作为时间戳这样的事情。

{
  "ad_account_id": 150,
  "internal_id": 3,
  "daily": [{timestamp: "2016-02-01"}]
)

但不幸的是我无法让这个工作,因为在$ project中你无法做到$ elemMatch。像$ filter等新东西似乎没有解决我的问题。

我也尝试过工会,我认为它几乎也存在。但是这给了我一个错误“”FieldPath'2016-12-01'不以$“开头。

您认为最好的方法是什么?

1 个答案:

答案 0 :(得分:1)

好吧,花了几个小时的时间,并有一个尤里卡时刻。结果我离解决方案太远了。

db.getCollection('test').aggregate(
    {
        "$match" : {
          "ad_account_id" : 150
        }
      },
      { "$project": {
        "ad_account_id": 1,
        "internal_id": 1,
        "daily": {
            "$setUnion": [
                { "$map": {
                    "input": "$daily",
                    "as": "day",
                    "in": {
                        "$cond": [
                            { "$and": [
                                { "$gte": [ "$$day.timestamp", "2016-12-01" ] },
                                { "$lte": [ "$$day.timestamp", "2016-12-02" ] }
                            ]},
                            "$$day",
                            false
                        ]
                    }
                }},
                [{"$literal": {"timestamp": "2016-12-01" } }]
            ]
        }
      }},
      {
        "$unwind" : "$daily"
      },
      {
        "$group" : {
          "impressions" : {
            "$sum" : "$daily.impressions"
          },
          "ad_account_id" : {
...          "$first" : "$ad_account_id"
...        },
          "_id" : "$internal_id"
        }
      },
      {
        "$project" : {
          "impressions" : 1,
          "ad_account_id" : 1
        }
      }
);

对于想要这个想法的人,我将"daily_mod": { $addToSet: "$daily" }添加到最后一个$ group阶段,并将其添加到上一个项目"daily_mod": 1

这将真正帮助您了解发生的情况并提供输出 - :

{ "_id" : 3, "impressions" : 0, "ad_account_id" : 150, "daily_mod" : [ { "timestamp" : "2016-12-01" } ] }
{ "_id" : 2, "impressions" : 0, "ad_account_id" : 150, "daily_mod" : [ false, { "timestamp" : "2016-12-01" } ] }
{ "_id" : 1, "impressions" : 5, "ad_account_id" : 150, "daily_mod" : [ { "timestamp" : "2016-12-01", "impressions" : 5 }, false, { "timestamp" : "2016-12-01" } ] }

如果有人能在表现方面给我一个更好的答案,那么很乐意将其标记为正确的答案。