Mongoose在聚合中使用子查询来汇总数据

时间:2017-03-29 04:30:03

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

我是猫鼬新手。我有这样的模特。

var ForumSchema = new mongoose.Schema({
User_id             : {type : Schema.Types.ObjectId, ref : 'User'},
Title               : {type : String},
Content             : {type : String},
Tags                : [{type : String}],
isPublic            : {type : Boolean, default : false},
Vote                : [{
    User_id         : {type : Schema.Types.ObjectId, ref : 'User'},
    Kind            : {type : Boolean}
}],
Rate                : [{
    User_id         : {type : Schema.Types.ObjectId, ref : 'User'},
    Count           : {type : Number}
}],
Comment             : [{
    User_id         : {type : Schema.Types.ObjectId, ref : 'User'},
    Content         : String,
    Created_at      : {type : Date, required : true, default : Date.now}
}],
Created_at          : {type : Date, required : true, default : Date.now},
Updated_at          : {type : Date}

});

我希望获得Vote之和值为true的论坛数据。像这样的json。

{
[
    _id         : <Some object id>,
    User_id     : <Some object id from User Model>,
    Title       : <Title>,
    Content     : <Content>,
    Tags        : [Some array],
    isPublic    : true,
    UpVote      : 23,
    ....
    ....
    ....
]

}

在mysql中,我可以使用子查询来完成此操作。我怎么能在猫鼬中做到这一点?

2 个答案:

答案 0 :(得分:3)

使用MongoDB服务器3.4及更高版本,您可以运行使用 $addFields 运算符的聚合管道,该运算符在{强> $filter 上{1}}数组用于过滤那些Vote属性值与true匹配的元素,当您获得过滤后的数组时,将其用作 $size 运算符的输入表达式然后计算过滤数组中项目的计数。

考虑以下操作以获得所需的结果:

Kind

说明

在上面,内部表达

Forum.aggregate([
    {
        "$addFields": {
            "UpVote": {
                "$size": {
                    "$filter": {
                        "input": "$Vote",
                        "as": "el",
                        "cond": "$$el.Kind"
                    }
                }
            }
        }
    }
]).exec((err, results) => {
    if (err) throw err;
    console.log(results);
})

根据指定的条件选择要返回的数组的子集。因此,它返回一个只包含那些符合条件的元素的数组。

{ "$filter": { "input": "$Vote", "as": "el", "cond": "$$el.Kind" } } 属性是指解析为数组的表达式。在上面,输入是Votes数组。

另一个字段 input 表示输入数组中元素的变量名称。 as 表达式通过此变量访问输入数组中的每个元素。

as 字段包含一个表达式,用于确定是否在结果数组中包含该元素。表达式通过 cond 中指定的变量名访问元素。

所以在上面,如果被评估的数组中的元素的as子属性等于Kind,由表达式true表示,则条件匹配且元素为包含在要返回的子集中。

举一个简单的例子,举个例如这个高级表达式:

"$$el.Kind"

返回数组

{
    "$filter": {
        "input": [
            { "Kind": true, "User_id": "58afed97bc343887a9ac9206" },
            { "Kind": false, "User_id": "58ad50a429b2961777f91c97" },
            { "Kind": true, "User_id": "58b3f0f598501abacd8ff391" }
        ],
        "as": "el",
        "cond": {
            "$eq": ["$$el.Kind", true]
        }
    }
}

条件部分

[
    { "Kind": true, "User_id": "58afed97bc343887a9ac9206" },
    { "Kind": true, "User_id": "58b3f0f598501abacd8ff391" }
]

可以简化为

"cond": {
    "$eq": ["$$el.Kind", true]
}

因为"cond": "$$el.Kind" 表达式已经计算为布尔值。

$size 运算符可以简单地计算数组中元素的数量,从而计算表达式,例如

"$$el.Kind"

表示为

{
    "$size":    {
        "$filter": {
            "input": [
                { "Kind": true, "User_id": "58afed97bc343887a9ac9206" },
                { "Kind": false, "User_id": "58ad50a429b2961777f91c97" },
                { "Kind": true, "User_id": "58b3f0f598501abacd8ff391" }
            ],
            "as": "el",
            "cond": {
                "$eq": ["$$el.Kind", true]
            }
        }
    }
}

并以2作为计数返回结果。

对于相反的DownVote计数,同样的逻辑适用:

{
    "$size": [
        { "Kind": true, "User_id": "58afed97bc343887a9ac9206" },
        { "Kind": true, "User_id": "58b3f0f598501abacd8ff391" }
    ]
}

对于早期的MongoDB版本3.2,您需要预测文档中的每个其他元素:

Forum.aggregate([
    {
        "$addFields": {
            "UpVote": {
                "$size": {
                    "$filter": {
                        "input": "$Vote",
                        "as": "el",
                        "cond": "$$el.Kind"
                    }
                }
            },
            "DownVote": {
                "$size": {
                    "$filter": {
                        "input": "$Vote",
                        "as": "el",
                        "cond": { "$not": ["$$el.Kind"] }
                    }
                }
            }
        }
    }
]).exec((err, results) => {
    if (err) throw err;
    console.log(results);
})

对于不支持 $filter 运算符的版本,请使用 $setDifference 运算符代替:

Forum.aggregate([
    {
        "$project": {
            "User_id"     : 1,
            "Title"       : 1,
            "Content"     : 1,
            "Tags"        : 1,
            "isPublic"    : 1,
            "UpVote"      : {
                "$size": {
                    "$filter": {
                        "input": "$Vote",
                        "as": "el",
                        "cond": "$$el.Kind"
                    }
                }
            },
            ....
            ....
            ....
        }
    }
]).exec((err, results) => {
    if (err) throw err;
    console.log(results);
})

答案 1 :(得分:0)

你可以在mongodb中使用聚合

db.getCollection('forum').aggregate([{
        $unwind: "$Vote"
    },
    {
        $match: {
            "Vote. Kind": true
        }
    },
    {
        $group: {
            _id: '$_id',
            "User_id": {
                '$first': '$User_id'
            },
            "Title": {
                '$first': '$Title'
            },
            UpVote: {
                $sum: 1
            }
        }
    }
])

你也可以在mongoose中使用同样的查询,最后你得到的票数