Mongodb 为丢失的分组数据生成默认值

Mongodb 为丢失的分组数据生成默认值,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我正在尝试使用mongodb聚合框架聚合数据。我希望结果总是包括我在第2阶段匹配的所有类别,即使没有数据。缺失的数据应显示为0:与当前输出相比,我缺失了 { period: 05/2019, category: 1, subtotal: 0, period: 04/2019, category: 1, subtotal: 0, period: 03/2019, category: 3, subtotal: 0, ... } 我有以下数据结构: 我已经构建了一个聚合管道,

我正在尝试使用mongodb聚合框架聚合数据。我希望结果总是包括我在第2阶段匹配的所有类别,即使没有数据。缺失的数据应显示为0:与当前输出相比,我缺失了

{
   period: 05/2019, category: 1, subtotal: 0,
   period: 04/2019, category: 1, subtotal: 0,
   period: 03/2019, category: 3, subtotal: 0,
   ...
}
我有以下数据结构:

我已经构建了一个聚合管道,可以正确生成每个期间/类别对的小计:

输出

我已尝试使用$ifNull,$exists。。。但似乎没有得到结果。如果可能的话,我希望在Mongodb中完成大部分数据工作,但如果我想对时段/类别对进行同样的工作,可能是不可能的(角落案例:如果没有任何类别存在,我如何知道缺少的时段…),因为输出用于绘图,我需要人工生成缺少的时段

我在考虑下一步如何使用addToSet进行尝试,然后进行某种查找?这是一条前进的道路吗

更新1:(MIKL的反馈)

结果

