mongodb中的condtitional聚合函数

时间:2016-10-11 07:14:09

标签: javascript mongodb mongoose aggregation-framework mongodb-aggregation

我有一个mongodb,其数据为

{
  "_id": "a",
  "reply": "<",
  "criterion": "story"
},
{
  "_id": "b",
  "reply": "<",
  "criterion": "story"
},
{
  "_id": "c",
  "reply": ">",
  "criterion": "story"
}

我希望结果如下:

 {
   "criterion": "story",
   "result" : {
                ">" : 1,
                "<" : 2
              } 
 }

我想聚合“标准”。所以,如果我这样做,将有1个文件。但是,我想计算“&lt;”的数量和“&gt;”并在新密钥中写入,如上面的json所示。这就是背后的逻辑。任何对mongodb有好主意的人都可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

您需要使用聚合框架,在该框架中运行具有 $group 运算符管道阶段的聚合管道,该管道阶段聚合文档以使用累加器运算符 $sum

对于所需的结果,您需要使用像 $cond 这样的tenary运算符来创建独立的计数字段,因为这会将文档数量提供给 {{ 3}} 表达式取决于名称值。 $sum 运算符可以有效地用于根据reply字段值评估计数。它将逻辑条件作为其第一个参数(if),然后返回第二个参数,其中评估为真(然后)或第三个参数,其中false(else)。这会将true/false布尔评估的返回值转换为1和0,分别输入 $cond

"$cond": [
    { "$eq": ["$reply", ">"] },
    1, 0
]

因此,如果正在处理的文档中"$reply"字段的值为">",则 $sum 运算符会将值1提供给 $cond 否则它会将零值相加。

使用 $sum 作为最后的管道步骤,因为它允许您重塑流中的每个文档,包含,排除或重命名字段,注入计算字段,创建子文档字段,使用数学表达式,日期,字符串和/或逻辑(比较,布尔,控制)表达式。它类似于SQL中的SELECT

以下管道应该返回所需的结果:

Model.aggregate([
    {
        "$group": {
            "_id": "$criterion",
            ">": {
                "$sum": {
                    "$cond": [ 
                        { "$eq": [ "$reply", ">" ] }, 
                        1, 0 
                    ]
                }
            },
            "<": {
                "$sum": {
                    "$cond": [ 
                        { "$eq": [ "$reply", "<" ] }, 
                        1, 0 
                    ]
                }
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "criterion": "$_id",
            "result.>": "$>",
            "result.<": "$<"
        }
    }
]).exec(function(err, result) {
    console.log(JSON.stringify(result, null, 4));
});

示例控制台输出

{
    "criterion" : "story",
    "result" : {
        ">" : 1,
        "<" : 2
    }
}

注意:此方法考虑到$reply字段的值已修复且已知,因此在值为动态且未知的情况下,它不灵活。

对于执行速度比上述速度快得多的更灵活的替代方案,具有更好的性能并且还考虑了计数字段的未知值,我建议按如下方式运行管道:

Model.aggregate([
    { 
        "$group": {
            "_id": {
                "criterion": "$criterion",
                "reply": "$reply"
            },
            "count": { "$sum": 1 }
        }
    },
    { 
        "$group": {
            "_id": "$_id.criterion",
            "result": {
                "$push": {
                    "reply": "$_id.reply",
                    "count": "$count"
                }
            }
        }
    }
]).exec(function(err, result) {
    console.log(JSON.stringify(result, null, 4));
});

示例控制台输出

{
    "_id" : "story",
    "result" : [ 
        {
            "reply" : "<",
            "count" : 2
        }, 
        {
            "reply" : ">",
            "count" : 1
        }
    ]
}