我可以在填充猫鼬之前使用填充吗? [英] Can I use populate before aggregate in mongoose?

查看:56
本文介绍了我可以在填充猫鼬之前使用填充吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个模型,一个是用户

I have two models, one is user

 userSchema = new Schema({
     userID: String,
     age: Number
 });

另一个是每天为所有用户记录的分数

and the other is the score recorded several times everyday for all users

 ScoreSchema = new Schema({
     userID: {type: String, ref: 'User'},
     score: Number,
     created_date = Date,
     ....
 })

我想对满足特定要求的某些用户的分数进行一些查询/计算,例如,我想计算每天大于20天的所有用户的分数平均值.

I would like to do some query/calculation on the score for some users meeting specific requirement, say I would like to calculate the average of score for all users greater than 20 day by day.

我的想法是,首先对得分进行填充来填充用户的年龄,然后对汇总进行操作.

My thought is that firstly do the populate on Scores to populate user's ages and then do the aggregate after that.

类似

Score.
    populate('userID','age').
    aggregate([
        {$match: {'userID.age': {$gt: 20}}},
        {$group: ...},
        {$group: ...}
    ], function(err, data){});

可以在聚合之前使用填充吗?还是我首先找到所有符合要求的userID并将它们保存在数组中,然后使用$ in匹配得分文档?

Is it Ok to use populate before aggregate? Or I first find all the userID meeting the requirement and save them in a array and then use $in to match the score document?

推荐答案

不,您不能在.aggregate()之前呼叫.populate(),这是您不能这样做的一个很好的理由.但是您可以采取不同的方法.

No you cannot call .populate() before .aggregate(), and there is a very good reason why you cannot. But there are different approaches you can take.

.populate()方法在客户端"工作,底层代码实际上执行附加查询(或更准确地说是$in查询),以从引用的集合中查找"指定的元素.

The .populate() method works "client side" where the underlying code actually performs additional queries ( or more accurately an $in query ) to "lookup" the specified element(s) from the referenced collection.

相反,.aggregate()是服务器端"操作,因此您基本上不能操纵客户端"内容,然后稍后将这些数据提供给聚合管道阶段.所有这些都必须存在于您正在操作的集合中.

In contrast .aggregate() is a "server side" operation, so you basically cannot manipulate content "client side", and then have that data available to the aggregation pipeline stages later. It all needs to be present in the collection you are operating on.

MongoDB 3.2和更高版本可以通过 $lookup 聚合管道操作.在这种情况下,最好还是从User集合处理,以缩小选择范围:

A better approach here is available with MongoDB 3.2 and later, via the $lookup aggregation pipeline operation. Also probably best to handle from the User collection in this case in order to narrow down the selection:

User.aggregate(
    [
        // Filter first
        { "$match": {
            "age": { "$gt": 20 } 
        }},
        // Then join
        { "$lookup": {
            "from": "scores",
            "localField": "userID",
            "foriegnField": "userID",
            "as": "score"
        }},
        // More stages
    ],
    function(err,results) {

    }
)

基本上,这将在User对象中包含一个新字段得分",作为在查找"中匹配到另一个集合的项目的数组":

This is basically going to include a new field "score" within the User object as an "array" of items that matched on "lookup" to the other collection:

{
    "userID": "abc",
    "age": 21,
    "score": [{
        "userID": "abc",
        "score": 42,
        // other fields
    }]
}

结果始终是一个数组,因为一般的预期用法是可能的一对多"关系的左连接".如果没有匹配的结果,那么它只是一个空数组.

The result is always an array, as the general expected usage is a "left join" of a possible "one to many" relationship. If no result is matched then it is just an empty array.

要使用内容,只需以任何方式使用数组即可.例如,您可以使用 $arrayElemAt 运算符以便只需在以后的任何操作中获取数组的单个第一个元素即可.然后,您可以像使用任何普通的嵌入式字段一样使用内容:

To use the content, just work with an array in any way. For instance, you can use the $arrayElemAt operator in order to just get the single first element of the array in any future operations. And then you can just use the content like any normal embedded field:

        { "$project": {
            "userID": 1,
            "age": 1,
            "score": { "$arrayElemAt": [ "$score", 0 ] }
        }}

如果没有可用的MongoDB 3.2,则处理受另一个集合的关系限制的查询的另一种选择是首先从该集合中获取结果,然后使用

If you don't have MongoDB 3.2 available, then your other option to process a query limited by the relations of another collection is to first get the results from that collection and then use $in to filter on the second:

// Match the user collection
User.find({ "age": { "$gt": 20 } },function(err,users) {

    // Get id list      
    userList = users.map(function(user) {
       return user.userID;
    });

    Score.aggregate(
        [ 
            // use the id list to select items
            { "$match": {
                "userId": { "$in": userList }
            }},
            // more stages
        ],
        function(err,results) {

        }
    );

});

因此,通过从另一个集合中获取有效用户列表到客户端,然后在查询中将其提供给另一个集合,是在较早版本中实现此目标的唯一方法.

So by getting the list of valid users from the other collection to the client and then feeding that to the other collection in a query is the onyl way to get this to happen in earlier releases.

这篇关于我可以在填充猫鼬之前使用填充吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