【问题标题】:Mongoose join operation猫鼬加入操作
【发布时间】:2023-03-29 07:35:02
【问题描述】:

我有 3 个架构,如下所示:

用户

    var UserSchema = new Schema({

        name: String

    });

演员

var ActorSchema = new Schema({

    name: String

});

评分

var RatingSchema = new Schema({

    actor: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Actor'
    },
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Actor'
    },
    userRating: Number

});

我想将所有演员信息发送到前端,例如 [actor1, actor2 ...]。 每个演员都包含演员详细信息和由当前登录的用户给出的“userRating”。 一个用户可以给多个演员评分,一个演员可以从多个用户那里获得评分。这些将存储在 Ratings 表中。

我写了这样的东西

Actor
        .find({})  // get all actors and populate userRating into each actor
        .populate({
            path: 'userRating',
            model: 'Rating',
            match: { actor: {$eq: req.actor}, user: {$eq: req.user}},
            select: 'userRating'
        })
        .exec(function(error, actors){
            if(error)
                res.status(501).json({error: error});
            else
                res.json(actors);
        });

结果中只有演员。演员对象不包含“userRating”。有人可以更正我的查询

【问题讨论】:

    标签: node.js mongodb mongoose mongoose-populate


    【解决方案1】:

    这取决于您在此处实际发送的作为查询参数输入的内容。此外,您需要了解的主要内容是,这不是“JOIN”,而是由mongoose 软件层发出的单独查询,因此在处理上存在明显差异。

    在作为参数提供的“值”实际上是引用的ObjectId 值的基本情况下,您实际上只希望这些直接在主“查询”中而不是.populate() 操作的参数(这实际上是发生“其他查询”的地方)。

    此外,您的“关系/参考”位于 Rating 模型中,因此您的查询将在此处发出:

    Rating.find({
      "actor": req.actor,
      "user": req.user
    }).populate("actor user").exec(function(err,ratings) {
        // Matched ratings by actor and user supplied
    }) 
    

    如果您的参数是每个对象的 "name" 数据,那么由于该信息在填充之前不会出现在 Rating 模型中,mongoose 可以做到这一点的唯一方法是检索“所有”@ 987654330@ 对象,然后使用"match" 标准进行“填充”,最后过滤掉由于不匹配项而导致填充为null 的任何结果:

    Rating.find().populate([
        { "path": "actor", "match": { "name": req.actor } },
        { "path": "user", "match": { "name": req.user } }
    ]).exec(function(err,ratings) {
        // Now filter out the null results
        ratings = ratings.filter(function(rating) {
            return ( rating.actor != null && rating.user != null )
        });
        // Then work with filtered data
    })
    

    当然,这是非常低效的,因为这是一个“客户端”操作,并且您正在“首先”提取所有 Rating 内容。因此,在这种情况下,您真正​​要做的是自己实际执行“三个”查询操作,并从UserActor 模型中获取ObjectId 值,以便将匹配应用于@987654338改为@模型:

    async.parallel(
        {
           "user": function(callback) {
               User.findOne({ "name": req.user },callback)
           },
           "actor": function(callback) {
               Actor.findOne({ "name": req.actor },callback)
           }
        },
        function(err,data) {
           // Use returned _id values in query
           Rating.find({
               "actor": data.actor._id,
               "user": data.user._id
           }).populate("actor user").exec(err,ratings) {
               // populated Rating results
           });
        }
    )
    

    然后查询解析您实际需要的“唯一”ObjectId 值,Rating 上的最终查询仅检索那些实际匹配条件的结果,而不是所有内容并执行“后过滤”操作。

    作为最后一种方法,如果您有可用的 MongoDB 3.2,那么您可以交替使用 $lookup 操作来代替在“服务器”上执行“JOINS”:

    Rating.aggregate(
      [
        { "$lookup": {
          "from": "users",
          "localField": "user",
          "foreignField": "_id",
          "as": "user"
        }},
        { "$unwind": "$user" },
        { "$match": { "user.name": req.user } },
        { "$lookup": {
          "from": "actors",
          "localField": "actor",
          "foreignField": "_id",
          "as": "actor"
        }},
        { "$unwind": "actor" },
        { "$match": { "actor.name": req.actor } }
      ],
      function(err,ratings) {
          // populated on the server in one request
      }
    )
    

    从“客户端”的角度来看,这只是“一个”请求和响应,而不是 .populate() 所做的。但它实际上只不过是之前提出的“客户端”逻辑的“服务器”端再现。

    因此,如果通过 "name" 的值进行查找,则应改为使用“三”查询方法以获得最佳性能,因为聚合版本实际上仍然在处理比它需要的更多的数据。

    当然,“最佳”观点是简单地使用 ObjectId 值开始。

    当然,这里的主要内容是 "userRating" 之类的信息属于 Rating 模型,因此您在所有情况下都提供“查询”以检索该数据。这些不是 SQL 中的“JOIN”操作,因此“服务器”不会查看组合结果然后选择字段。

    作为一些自我教育,打开“调试”以查看mongoose 实际上是如何向服务器发出语句的。然后你会看到.populate()是如何实际应用的:

    mongoose.set("debug",true)
    

    【讨论】:

      猜你喜欢
      • 2015-03-03
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 2019-08-28
      • 2023-01-27
      • 2018-05-21
      • 2018-05-25
      • 2014-07-15
      相关资源
      最近更新 更多