{ "_id" : { "period" : "05/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 6 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "05/2019", "category" : 3 }, "subtotal" : 413 }
{ "_id" : { "period" : "05/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "05/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 3 }, "subtotal" : 50 }
{ "_id" : { "period" : "03/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 1 }, "subtotal" : 129.44 }
{ "_id" : { "period" : "02/2019", "category" : 3 }, "subtotal" : 31.35 }
{ "_id" : { "period" : "03/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 3 }, "subtotal" : 50 }
{ "_id" : { "period" : "01/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 1 }, "subtotal" : 132.44 }
{ "_id" : { "period" : "02/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 1 }, "subtotal" : 72.58 }
{ "_id" : { "period" : "12/2018", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 1 }, "subtotal" : 161.81 }
{ "_id" : { "period" : "11/2018", "category" : 1 }, "subtotal" : 61.54 }
{ "_id" : { "period" : "11/2018", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 3 }, "subtotal" : 62 }
{ "_id" : { "period" : "12/2018", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 1 }, "subtotal" : 92.84 }
{ "_id" : { "period" : "05/2019", "category" : 7 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 6 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 0 }, "subtotal" : 0.0 }
更新2(工作答案授予Mickl)

您需要运行两个单独的管道,然后“合并”结果。第一个是$group的常规聚合,第二个是计算所有必需的
类别/期间
值。然后,您可以运行以合并结果,并运行以重塑数据

db.exps.aggregate([
    {
        $facet: {
            grouped: [
                { $match: { category: { $in: [1,3] } } },
                { $group: { _id: { period: "$period", category: "$category" }, subtotal: { $sum: "$amount" } } }
            ],
            periods: [
                { $group: { _id: null, period: { $addToSet: "$period" } } },
                { $addFields: { category: [ 1, 3 ] } },
                { $unwind: "$category" },
                { $unwind: "$period" }
            ]
        }
    },
    {
        $project: {
            data: {
                $map: {
                    input: "$periods",
                    as: "p",
                    in: {
                        $let: {
                            vars: { 
                                group: { 
                                    $filter: { 
                                        input: "$grouped", 
                                        as: "g", 
                                        cond: { 
                                            $and: [ 
                                                { $eq: [ "$$p.period", "$$g._id.period" ] } ,
                                                { $eq: [ "$$p.category", "$$g._id.category" ] }
                                            ] 
                                        } 
                                    } 
                                } 
                            },
                            in: {
                                period: "$$p.period",
                                category: "$$p.category",
                                group: { $arrayElemAt: [ "$$group", 0 ] }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        $unwind: "$data"
    },
    {
        $replaceRoot: {
            newRoot: "$data"
        }
    },
    {
        $project: {
            period: 1,
            category: 1,
            amount: { $ifNull: [ "$group.subtotal", 0 ] }
        }
    },
    {
        $sort: {
            category: 1,
            period: 1 
        }
    }
])
输出:

{ "period" : "01/2019", "category" : 1, "amount" : 161.81 }
{ "period" : "02/2019", "category" : 1, "amount" : 129.44 }
{ "period" : "03/2019", "category" : 1, "amount" : 92.84 }
{ "period" : "04/2019", "category" : 1, "amount" : 72.58 }
{ "period" : "05/2019", "category" : 1, "amount" : 0 }
{ "period" : "12/2018", "category" : 1, "amount" : 0 }
{ "period" : "01/2019", "category" : 3, "amount" : 50 }
{ "period" : "02/2019", "category" : 3, "amount" : 31.35 }
{ "period" : "03/2019", "category" : 3, "amount" : 0 }
{ "period" : "04/2019", "category" : 3, "amount" : 50 }
{ "period" : "05/2019", "category" : 3, "amount" : 413 }
{ "period" : "12/2018", "category" : 3, "amount" : 0 }

谢谢你的帮助。不幸的是,这并不能解决全部问题。仍然缺少结果中未显示的类别值。(见更新答案)@jcuypers好的,现在我知道你在找什么了。仅针对类别执行此操作很容易,但您的
\u id
由类别和期间组成。您如何知道应该为缺少的时段返回哪些值?如果我不够清楚,很抱歉。是的,我有完全相同的想法,但我想我希望有一个奇迹般的解决方案:)因此,逻辑/数据格式的一致性需要在API服务器或客户端中完成。我认为奇迹般的解决方案存在:)如果你能编辑你的帖子并粘贴JSON数据而不是图像,检查会更容易。我会想办法的later@jcuypers,修改了我的答案
{ "_id" : { "period" : "05/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 6 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "05/2019", "category" : 3 }, "subtotal" : 413 }
{ "_id" : { "period" : "05/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "05/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 3 }, "subtotal" : 50 }
{ "_id" : { "period" : "03/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 1 }, "subtotal" : 129.44 }
{ "_id" : { "period" : "02/2019", "category" : 3 }, "subtotal" : 31.35 }
{ "_id" : { "period" : "03/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 3 }, "subtotal" : 50 }
{ "_id" : { "period" : "01/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 1 }, "subtotal" : 132.44 }
{ "_id" : { "period" : "02/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 1 }, "subtotal" : 72.58 }
{ "_id" : { "period" : "12/2018", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "02/2019", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 1 }, "subtotal" : 161.81 }
{ "_id" : { "period" : "11/2018", "category" : 1 }, "subtotal" : 61.54 }
{ "_id" : { "period" : "11/2018", "category" : 5 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 3 }, "subtotal" : 62 }
{ "_id" : { "period" : "12/2018", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 0 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "03/2019", "category" : 1 }, "subtotal" : 92.84 }
{ "_id" : { "period" : "05/2019", "category" : 7 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "01/2019", "category" : 4 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "11/2018", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "04/2019", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 2 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 6 }, "subtotal" : 0.0 }
{ "_id" : { "period" : "12/2018", "category" : 0 }, "subtotal" : 0.0 }
db.exps.aggregate([
    {
        $facet: {
            grouped: [
                { $match: { category: { $in: [1,3] } } },
                { $group: { _id: { period: "$period", category: "$category" }, subtotal: { $sum: "$amount" } } }
            ],
            periods: [
                { $group: { _id: null, period: { $addToSet: "$period" } } },
                { $addFields: { category: [ 1, 3 ] } },
                { $unwind: "$category" },
                { $unwind: "$period" }
            ]
        }
    },
    {
        $project: {
            data: {
                $map: {
                    input: "$periods",
                    as: "p",
                    in: {
                        $let: {
                            vars: { 
                                group: { 
                                    $filter: { 
                                        input: "$grouped", 
                                        as: "g", 
                                        cond: { 
                                            $and: [ 
                                                { $eq: [ "$$p.period", "$$g._id.period" ] } ,
                                                { $eq: [ "$$p.category", "$$g._id.category" ] }
                                            ] 
                                        } 
                                    } 
                                } 
                            },
                            in: {
                                period: "$$p.period",
                                category: "$$p.category",
                                group: { $arrayElemAt: [ "$$group", 0 ] }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        $unwind: "$data"
    },
    {
        $replaceRoot: {
            newRoot: "$data"
        }
    },
    {
        $project: {
            period: 1,
            category: 1,
            amount: { $ifNull: [ "$group.subtotal", 0 ] }
        }
    },
    {
        $sort: {
            category: 1,
            period: 1 
        }
    }
])
{ "period" : "01/2019", "category" : 1, "amount" : 161.81 }
{ "period" : "02/2019", "category" : 1, "amount" : 129.44 }
{ "period" : "03/2019", "category" : 1, "amount" : 92.84 }
{ "period" : "04/2019", "category" : 1, "amount" : 72.58 }
{ "period" : "05/2019", "category" : 1, "amount" : 0 }
{ "period" : "12/2018", "category" : 1, "amount" : 0 }
{ "period" : "01/2019", "category" : 3, "amount" : 50 }
{ "period" : "02/2019", "category" : 3, "amount" : 31.35 }
{ "period" : "03/2019", "category" : 3, "amount" : 0 }
{ "period" : "04/2019", "category" : 3, "amount" : 50 }
{ "period" : "05/2019", "category" : 3, "amount" : 413 }
{ "period" : "12/2018", "category" : 3, "amount" : 0 }