在查找/聚合之后填写具有默认值的文档

时间:2014-07-15 07:15:03

标签: mongodb

我有一个集合:

{ "name" : "A", "value" : 1, "date" : ISODate("2014-01-01T00:00:00.000Z") }
{ "name" : "B", "value" : 7, "date" : ISODate("2014-01-01T00:00:00.000Z") }
{ "name" : "A", "value" : 3, "date" : ISODate("2014-01-02T00:00:00.000Z") }
{ "name" : "B", "value" : 8, "date" : ISODate("2014-01-02T00:00:00.000Z") }
{ "name" : "B", "value" : 8, "date" : ISODate("2014-01-03T00:00:00.000Z") }
{ "name" : "A", "value" : 5, "date" : ISODate("2014-01-04T00:00:00.000Z") }
{ "name" : "A", "value" : 4, "date" : ISODate("2014-01-05T00:00:00.000Z") }

2014年1月3日的A文件不可用。当我在A上进行查找/聚合时,我希望文档在我的结果集中显示为默认值(或更好,值与上一个日期相同)。例如:

{ "name" : "A", "value" : 1, "date" : ISODate("2014-01-01T00:00:00.000Z") }
{ "name" : "A", "value" : 3, "date" : ISODate("2014-01-02T00:00:00.000Z") }
{ "name" : "A", "value" : 3 (or default value -1), "date" : ISODate("2014-01-03T00:00:00.000Z") }
{ "name" : "A", "value" : 5, "date" : ISODate("2014-01-04T00:00:00.000Z") }
{ "name" : "A", "value" : 4, "date" : ISODate("2014-01-05T00:00:00.000Z") }

如何做到这一点?

1 个答案:

答案 0 :(得分:5)

为了能够在聚合框架中执行此操作,您需要的一件事是您希望报告涵盖的日期数组。例如,对于您显示的输入,您可能有一个数组:

days = [ ISODate("2014-01-01T00:00:00Z"), ISODate("2014-01-02T00:00:00Z"), 
         ISODate("2014-01-03T00:00:00Z"), ISODate("2014-01-04T00:00:00Z"), 
         ISODate("2014-01-05T00:00:00Z"), ISODate("2014-01-06T00:00:00Z") ];

表示您希望这六天中的每一天都有代表。

以下是您要运行的聚合:

db.coll.aggregate( [
     {$group : {_id:{name:"$name",date:"$date"},value:{$sum:"$value"}}},
     {$group : {_id:"$_id.name", days:{$addToSet:"$_id.date"},docs:{$push:"$$ROOT"}}},
     {$project : {missingDays:{$setDifference:[days,"$days"]},docs:1}},
     {$unwind : "$missingDays"},
     {$unwind : "$docs"},
     {$group : { 
          _id:"$_id", 
          days:{$addToSet:{date:"$docs._id.date",value:"$docs.value"}},
          missingDays:{$addToSet:{date:"$missingDays",value:{$literal:0}}}
     } }, 
     {$project : {_id:0, name:"$_id", date:{$setUnion:["$days","$missingDays"]}}},
     {$unwind : "$date"},
     {$sort : {date:1,name:1}}
] )

在您的样本输入上,如上所述定义天数,输出:

{ "name" : "A", "date" : { "date" : ISODate("2014-01-01T00:00:00Z"), "value" : 1 } }
{ "name" : "A", "date" : { "date" : ISODate("2014-01-02T00:00:00Z"), "value" : 3 } }
{ "name" : "A", "date" : { "date" : ISODate("2014-01-03T00:00:00Z"), "value" : 0 } }
{ "name" : "A", "date" : { "date" : ISODate("2014-01-04T00:00:00Z"), "value" : 5 } }
{ "name" : "A", "date" : { "date" : ISODate("2014-01-05T00:00:00Z"), "value" : 4 } }
{ "name" : "A", "date" : { "date" : ISODate("2014-01-06T00:00:00Z"), "value" : 0 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-01T00:00:00Z"), "value" : 7 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-02T00:00:00Z"), "value" : 8 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-03T00:00:00Z"), "value" : 8 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-04T00:00:00Z"), "value" : 0 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-05T00:00:00Z"), "value" : 0 } }
{ "name" : "B", "date" : { "date" : ISODate("2014-01-06T00:00:00Z"), "value" : 0 } }

在您的情况下,可能不需要第一个小组阶段 - 如果有多个文档具有相同的名称和日期,那么在这种情况下您需要为它们添加值。第二个$ group和$ project阶段计算出每个名称的天数与您想要覆盖的天数之间的差异,创建missingDays,它将在下一个$group阶段获得值0 。该小组阶段为每个name创建一个日期数组,其中包含数据和缺少日期的数组。它以类似的方式构造它们,以便后面的$project阶段可以使用$setUnion运算符创建它们的并集。在此之后,剩下的就是$unwind日期数组,并按照您想要的方式对其进行排序。