Mongoose:填充填充字段

Posted

技术标签:

【中文标题】Mongoose:填充填充字段【英文标题】:Mongoose: Populate a populated field 【发布时间】: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 时,这会产生以下错误 【参考方案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) );

【讨论】:

以上是关于Mongoose:填充填充字段的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose 先填充,然后根据填充的字段进行过滤

mongoose 模式未在填充字段中注册错误

Mongoose:使用 _id 以外的字段填充路径

如何填充 Mongoose 数组中的字段?

Mongoose 切片数组,在填充字段中

Mongoose 按填充字段排序