📕
余烬的小册
数据结构与算法GitHub
  • 总述
  • 经验记录
    • 经验总结
      • web component
      • 前端性能优化总结与分析
      • 我的长列表优化方案
      • 双向通讯解决方案
      • 🔧基于istanbul实现代码测试覆盖率工具
      • 表单系统(低代码表单)
      • 跨端小程序
      • 设计一个即时聊天功能
      • 跨页面通讯 3658699fe4cb4d0bbe22b0881390bacd
    • 踩坑记录
      • HTML踩坑记录
      • Flutter踩坑记录
      • CSS踩坑记录
  • 源码解析
    • Vue源码解析
      • Vue2源码解析系列-响应式原理
      • Vue2源码解析系列-模板编译
      • Vue2源码解析系列-渲染系统(待更新)
        • Patch
      • Vue2源码解析系列-调度系统(todo)
      • Vue2组件更新流程(todo)
      • 如何学习Vue源码
      • Vue3源码解析系列-响应系统
      • Vue3源码解析系列-渲染系统
      • Vue3源码解析系列-组件化和渲染优化(todo)
      • Vue router源码解析(todo)
    • React源码解析(todo)
    • 微前端
      • qiankun源码解析(todo)
    • Vite源码解析
      • Vite Client源码
      • Vite Server源码(todo)
  • 前端技术
    • javaScript
      • ES6
        • 变量声明
        • 模块化
        • 箭头函数
        • 你不知道的for...of
        • 新的数据结构Set和Map
        • JavaScript异步编程终极解决方案
        • ES6 Class 3a0c0a225a534984aabe9a943c5df975
      • JavaScript Error
      • JavaScript浅拷贝和深拷贝
      • JavaScript闭包
      • JavaScript最佳实践
      • JavaScript设计模式
      • async函数的polyfill
    • 深入理解JavaScript系列
      • JavaScript中的继承
      • JavaScript原始类型和引用类型
      • JavaScript浅拷贝和深拷贝
      • JavaScript手写系列
      • JavaScript之this
      • 词法环境和环境记录
      • JavaScript内存泄漏
      • 执行上下文
      • 从ECMAScript规范中学习this
    • TypeScript
      • TypeScript基础教程
      • Typescript高级操作
      • TypeScript工具类型
      • Typescript手写实现工具类型
      • Typescript总结(思维导图)
    • 浏览器原理
      • 页面渲染原理
      • 浏览器存储
      • JavaScript事件循环
      • 事件循环
      • 跨域
      • DOM事件流
      • 从输入url到页面渲染
      • 判断节点之间的关系及根据节点关系查找节点
      • history API
    • 跨端技术
      • Flutter
        • Flutter布局组件
    • 前端工程化
      • Babel插件开发指南
      • 循环依赖
      • pm2
    • React
      • React 状态管理
      • React组件通讯
      • Redux入门
      • Flux
      • React Hook(todo)
      • Effect
  • 服务器端
    • 计算机网络
      • 应用层
      • 运输层
      • 物理层
      • 数据链路层
      • HTTP缓存
      • HTTPS
      • 网络层
    • NodeJs
      • Node.js
      • nodejs最佳实践
      • 《深入浅出Nodejs》小结
      • mongoose填充(populate)
      • node事件循环
      • Node子进程
      • nestjs从零开始
      • nodejs流
      • Nodejs调试
      • Koa源码解析
    • 服务器
      • 操作系统
      • Linux
      • nginx常用指令
      • nginx常用配置
    • 数据库
      • Mysql常见语法
      • MongoDB Indexes索引
  • 前端安全与性能优化
    • 前端安全
      • 跨站脚本攻击(XSS)
      • 跨站点请求伪造(CSRF)
      • 点击劫持
      • 中间人攻击
      • 越权攻击与JWT
    • 前端性能优化
      • 前端监控系统
      • 前端性能优化总结与分析 7348bba0918645b1899006dc842a64c1
      • 衡量性能的核心指标 0dc15ef127cf4f4a9f1137c377420292
      • 图片懒加载
  • 杂项
    • 其他
      • Git
      • web component框架
      • 实现滚动框的懒加载
      • Stencil指南
    • CSS
      • 定位和层叠上下文
      • BFC
      • 盒模型
      • css选择器
      • css变量
由 GitBook 提供支持
在本页
  • mongoose填充(populate)
  • 用法
  • 基础用法
  • 只填充特定字段
  • 匹配规则
  • 填充多个字段
  • 填充数组
  • 判断是否已填充及取消填充
  • 限制填充总数和限制每文档填充数
  • 参考
在GitHub上编辑
  1. 服务器端
  2. NodeJs

mongoose填充(populate)

mongoose填充(populate)

当需要在一个集合中引用另一个集合的文档时,就需要使用到mongoose的填充功能。所谓的填充是指首先为当前集合文档的某个字段引用另一个集合的文档,通常是引用_id,然后再将被引用的文档填充 进原文档中。

下面讲讲常见的用法:

用法

基础用法

