【问题标题】:MongoDB aggreagte fill missing days [duplicate]MongoDB聚合填充缺失天数[重复]
【发布时间】:2014-12-11 23:17:02
【问题描述】:

我有一个包含以下文档的产品集合:

{ "_id" : 1, "item" : "abc", created: ISODate("2014-10-01T08:12:00Z") }
{ "_id" : 2, "item" : "jkl", created: ISODate("2014-10-02T09:13:00Z") }
{ "_id" : 3, "item" : "hjk", created: ISODate("2014-10-02T09:18:00Z") }
{ "_id" : 4, "item" : "sdf", created: ISODate("2014-10-07T09:14:00Z") }
{ "_id" : 5, "item" : "xyz", created: ISODate("2014-10-15T09:15:00Z") }
{ "_id" : 6, "item" : "iop", created: ISODate("2014-10-16T09:15:00Z") }

我想画一个图表来描述按天计算的产品数量,所以我使用 mongodb 聚合框架来按天计算产品组:

  var proj1 = {
      "$project": {
          "created": 1,
          "_id": 0,
          "h": {"$hour": "$created"},
          "m": {"$minute": "$created"},
          "s": {"$second": "$created"},
          "ml": {"$millisecond": "$created"}
      }
  };

  var proj2 = {
      "$project": {
          "created": {
              "$subtract": [
                  "$created", {
                      "$add": [
                          "$ml",
                          {"$multiply": ["$s", 1000]},
                          {"$multiply": ["$m", 60, 1000]},
                          {"$multiply": ["$h", 60, 60, 1000]}
                      ]
                  }]
          }
      }
  };

  db.product.aggregate([
          proj1,
          proj2,
          {$group: {
              _id: "$created",
              count: {$sum: 1}
          }},
          {$sort: {_id: 1}}
      ])

mongo shell 中的结果是:

{
    "result" : [ 
        {
            "_id" : ISODate("2014-10-01T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-10-02T00:00:00.000Z"),
            "count" : 2
        }, 
        {
            "_id" : ISODate("2014-10-07T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-10-15T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-10-16T00:00:00.000Z"),
            "count" : 1
        }
    ],
    "ok" : 1
}

当然,有些日子没有产品,使用上面结果集的图表如下所示:

但所需的图表应如下所示:

所以问题是:如何使用count = 0 将缺失的天数(例如过去 30 天)添加到结果集中? 这意味着,所需的结果集应该如下所示这个:

