node.js - 将不同分组总计合并到总输出中

标签 node.js mongodb mongoose mongodb-query aggregation-framework

现在我已经花了一个周末在 $project、aggregate() 和 $group 上敲敲打打,现在是时候让我自己接受新一轮的怜悯了。我正在尝试调用电话,获取用户的总数,按性别分组(这是更容易的部分)并按年龄范围分组(这让我很沮丧)。

我让它与一组一起工作:

        Person.aggregate([
            {
                $match: {
                    user_id: id
                }
            },
            {
                $group: {
                        _id: '$gender',
                        total: { $sum: 1 }
                }
            }
        ])
        .exec(function(err, result) {
                etc...

由此,它会在一个漂亮的 json 输出中给出有多少男性、多少女性。但如果我添加第二组,它似乎会跳过第一组,并对第二组发出嘶嘶声:

        Person.aggregate([
            {
                $match: {
                    user_id: id
                }
            },
            {
                $group: {
                        _id: '$gender',
                        total: { $sum: 1 }
                },
                $group: {
                        _id: '$age',
                        age: { $gte: 21 },
                        age: { $lte: 30 },
                        total: { $sum: 1 }
                }
            }
        ])
        .exec(function(err, result) {
                etc...

它不喜欢 $gte 或 $lte。如果我将其切换到 $project,那么它将执行 gte/lte,但抛出适合 $sum 或 $count 的信息。最重要的是,我在任何地方都找不到任何有关如何构造多请求返回的示例。这只是“这是一件事”,但我不想仅仅为了获取所有人员年龄组而调用 12 个以上的电话。我希望输出看起来像这样:

    [
        {"_id":"male","total":49},
        {"_id":"woman","total":42},
        {"_id":"age0_10", "total": 1},
        {"_id":"age11_20", "total": 5},
        {"_id":"age21_30", "total": 15}
    ]

(我不知道如何使年龄的 _id 成为实际年龄以外的东西,这没有意义,b/c 我不想要 1517191919 的 id 或其他什么,我想要一个可靠的名称所以我知道在模板中的哪里输出它。所以我知道 _id: "$age"不会给我我想要的东西,但我也不知道如何得到我想要的东西。)

我唯一一次看到不止一件事,那就是 $match、$group 和 $project。但是如果 $project 意味着我不能使用 $sum 或 $count,我可以做多个 $groups,如果可以的话,有什么技巧呢?

最佳答案

对于不同年龄段的结果,$cond聚合框架的运算符可以在这里提供帮助。作为三元运算符,它接受逻辑结果( if 条件 ),并且可以返回一个值 where true ( then ) 或 else where false ( else )。对于不同年龄组的情况,您可以将调用“嵌套”在 else 条件中以满足每个范围,直到逻辑上耗尽为止。

整体情况在分组中同时包含“性别”和“年龄”的结果并不实际。虽然“可以”做到这一点,但唯一的方法基本上是累积数组中的所有数据,并为后续分组再次计算出来。这不是一个好主意,因为在尝试保留数据时,它几乎总是会打破 16MB 的实际 BSON 限制。因此通常需要更好的方法。

因此,如果 API 支持(您在 Nodejs 下,所以它支持),那么通常最好单独运行每个查询并组合结果。 Node async库有这样的功能:

async.concat(
    [
        // Gender aggregator
        [
            { "$group": {
                "_id": "$gender",
                "total": { "$sum": 1 }
            }}
        ],
        // Age aggregator
        [
            { "$group": {
                "_id": {
                    "$cond": {
                        "if": { "$lte": [ "$age", 10 ] },
                        "then": "age_0_10",
                        "else": {
                            "$cond": {
                               "if": { "$lte": [ "$age", 20 ] },
                               "then": "age_11_20",
                               "else": {
                                   "$cond": {
                                       "if": { "$lte": [ "$age", 30 ] },
                                       "then": "age_21_30",
                                       "else": "age_over_30"
                                   }
                               }
                            }
                        }
                    }
                },
                "total": { "$sum": 1 }
            }}
        ]
    ],
    function(pipeline,callback) {
        Person.aggregate(pipeline,callback);
    },
    function(err,results) {
        if (err) throw err;
        console.log(results);
    }
);

默认执行async.concat这里将启动任务并行运行,因此两者可以同时在服务器上运行。输入数组中的每个管道都将传递给聚合方法,该方法将返回结果并将输出数组合并到最终结果中。

最终结果不仅是您得到了与年龄组完美匹配的结果,而且两个结果集似乎处于相同的组合响应中,无需进行其他工作即可合并内容。

这不仅方便,而且并行执行使得时间效率更高,并且在用于返回结果的聚合方法上减少了负担(如果不是不可能的话)。

关于node.js - 将不同分组总计合并到总输出中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34279871/

相关文章:

javascript - bootstrappedUser - Jade 或 EJS 到 HTML

c - mongodb 作为循环数据库

node.js - mongodb mongoose 忽略查询中的当前用户

javascript - javascript 中对 undefined object 的引用

mysql - Typeorm 列类型依赖于数据库

mongodb - 带有自定义标签的Docker图像提取

javascript - Mongoose 在嵌套模式中查找

javascript - Mongoose near(...) 对 2dsphere 索引字段的查询未返回有效结果

node.js - 为 Sails.js 应用程序初始化 MongoDB 数据库和用户的推荐方法是什么?

mongodb - 删除 MongoDB 数据库中的集合