猫鼬加入行动

时间:2016-04-17 23:12:54

标签: node.js mongodb mongoose mongoose-populate

我有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'这是由当前登录的用户提供的。 用户可以向多个演员提供评级,演员可以从多个用户接收评级。这些将存储在评级表中。

我写了这样的东西

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'。有人可以更正我的查询

1 个答案:

答案 0 :(得分:1)

这取决于您实际发送的内容作为查询参数的输入。另外你需要了解的主要事情是 a" 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可以执行此操作的唯一方法是检索"所有" Rating个对象,然后进行"人口"使用"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内容"首先"。所以你在这种情况下真正要做的就是实际做"三"自己进行查询操作,并从ObjectIdUser模型中获取Actor值,以便将匹配应用于Rating模型:

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模型,因此您可以在此处提供"查询"在所有情况下,为了检索该数据。这些不是" JOIN"像SQL一样的操作,所以"服务器"没有查看合并后的结果,然后选择字段。

随着一点自我教育的开启"调试"了解mongoose实际上如何向服务器发出语句。然后,您将看到.populate()实际应用的方式:

mongoose.set("debug",true)