假设现在要实现一个评论的集合,这个集合中包含author字段需要引用user-info集合中对应用户的文档信息,那么首先我们需要通过ref来建立联系。

  • ref: 要引用的文档所在的集合的名称

  • type: 字段类型,可以是其他类型,但是通常会是ObjectId,因为通常是根据_id来找到对应要引用的文档。

export const CommentSchema = new mongoose.Schema(
  {
    author: {
      ref: 'user_info',
      type: mongoose.Types.ObjectId,
    },
  },
);
export const commentModel = mongoose.model('comments', CommentSchema);

然后我们在创建comment文档的时候,需要将author文档的_id存进去。

// 假设这是user_info集合中,评论作者的文档
/*
{ "_id" : ObjectId("6318650a866c3556e01c49fd"), "email" : "123@qq.com", "password" : "123", "role" : "管理员", "__v" : 0 }
*/

commentModel.create({
	author: ObjectId("6318650a866c3556e01c49fd")
})

注意author存的是ObjectId,这也就是为什么前面的type一般是写ObjectId。

而此时你的文档是这样的

{
	_id: ObjectId("62d2dd84054bb169ad1efdd9"),
	author: ObjectId("6318650a866c3556e01c49fd")
}

如果你直接查询,那么author仍然只是一条ObjectId,要想把它变成user_info集合下对应的文档信息,你就需要执行populate进行填充。populate传递的第一个参数是要填充的路径,这个例子里是author。

commentModel.find({_id: ObjectId("62d2dd84054bb169ad1efdd9")})
	.populate('author')
// 返回的结果是
{
	_id: ObjectId("62d2dd84054bb169ad1efdd9"),
	author: { "_id" : ObjectId("6318650a866c3556e01c49fd"), "email" : "123@qq.com", "password" : "123", "role" : "管理员", "__v" : 0 }
}

这样就实现了mongoose的填充效果。

只填充特定字段

有时候我们并不需要user_info文档的所有信息,例如上个例子中我们只需要用户的email字段。那么可以这样写:

commentModel.find({_id: ObjectId("62d2dd84054bb169ad1efdd9")})
	.populate('author','email')
// 也可以这样写
commentModel.find({_id: ObjectId("62d2dd84054bb169ad1efdd9")})
	.populate({
		path: 'author',
		select: 'email'
	})

匹配规则

我们可以设置某些条件,只有满足这些条件才会填充。

commentModel.find({_id: ObjectId("62d2dd84054bb169ad1efdd9")})
	.populate({
		path: 'author',
		select: 'email',
		match: {
				email: "123@qq.com"
		}
	})

在上面的例子中,mongodb会检查被引用文档的email字段,只有当email字段等于"123@qq.com"时才会被填充。这个例子看起来没什么用,但是在实际开发中你可以使用$lt等关键字来扩展使用场景。

填充多个字段

path传递一个数组即可。

commentModel.find({_id: ObjectId("62d2dd84054bb169ad1efdd9")})
	.populate({
		path: ['author', '其他要填充的字段名'],
	})

填充数组

如果要填充的对象是一个包含多个文档的数组,那么直接对这个数组调用populate即可。

如果填充字段是一个嵌套结构,则可以用.符号

const post = await postModel.find({_id: ObjectId("62d2dd84054bb169ad1efd11")})
await post.populate({
  path: 'comments.author',
});

判断是否已填充及取消填充

你可以使用populated函数,如果该函数返回一个真值,那么就表示该字段已经被填充,否则说明该字段并未被填充。

story.populated('author'); // truthy

story.depopulate('author'); // Make `author` not populated anymore
story.populated('author'); // undefined

取消填充需要使用depopulate函数,示例如上👆

限制填充总数和限制每文档填充数

当填充字段的个数过多时就会大幅度影响性能,因此我们有时需要限制填充的个数,mongoose提供limit选项来选择填充的总数,注意这里limit是设置填充的总数,而非每个文档填充的个数,如果要设置每个文档填充的个数,你需要使用perDocumentLimit ,下面这个例子体现了两者的区别。

Story.create([
  { title: 'Casino Royale', fans: [1, 2, 3, 4, 5, 6, 7, 8] },
  { title: 'Live and Let Die', fans: [9, 10] }
]);

// 当使用limit时
const stories = await Story.find().populate({
  path: 'fans',
  options: { limit: 2 }
});

stories[0].name; // 'Casino Royale'
stories[0].fans.length; // 2

// 2nd story has 0 fans!
stories[1].name; // 'Live and Let Die'
stories[1].fans.length; // 0

// 使用perDocumentLimit时
const stories = await Story.find().populate({
  path: 'fans',
  // Special option that tells Mongoose to execute a separate query
  // for each `story` to make sure we get 2 fans for each story.
  perDocumentLimit: 2
});

stories[0].name; // 'Casino Royale'
stories[0].fans.length; // 2

stories[1].name; // 'Live and Let Die'
stories[1].fans.length; // 2

参考

上面的示例只涉及到基础的用法,有关填充的更多细节可以去看官方文档,文档链接如下:

上一页《深入浅出Nodejs》小结下一页node事件循环

最后更新于1年前

Mongoose v6.5.5: Query Population (mongoosejs.com)
Mongoose v6.5.5: (mongoosejs.com)