Mongoose 填充子子文档

Posted

技术标签:

【中文标题】Mongoose 填充子子文档【英文标题】:Mongoose populate sub-sub document 【发布时间】:2014-08-16 09:19:40 【问题描述】:

我的 MongoDB 中有这个设置

项目:

title: String
comments: [] // of objectId's

评论:

user: ObjectId()
item: ObjectId()
comment: String

这是我的 Mongoose 架构:

itemSchema = mongoose.Schema(
    title: String,
    comments: [ type: Schema.Types.ObjectId, ref: 'comments' ],
);

Item = mongoose.model('items', itemSchema);

commentSchema = mongoose.Schema(
    comment: String,
    user:  type: Schema.Types.ObjectId, ref: 'users' ,
);

Comment = mongoose.model('comments', commentSchema);

这是我与 cmets 一起获得物品的地方:

Item.find().populate('comments').exec(function(err, data)
    if (err) return handleError(err);
    res.json(data);
);

如何使用其各自的用户填充 cmets 数组?既然每条评论都有一个用户ObjectId()?

【问题讨论】:

您可以分两个阶段进行操作,如该问题的已接受答案所示:***.com/questions/19222520/… 【参考方案1】:

另一种方法(更简单):

Item
  .find()
  .populate(
	path:     'comments',			
	populate:  path:  'user',
		    model: 'users' 
  )
  .exec(function(err, data)
    if (err) return handleError(err);
    res.json(data);
);

【讨论】:

感谢您的回答。你的回答很棒。 顺便说一下,不需要第一次填充 这是在 4 小时的研究中对我有用的独特答案,恭喜! > 更容易并帮助解决我的问题!非常感谢!【参考方案2】:

作为对结果对象调用填充的完整示例:

Item.find().populate("comments").exec(function(err,data) 
    if (err) return handleError(err);

    async.forEach(data,function(item,callback) 
        User.populate(item.comments, "path": "user" ,function(err,output) 
            if (err) throw err; // or do something

            callback();
        );
    , function(err) 
        res.json(data);
    );

);

从模型调用的表单中对.populate() 的调用将文档或数组作为其第一个参数。因此,您循环遍历每个项目的返回结果,并以这种方式在每个“cmets”数组上调用填充。 “路径”告诉函数它匹配的是什么。

这是使用 forEach 的“异步”版本完成的,因此它是非阻塞的,但通常在所有操作之后,响应中的所有项目不仅填充有 cmets,而且 cmets 本身具有相关的“用户”详情。

【讨论】:

这很有帮助。谢谢! @neil Lunn 我遇到了错误。你能帮我解决这个问题吗? async.forEach(data,function(item,callback) ^ ReferenceError: async is not defined @FarheenP - 使用npm install async 安装它,然后您可以通过var async = require('async'); 包含它。见github.com/caolan/async【参考方案3】:

更简单

Item
  .find()
  .populate(
    path: 'comments.user',
    model: 'users' 
  )
  .exec(function(err, data)
    if (err) return handleError(err);
    res.json(data);
);

【讨论】:

【参考方案4】:

要添加人们可能希望使用的最终方法来仅从子文档中选择特定字段,您可以使用以下“选择”语法:

  Model.findOne( _id: 'example' )
    .populate( 
      path: "comments", // 1st level subdoc (get comments)
      populate:  // 2nd level subdoc (get users in comments)
        path: "user",
        select: 'avatar name _id'// space separated (selected fields only)
      
    )
    .exec((err, res) =>  
        // etc
     );

【讨论】:

【参考方案5】:

填充子子文档并从多个模式中填充

ProjectMetadata.findOne(id:req.params.prjId)
.populate(
    path:'tasks',
    model:'task_metadata',
    populate:
        path:'assigned_to',
        model:'users',
        select:'name employee_id -_id' // to select fields and remove _id field

    
)
.populate(
    path:'client',
    model:'client'
)
.populate(
    path:'prjct_mgr',
    model:'users'
)
.populate(
    path:'acc_exec',
    model:'users'
)
.populate(
    path:'prj_type',
    model:'project_type'
).then ( // .. your thing

或者你可以按照以下方式进行..

   ProjectMetadata.findOne(id:req.params.prjId)
    .populate(
        [
        path:'tasks',
        model:TaskMetadata,
        populate:[
            path:'assigned_to',
            model:User,
            select:'name employee_id'
        ,
        
            path:'priority',
            model:Priority,
            select:'id title'
        ],
        select:"task_name id code assign_to stage priority_id"
    ,
    
        path:'client',
        model:Client,
        select:"client_name"
    ,
    
        path:'prjct_mgr',
        model:User,
        select:"name"
    ,
    
        path:'acc_exec',
        model:User,
        select:'name employee_id'
    ,
    
        path:'poc',
        model:User,
        select:'name employee_id'
    ,
    
        path:'prj_type',
        model:ProjectType,
        select:"type -_id"
    

])

当我必须获取同一个父级的多个子子文档时,我发现第二种方法(使用数组)更有用。

【讨论】:

【参考方案6】:

我用这个:

.populate(
            path: 'pathName',
            populate: [
                
                    path: 'FirstSubPathName',
                    model: 'CorrespondingModel',
                ,
                
                    path: 'OtherSubPathName',
                    model: 'CorrespondingModel',
                ,
                
                    path: 'AnotherSubPathName',
                    model: 'CorrespondingModel',
                ,
            ]
        );

这是我发现的更简单的方法。我希望能提供帮助。 :)

【讨论】:

【参考方案7】:

试试看吧 查找项目并获取与项目相关的填充任务和特定任务用户查找

db.Project.find()
.populate(
    path: 'task',
    populate:  path: 'user_id'
)
.exec(async(error,results)=>

)

【讨论】:

欢迎来到 SO!尝试格式化您的答案以获得更好的可读性,这将有助于其他人遇到相同的问题。【参考方案8】:

查看下面的屏幕截图。这东西就像魅力一样!

【讨论】:

Please don't post screenshots of text。它们无法被搜索或复制,并且可用性差。相反,将代码作为文本直接粘贴到您的问题中。如果选择它并单击 按钮或 Ctrl+K 代码块将缩进四个空格,这将导致其呈现为代码。【参考方案9】:

这对我有用:

即不需要模型

    .populate(
      path: 'filters',
      populate: 
        path: 'tags',
        populate: 
          path: 'votes.user'
        
      
    )
    .populate(
      path: 'members'
    )

【讨论】:

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

使用聚合 mongodb mongoose 将集合子子文档与其他集合子文档连接起来

Mongoose 填充子文档数组

Mongoose 不参考新保存的文档填充以前保存的文档

如何填充嵌套的 Mongoose 嵌入文档

尝试加入两个文档时无法在 Mongoose 中填充路径

NodeJS - 我们可以从 Mongoose 更新填充的子文档吗?