【问题标题】:Mongoose: deep population (populate a populated field)Mongoose:深人口(填充人口密集的领域)
【发布时间】:2013-09-22 22:35:11
【问题描述】:

我有Category 模型:

Category:
    ...
    articles: [{type:ObjectId, ref:'Article'}]

文章模型包含对Account model 的引用。

Article:
    ...
    account: {type:ObjectId, ref:'Account'}

因此,填充 articles 类别模型将是:

{ //category
    articles: //this field is populated
     [ { account: 52386c14fbb3e9ef28000001, // I want this field to be populated
         date: Fri Sep 20 2013 00:00:00 GMT+0400 (MSK),
         title: 'Article 1' } ],
    title: 'Category 1' }

问题是:如何填充已填充字段([articles])的子字段(帐户)?这是我现在的做法:

globals.models.Category
    .find
        issue : req.params.id
        null
        sort:
            order: 1
    .populate("articles") # this populates only article field, article.account is not populated
    .exec (err, categories) ->
        console.log categories

我知道这里讨论过:Mongoose: Populate a populated field 但没有找到真正的解决方案

【问题讨论】:

  • 就像 rroxysam 说的,.populate({path : 'userId', populate : {path : 'reviewId'}}).exec(...) 似乎是一个递归逻辑,这是有道理的。它的作品!
  • 自发布此问题以来对 Mongoose 的更新已解决此问题。这是文档:Populating across multiple levels

标签: mongodb mongoose populate


【解决方案1】:

很抱歉打破你的泡沫,但没有直接支持的解决方案。至于Github issue #601,看起来就惨了。根据3.6 release notes 的说法,开发人员似乎承认该问题对手动递归/深度填充感到满意。

因此,根据发行说明,推荐的方法是在回调中嵌套填充调用,因此在您的 exec() 函数中,在发送响应之前使用 categories.populate 进一步填充。

