2016-04-17 8 views
1

У меня есть 3 схемы как ниже:Mongoose присоединиться к операции

пользователя

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

я получил только актер в результате. объект actor не содержит «userRating». может кто-то исправить мой запрос

ответ

1

Это зависит от того, что вы действительно отправляете в качестве входных данных для параметров запроса здесь. Также главное, что вам нужно понять, это то, что это не «ПРИСОЕДИНЯЙТЕСЬ», но на самом деле отдельные запросы выдаются программным слоем mongoose, поэтому существуют различные различия в обращении.

В базовом случае, когда «значения», поставляемые в качестве параметров, на самом деле являются значениями ссылок, вы фактически просто хотите их непосредственно в основном «запросе», а не в аргументах действия .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 содержания «первый».Так что вы на самом деле означает сделать в этом случае на самом деле сделать «три» операции запроса самостоятельно, и получать ObjectId значения из обоих User и 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 операцию вместо того, чтобы выполнить «СТАЛ» на «сервере» вместо:

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) 

 Смежные вопросы

  • Нет связанных вопросов^_^