{
    "result" : [
        {
            "_id" : ISODate("2014-09-16T00:00:00.000Z"),
            "count" : 0
        }, 
        {
            "_id" : ISODate("2014-09-17T00:00:00.000Z"),
            "count" : 0
        }, 
        ...            
        {
            "_id" : ISODate("2014-10-01T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-10-02T00:00:00.000Z"),
            "count" : 2
        }, 
        {
            "_id" : ISODate("2014-10-03T00:00:00.000Z"),
            "count" : 0
        },
        ...
        {
            "_id" : ISODate("2014-10-07T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-09-08T00:00:00.000Z"),
            "count" : 0
        },
        ...
        {
            "_id" : ISODate("2014-10-15T00:00:00.000Z"),
            "count" : 1
        }, 
        {
            "_id" : ISODate("2014-10-16T00:00:00.000Z"),
            "count" : 1
        },
        // also, add some extra days 
        {
            "_id" : ISODate("2014-10-17T00:00:00.000Z"),
            "count" : 0
        },
        {
            "_id" : ISODate("2014-10-10T00:00:00.000Z"),
            "count" : 0
        }
    ],
    "ok" : 1
}

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    好的,首先:不存在的值被评估为null(大致翻译为“nada”、“nothing”、“not there”),它不等于 0,这是一个明确的定义价值。

    例如,MongoDB 对 0 和 42 之间的区别没有语义理解。那么 MongoDB 应该如何决定在一天中假设哪个值(其中 mongo 也没有语义理解)?

    基本上,您有两个选择:在没有值记录的情况下每天保存一个 0,或者在您想要创建图表的时间内在应用程序中迭代,并在每天不存在值的情况下发出 0作为替代品。我建议做前者,因为这样可以使用聚合框架。

    【讨论】:

    • “Null”在语义上更像是 SQLJSON null 而不是指针 NULL.
    • 这两者都是什么的序列化表示? ;)
    • 没什么——它们是空值,不是任何东西的序列化值,而是值本身超出正常值范围——存在细微的语义差异。
    • 这正是我的观点,不是吗? ;)
    • 好吧,没什么大不了的——只是不确定为什么您的 null 参考链接指向“Null Pointer”的文章,而不是描述 Null 概念的内容。
    【解决方案2】:

    使用 聚合 来完全处理这个问题是一件很痛苦的事情。
    但可以到达。
    (需要MongoDB V2.6+)

    var proj1 = {
        "$project" : {
            "created" : 1,
            "_id" : 0,
            "h" : {
                "$hour" : "$created"
            },
            "m" : {
                "$minute" : "$created"
            },
            "s" : {
                "$second" : "$created"
            },
            "ml" : {
                "$millisecond" : "$created"
            }
        }
    };
    
    var proj2 = {
        "$project" : {
            "created" : {
                "$subtract" : [ "$created", {
                    "$add" : [ "$ml", {
                        "$multiply" : [ "$s", 1000 ]
                    }, {
                        "$multiply" : [ "$m", 60, 1000 ]
                    }, {
                        "$multiply" : [ "$h", 60, 60, 1000 ]
                    } ]
                } ]
            }
        }
    };
    
    var group1 = {
            $group : {
                _id : "$created",
                count : {
                    $sum : 1
                }
            }
        };
    
    var group2 = {
            $group : {
                _id : 0,
                origin : {
                    $push : "$$ROOT"
                },
                maxDate : {
                    $max : "$_id"
                }
            }
    };
    
    var step = 24 * 60 * 60 * 1000; // milliseconds of one day
    
    var project3 = {
        $project : {
            origin : 1,
            extents : {
                $map : {
                    "input" : [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29],
                    "as" : "e",
                    "in" : {
                        _id : {
                            $subtract : [ "$maxDate", {
                                $multiply : [ step, "$$e"]
                            }]
                        },
                        count : {
                            $add : [0]
                        }
                    }
                }
            }
        }
    };
    
    var project4 = {
            $project : {
                _id : 0,
                values : {
                    $setUnion : [ "$origin", "$extents"]
                }
            }
    };
    
    var unwind1 = {
            $unwind : "$values"
    };
    
    var group3 = {
            $group : {
                _id : "$values._id",
                count : {
                    $max : "$values.count"
                }
            }
    };
    
    db.product.aggregate([ proj1, proj2, group1, group2, project3, project4,
            unwind1, group3, {
                $sort : {
                    _id : 1
                }
            } ]);
    

    我想在申请结束时填写这样的缺失部分供您参考:

    function sortResult(x, y) {
        var t1 = x._id.getTime();
        var t2 = y._id.getTime();
        if (t1 < t2) {
            return -1;
        } else if (t1 == t2) {
            return 0;
        } else {
            return 1;
        }
    }
    
    var result = db.product.aggregate();
    
    var endDateMilliseconds = result[result.length - 1]._id.getTime();
    var step = 24 * 60 * 60 * 1000; // milliseconds of one day
    
    var map = {};
    for (var i in result) {
        map[ result[i]._id.getTime() ] = result[i];
    }
    
    for (var ms = endDateMilliseconds, x = 1; x < 30; x++) {
        ms -= step;
        if ( ! ( ms in map ) ) {
            map[ms] = {_id : new Date(ms), count : 0};
        }
    }
    
    var finalResult = [];
    for (var x in map) {
        finalResult.push(map[x]);
    }
    finalResult.sort(sortResult);
    printjson(finalResult);
    

    【讨论】:

    • 这个问题可以更好地解释。虽然我在第一段和第二段代码中都看到了逻辑,但很多人会对它们如何一起运行感到困惑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-23
    • 2021-10-22
    • 2022-10-31
    • 1970-01-01
    • 2015-12-23
    • 2017-08-14
    • 1970-01-01
    相关资源
    最近更新 更多