【讨论】:

    【解决方案2】:

    Mongoose 现在有一个新方法 Model.populate 用于深度关联:

    https://github.com/Automattic/mongoose/issues/1377#issuecomment-15911192

    【讨论】:

      【解决方案3】:

      在 3.6 中完成此操作的最简单方法是使用 Model.populate

      User.findById(user.id).select('-salt -hashedPassword').populate('favorites.things').exec(function(err, user){
          if ( err ) return res.json(400, err);
      
          Thing.populate(user.favorites.things, {
              path: 'creator'
              , select: '-salt -hashedPassword'
          }, function(err, things){
              if ( err ) return res.json(400, err);
      
              user.favorites.things = things;
      
              res.send(user.favorites);
          });
      });
      

      【讨论】:

      • 我很好奇如果 user.favorites 是一个数组,你会如何做同样的事情?
      • 同理。只要摆脱 .thing
      【解决方案4】:
      globals.models.Category.find()
        .where('issue', req.params.id)
        .sort('order')
        .populate('articles')
        .exec(function(err, categories) {
      
          globals.models.Account.populate(categories, 'articles.account', function(err, deepResults){
      
            // deepResult is populated with all three relations
            console.log(deepResults[0].articles[0].account);
      
          });
      });
      

      以下示例的灵感来自@codephobia 提出的问题,并填充了许多关系的两个级别。首先获取一个user,填充其相关orders 数组并包含每个orderDetail

      user.model.findOne()
        .where('email', '***@****.com')
        .populate('orders')
        .exec(function(err, user) {
      
          orderDetail.model.populate(user, 'orders.orderDetails', function(err, results){
      
            // results -> user.orders[].orderDetails[] 
          });
      });
      

      这在3.8.8 中工作正常,但在3.6.x 中应该工作。

      【讨论】:

        【解决方案5】:

        可能有点太晚了,但我写了一个Mongoose plugin 来在任意嵌套级别执行深度填充。注册此插件后,您只需一行即可填充类别的文章和帐户:

        Category.deepPopulate(categories, 'articles.account', cb)
        

        您还可以指定 populate options 来控制每个填充路径的 limitselect... 等内容。查看插件文档了解更多信息。

        【讨论】:

          【解决方案6】:

          首先,将 mongoose 3 更新为 4,然后使用最简单的方法在 mongoose 中进行深度填充,如下所示:

          假设您的博客架构具有 userId 作为 ref Id,然后在 User 中您有一些评论作为架构 Review 的 ref Id。所以基本上,你有三个模式: 1. 博客 2. 用户 3. 回顾

          而且,您必须从博客中查询,哪个用户拥有此博客和用户评论。 所以你可以查询你的结果:

          BlogModel
            .find({})
            .populate({
              path : 'userId',
              populate : {
                path : 'reviewId'
              }
            })
            .exec(function (err, res) {
          
            })
          

          【讨论】:

          • 如果我已经阅读了您的答案并且没有在上面的答案上浪费我一个小时的时间!
          • 感谢您展示查询的结构,而不仅仅是链接到文档。效果很好!
          • 这应该是正确的答案。谢谢@techyaura
          • 当您想要填充两个字段时,您将如何填充第二级?当我使用 select 时,它只为我返回最后一个字段:因为我只想取回二级文档中的特定字段
          • 以防万一有人想知道您是否要填充多个路径,您可以像这样传递对象数组:.populate([ { path: 'path_1', populate: { path: 'field_1' } },{ 路径:'path_2',填充:[{ 路径:'field_1'},{ 路径:'field_2'}] } ]).exec()。另请注意,在数组中提供简单的字符串不会像通常那样工作。请改用 { path: 'field' } 结构。
          【解决方案7】:

          跨多个级别填充

          假设您有一个跟踪用户朋友的用户模式。

          var userSchema = new Schema({
            name: String,
            friends: [{ type: ObjectId, ref: 'User' }]
          });
          

          Populate 可让您获取用户的朋友列表,但如果您还想要用户的朋友的朋友怎么办?指定填充选项告诉猫鼬填充所有用户朋友的朋友数组:

          User.findOne({ name: 'Val' }).populate({
              path: 'friends',
              // Get friends of friends - populate the 'friends' array for every friend
              populate: { path: 'friends' }
          });
          

          参考:http://mongoosejs.com/docs/populate.html#deep-populate

          【讨论】:

          • 谢谢你,@Lucas 你在填充指令上面的评论让我明白填充是如何工作的。
          【解决方案8】:

          这个概念是深层人口。 这里的Calendar,Subscription,User,Apartment是mongoose ODM模型的不同层次

          Calendar.find({}).populate({
                path: 'subscription_id',model: 'Subscription',
                   populate: {path: 'user_id',model: 'User',
                     populate: {path: 'apartment_id',model: 'Apartment',
                        populate: {path: 'caterer_nonveg_id',
                                    model: 'Caterer'}}}}).exec(function(err,data){ 
                                    if(!err){
                                       console.log('data all',data)
                                     }
                                     else{
                                       console.log('err err err',err)
                                      }
                             });
          

          【讨论】:

            【解决方案9】:

            如果你想选择multi populate inside populate,你应该试试这个方法:

            我有 Booking 架构:

            let Booking = new Schema({
              ...,  // others field of collection
              experience: { type: Schema.Types.ObjectId, ref: 'Experience' },
              ...},{
                collection: 'booking'
              });
            

            经验架构:

            let Experience = new Schema({
              ...,
              experienceType: {type: Schema.Types.ObjectId, ref: 'ExperienceType'},
              location: {type: Schema.Types.ObjectId, ref: 'Location'},
              ...} // others field of collection
              ,{
                collection: 'experience'
              });
            
            当您找到Booking时,

            获取ExperienceExperienceType和Location

            Booking.findOne({_id: req.params.id})
              .populate({path: 'experience',
                populate: [{path: 'experienceType', select: 'name'}, {path: 'location', select: 'name'}],
              })
              .exec((err, booking) => {
                if(err){
                  console.log(err);
                }
                else {
                  res.json(booking);
                }
              });
            

            【讨论】:

              【解决方案10】:

              或者你可以将 Object 传递给 populate 方法:

              const myFilterObj = {};
              const populateObj = {
                              path: "parentFileds",
                              populate: {
                                  path: "childFileds",
                                  select: "childFiledsToSelect"
                              },
                              select: "parentFiledsToSelect"
                             };
              Model.find(myFilterObj)
                   .populate(populateObj).exec((err, data) => console.log(data) );
              

              【讨论】:

                猜你喜欢
                • 2023-04-02
                • 2015-06-06
                • 2015-01-25
                • 2017-07-06
                • 1970-01-01
                • 2020-06-13
                • 2019-10-01
                • 2013-06-29
                • 2011-03-31
                相关资源
                最近更新 更多