【问题标题】:Mongoose: Populate a populated fieldMongoose:填充填充字段
【发布时间】:2012-01-22 13:21:42
【问题描述】:

我使用 MongoDB 作为我的应用程序的日志管理器,然后同步移动客户端。我在 NodeJS 中设置了这个模型:

var UserArticle = new Schema({
    date: { type: Number, default: Math.round((new Date()).getTime() / 1000) }, //Timestamp!
    user: [{type: Schema.ObjectId, ref: "User"}],
    article: [{type: Schema.ObjectId, ref: "Article"}],
    place: Number,    
    read: Number,     
    starred: Number,   
    source: String
});
mongoose.model("UserArticle",UserArticle);

var Log = new Schema({
    user: [{type: Schema.ObjectId, ref: "User"}],
    action: Number, // O => Insert, 1 => Update, 2 => Delete
    uarticle: [{type: Schema.ObjectId, ref: "UserArticle"}],
    timestamp: { type: Number, default: Math.round((new Date()).getTime() / 1000) }
});
mongoose.model("Log",Log);

当我想检索日志时,我使用以下代码:


var log = mongoose.model('Log');
log
.where("user", req.session.user)
.desc("timestamp")
.populate("uarticle")
.populate("uarticle.article")
.run(function (err, articles) {
if (err) {
    console.log(err);
        res.send(500);
    return;
}
res.json(articles);

如您所见,我希望 mongoose 填充 Log 集合中的“uarticle”字段,然后我想填充 UserArticle 的“article”字段(“uarticle”)。

但是,使用此代码,Mongoose 仅​​使用 UserArticle 模型填充“uarticle”,而不是 uarticle 内部的文章字段。

是否可以使用 Mongoose 和 populate() 来完成它,或者我应该做其他事情?

谢谢,

【问题讨论】:

  • 我遇到了同样的问题,其中引用嵌入在数组中 -> myList: [{ mid: {type:Schema.ObjectId, 'ref':'OtherModel'}, meta: [细绳]}]。当我尝试 .populate('myList.mid')... TypeError: Cannot call method 'path' of undefined 时,这会产生以下错误

标签: mongodb node.js mongoose populate


【解决方案1】:

根据我在文档中查看的内容以及从您那里听到的信息,这无法实现,但您可以在回调函数中自己填充“uarticle.article”文档。

但是我想指出另一个我认为更重要的方面。您在集合 A 中有引用集合 B 的文档,在集合 B 的文档中,您有另一个对集合 C 中文档的引用。

你要么做错了(我指的是数据库结构),要么你应该在这里使用关系数据库,比如 MySQL。 MongoDB 的强大之处在于您可以在文档中嵌入更多信息,因此必须进行较少的查询(将数据放在单个集合中)。虽然引用某些东西是可以的,但有一个引用然后另一个引用似乎并没有在这里充分利用 MongoDB。

也许您想分享您的情况和数据库结构,以便我们为您提供更多帮助。

【讨论】:

  • Logs 集合是唯一一个有奇怪引用的集合,我只是想要,因为其他集合只有 1 个引用(以避免重复很多次相同的数据)。我认为为了避免使用大量信息,我不会填充“uarticle”,客户端会请求获取文章的详细信息,只是为了让事情变得简单快捷。
  • 您能否就您的答案提供更多详细信息——我是否没看错,甚至应该检查一个参考。我刚刚开始使用 mongodb。毕竟很多关系表只有一个外键。嵌入文档是否比填充 ObjectId 引用更可取?
  • 这真的取决于项目的结构,有时您甚至可能想要复制数据(嵌入)以提高速度,有时您可能需要引用它,因为该数据将仅在某些条件。
【解决方案2】:

怎么样:

populate_deep = function(type, instance, complete, seen)
{
  if (!seen)
    seen = {};
  if (seen[instance._id])
  {
    complete();
    return;
  }
  seen[instance._id] = true;
  // use meta util to get all "references" from the schema
  var refs = meta.get_references(meta.schema(type));
  if (!refs)
  {
    complete();
    return;
  }
  var opts = [];
  for (var i=0; i<refs.length; i++)
    opts.push({path: refs[i].name, model: refs[i].ref});
  mongoose.model(type).populate(instance, opts, function(err,o){
    utils.forEach(refs, function (ref, next) {
      if (ref.is_array)
        utils.forEach(o[ref.name], function (v, lnext) {
          populate_deep(ref.ref_type, v, lnext, seen);
        }, next);
      else
        populate_deep(ref.ref_type, o[ref.name], next, seen);
    }, complete);
  });
}

meta utils 很粗糙...想要 src 吗?

【讨论】:

    【解决方案3】:

    您可以使用mongoose-deep-populate 插件来执行此操作。用法:

    User.find({}, function (err, users) {
       User.deepPopulate(users, 'uarticle.article', function (err, users) {
          // now each user document includes uarticle and each uarticle includes article
       })
    })
    

    免责声明:我是插件的作者。

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,但经过几个小时的努力我找到了解决方案。它可以不使用任何外部插件:)

          applicantListToExport: function (query, callback) {
            this
             .find(query).select({'advtId': 0})
             .populate({
                path: 'influId',
                model: 'influencer',
                select: { '_id': 1,'user':1},
                populate: {
                  path: 'userid',
                  model: 'User'
                }
             })
           .populate('campaignId',{'campaignTitle':1})
           .exec(callback);
          }
      

      【讨论】:

        【解决方案5】:

        Mongoose v5.5.5 似乎允许填充已填充的文档。

        您甚至可以提供一个包含多个字段的数组来填充已填充的文档

        var batch = await mstsBatchModel.findOne({_id: req.body.batchId})
          .populate({path: 'loggedInUser', select: 'fname lname', model: 'userModel'})
          .populate({path: 'invoiceIdArray', model: 'invoiceModel', 
             populate: [
               {path: 'updatedBy', select: 'fname lname', model: 'userModel'},
               {path: 'createdBy', select: 'fname lname', model: 'userModel'},
               {path: 'aircraftId', select: 'tailNum', model: 'aircraftModel'}
             ]});
        

        【讨论】:

          【解决方案6】:

          或者您可以简单地将 obj 传递给填充:

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

          【讨论】:

            猜你喜欢
            • 2017-05-21
            • 2013-07-06
            • 2021-09-06
            • 1970-01-01
            • 1970-01-01
            • 2015-09-19
            • 2019-01-01
            • 2019-01-31
            • 2018-12-28
            相关资源
            最近更新 更多