Mongoose:填充填充的字段

19

我正在使用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(“uarticle”)的“article”字段。

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

是否可以使用Mongoose和populate()来完成这个任务,还是我应该做其他事情?

谢谢,


我遇到了同样的问题,即引用嵌入在数组中 -> myList:[{mid:{type:Schema.ObjectId,'ref':'OtherModel'},meta:[String]}]。 当我尝试使用.populate('myList.mid')时,会产生以下错误... TypeError:无法调用未定义的“path”方法 - Greg
6个回答

15

根据我从文档中查阅和你的陈述,这不能实现,但是您可以在回调函数中自行填充“uarticle.article”文件。

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

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

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


日志集合是唯一一个具有奇怪引用的集合,我只是想这样做,因为其他集合只有一个引用(为了避免重复多次相同的数据)。我认为为了避免使用大量信息,我不会填充“uarticle”,客户端将发出请求以获取文章的详细信息,只是为了保持简单和快速。 - Francesc
1
你能否提供更多关于你回答的细节 -- 我理解得对吗,即使只有一个引用也应该被检查。我刚开始学习mongodb。毕竟,很多关系表只有一个外键。嵌入式文档是否比填充ObjectId引用更可取? - grantwparks
这真的取决于项目的结构,有时您甚至可能希望复制数据(嵌入)以提高速度,而其他时候您可能需要引用它,因为该数据仅在某些条件下被查询。 - alessioalex

6
你可以使用 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
   })
})

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



5

我曾经遇到过同样的问题,但经过数小时的努力,我找到了解决方案。而且它可以在不使用任何外部插件的情况下实现:)

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

1
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'}
     ]});

0

或者你可以直接将一个对象传递给 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) );


0

可以考虑这样做:

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 粗糙...需要源码吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接