【问题标题】:Mongoose using subquery in aggregation to sum dataMongoose 在聚合中使用子查询对数据求和
【发布时间】:2017-03-29 16:08:14
【问题描述】:

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

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 的论坛数据,该值为真。喜欢这个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 中,我可以通过使用子查询来做到这一点。我如何在猫鼬中做到这一点?

【问题讨论】:

  • 告诉我们您的尝试

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


【解决方案1】:

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

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

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"
    }
}

根据指定条件选择要返回的数组子集。结果,它返回一个数组,其中仅包含符合条件的元素。

input 属性是指解析为数组的表达式。在上面,输入是 Votes 数组。

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

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

所以在上面如果正在评估的数组中的元素具有等于trueKind 子属性,用表达式"$$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"

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

$size 运算符简单地计算数组中元素的数量,因此表达式,例如

{
    "$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]
            }
        }
    }
}

表示为

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

并返回一个计数为 2 的结果。

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

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);
})

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

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);
})

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

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

【讨论】:

  • 这是我认为的工作。但这意味着什么? "as": "el", "cond": "$$el.Kind"
  • 我已经添加了一些关于幕后发生的事情的解释,但有关更多详细信息,请咨询docs
  • 谢谢,这是可行的。我只是这样运行它"$filter": { "input": "$Vote", "as": "vote", "cond": { "$eq": [ "$$vote.Kind", true ] } }
  • 太棒了,它可能是语法糖,但表达式 "$filter": { "input": "$Vote", "as": "vote", "cond": { "$eq": [ "$$vote.Kind", true ] } } 可以简化为 "$filter": { "input": "$Vote", "as": "vote", "cond": "$$vote.Kind" },因为 "$$vote.Kind" 表达式已经计算为布尔值。
【解决方案2】:

你可以在 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 中使用相同的查询,最后您会获得赞成票数

【讨论】:

  • 如果我想用这个显示DownVote,这可能吗?
猜你喜欢
  • 1970-01-01
  • 2020-08-02
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 2016-11-12
  • 1970-01-01
相关资源
最近更新 更多