既然我已经度过了一个周末在$ 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个以上电话只是为了获得所有Person年龄组的信息。我希望输出看起来像这样:

    [
        {"_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我不需要ID 1517191919或其他任何东西,我想要一个可靠的名称,所以我知道所以我确实知道_id:“ $ age”不会给我想要的东西,但是我也不知道如何得到想要的东西。)

我唯一一次看到不止一件事的地方是:$ match,$ group和$ project。但是,如果$ project表示我不能使用$ sum或$ count,那么我可以执行多个$ groups,如果可以的话,它的窍门是什么?

最佳答案

至于在不同年龄组中生成结果的情况,聚合框架的$cond运算符可以在这里提供帮助。作为三元运算符,它接受逻辑结果(如果condition),并且可以在true(则)或false(否则)的地方返回值。在不同年龄段的情况下,您将“嵌套”处于else条件的呼叫以满足每个范围,直到逻辑上用尽为止。

总体而言,一次通过并不能将“性别”和“年龄”的结果都分组在一起,实际上并不可行。虽然“可以”完成,但唯一的方法基本上是将所有数据累积在数组中,然后再对其进行处理以进行后续分组。这不是一个好主意,因为在尝试保留数据时,它几乎总是会打破实际的BSON限制16MB。因此,通常需要更好的方法。

这样,在API支持的地方(您在nodejs下,就可以了),那么通常最好分别运行每个查询并合并结果。节点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将启动并行运行的任务,因此这两个任务可以同时在服务器上运行。输入数组中的每个管道都将传递给Aggregation方法,该方法随后将返回结果并将输出数组合并为最终结果。

最终结果不仅是将结果正确地键入年龄组,而且两个结果集似乎在相同的组合响应中,不需要其他任何工作来合并内容。

这不仅方便,而且并行执行使时间效率更高,并且对用于返回结果的聚合方法的负担也更少(如果不能克服的话)。

10-07 16:04
查看更多