node.js - 填写记录中缺失的日期

标签 node.js mongodb mongoose aggregation-framework aggregation

我有一组 ProductViews:

{
    productId: "5b8c0f3204a10228b00a1745",
    createdAt: "2018-09-07T17:18:40.759Z"
}

我有一个获取特定产品每日浏览量的查询:

ProductView.aggregate([
    { $match: { productId } },
    { $project: { day: { $substr: ["$createdAt", 0, 10] } } },
    {
        $group: {
            _id: "$day",
            count: { $sum: 1 },
            time: { $avg: "$createdAt" },
        }
    },
    { $sort: { _id: 1 } },
    {
        $project: {
            date: '$_id',
            views: '$count',
        },
    },
]).exec((err, result) => ...)

当前给出:

[
    { date: '2018-09-01', views: 1 },
    { date: '2018-09-02', views: 3 },
    { date: '2018-09-04', views: 2 },
    { date: '2018-09-05', views: 5 },
    // ...
]

问题:

问题是,此聚合不会返回 { date: '2018-09-03', views: 0 } 对于 0 View 的日子。这会导致数据显示不正确:[![在此处输入图像描述][1]][1]

结果应如下所示:

[
    { date: '2018-09-01', views: 1 },
    { date: '2018-09-02', views: 3 },
    { date: '2018-09-03', views: 0 }, <=
    { date: '2018-09-04', views: 2 },
    { date: '2018-09-05', views: 5 },
    // ...
]

P.S.: 传入开始和结束日期,根据这个范围输出结果就完美了 [1]: /image/uHPBs.png

最佳答案

您需要几个额外的阶段来返回默认值。首先,您需要使用 $group 并将 _id 设置为 null 以将所有结果收集到一个文档中。然后你可以使用$map以天数数组作为输入。在那个 $map 里面你可以使用 $indexOfArray查找该日期是否存在于您当前的结果集中。如果是 (index != -1),那么您可以返回该值,否则您需要返回默认子文档并将 views 设置为 0。然后你可以使用$unwind取回文档列表和$replaceRoot将嵌套的 stats 提升到顶层。

ProductView.aggregate([
    { $match: { productId: '5b8c0f3204a10228b00a1745' } },
    { $project: { day: { $substr: ["$createdAt", 0, 10] } } },
    {
        $group: {
            _id: "$day",
            count: { $sum: 1 },
            time: { $avg: "$createdAt" },
        }
    },
    { $sort: { _id: 1 } },
    {
        $project: {
            date: '$_id',
            views: '$count',
        },
    },
    {
        $group: {
            _id: null,
            stats: { $push: "$$ROOT" }
        }
    },
    {
        $project: {
            stats: {
                $map: {
                    input: [ "2018-09-01", "2018-09-02", "2018-09-03", "2018-09-04", "2018-09-05" ],
                    as: "date",
                    in: {
                        $let: {
                            vars: { dateIndex: { "$indexOfArray": [ "$stats._id", "$$date" ] } },
                            in: { 
                                $cond: {
                                    if: { $ne: [ "$$dateIndex", -1 ] },
                                    then: { $arrayElemAt: [ "$stats", "$$dateIndex" ] },
                                    else: { _id: "$$date", date: "$$date", views: 0 }
                                } 
                            }
                        }
                    }
                }
            }
        }
    },
    {
        $unwind: "$stats"
    },
    {
        $replaceRoot: {
            newRoot: "$stats"
        }
    }
]).exec((err, result) => ...)

您可以使用简单循环在应用程序逻辑中生成静态日期列表。我相信这在 MongoDB 中也是可能的(使用 $range ),但它可能会使这个聚合管道复杂化。如果您对此感到满意,或者您想尝试在 MongoDB 中生成该日期数组,请告诉我。

关于node.js - 填写记录中缺失的日期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52235027/

相关文章:

Node.js Socket.IO 监听房间连接的最佳方式?

javascript - 如何通过 NodeJS 将文件上传到 S3 并在浏览器中跟踪进度?

javascript - 如何检查文件名字符串不是文件路径

javascript - 我收到重复 key 错误 -- MongoError : E11000 duplicate key error collection. 我确实有 2 个集合、用户和个人资料。

node.js - 如何在控制台中打印 mongoose find() 数组文档

mongodb - 如何在复杂的 Collection-Object 中附加新文档

node.js - 从 Mongoose 的两个集合中映射和获取数据

node.js - mongoDB + Mongoose : choosing the right schema for hierarchical data

node.js - Mongoose 聚合以获取平均评分,对每个评分进行计数并返回实际评分

node.js - 使用 node.js、mongoose、gridfs-stream 读取文件