用过 MongoDB 的人应该都知道它是没有关系型数据库里的 join 特性的,这意味着当咱们使用 MongoDB 读取某个 document 及其关联的 document 的字段的时候,变得尤其麻烦。数据库
基于此,Mongoose 封装了一个 population 的功能,当你在定义 Schema 的时候指定了某个 field 是引用了另外一个 Schema ,那么你在获取 document 的时候就可使用 populate 方法让 Mongoose 帮你经过引用 Schema 和 id 找到关联的另外一个 document,而且用该 document 的内容替换掉原来引用字段的内容,使引用的 ducoment 使用起来就像是内嵌的 document 同样方便。数组
首先,若是要使用这个功能,必须给你的“外键字段”定义 Mongoose 的“外键约束”(这里借用关系型数据库的术语,只是为了帮助理解)。假设咱们有一个用户的 Schema ,其中有一个字段是followings,保存用户关注了的其余用户,相似微博。Mongoose 的 Schema 定义以下:mongoose
var mongoose = require('mongoose') , Schema = mongoose.Schema var UserSchema = Schema({ name : String, followings : [{ type: Schema.Types.ObjectId, ref: 'User' }] }); var User = mongoose.model('User', UserSchema);
而后咱们建立两个 User 实例,而且先将其中之一保存进数据库(为了产生 _id ):函数
var lily = new User({name: 'lily'}) ,lucy = new User({name: 'lucy'}); lily.save(cb);
lily 保存成功后,咱们再在回调函数 cb 中保存 lucy ,不过这个时候假设 lucy 关注了 lily:ui
function cb(err) { if(err) throw err; lucy.followings.push(lily._id);// line 1 lucy.save(cb1); }
这样就保存完成了。不过好像咱们没用引用字段以前也是这么保存的吧?有没有更直接一点的?有,你其实能够将 line 1 的语句改为如下形式, Mongoose 也会自动帮你作内部解析的:code
lucy.followings.push(lily);
这样看起来代码就更接近天然语言了,有点ORM的味道。对象
固然,上面的例子可能对开发者来讲没有什么太大的用处,那么就来看看 population 在获取 document 的时候能给咱们带来什么好处吧。排序
假设咱们如今要找到 lucy 的信息以及她所关注的用户,咱们能够这样:开发
User.findOne({name: 'lucy'}).exec(cb2);
可是这样拿出来的 lucy 的 followings 是一组存储了 ObjectId 的数组,还得去一个个查找对应的用户,多麻烦啊。因此这个时候用 population 就简单多了,直接指定你想要 populate 哪一个引用字段:get
User.findOne({name: 'lucy'}).populate('followings').exec(cb2);
这样的话,在回调函数 cb2 中你获得的就是一个存储了若干个 User 对象的数组了。
populate 方法能够用在 document 上、 model 上或者是 query 对象上,这意味着你几乎能够在任何地方调用这个方法以填充你的引用字段。
固然,populate 方法在不一样对象上参数不大同样,可是都接收一个option的参数,你能够用这些参数指定:
目前,Mongoose 只支持如下几种引用字段的类型:
并且天然而然,引用 document 的主键类型必须和引用字段类型相对应。在生产环境中,推荐主键类型和引用类型都使用 ObjectId ,一是由于 ObjectId 不包含业务含义,二是 ObjectId 不大可能重复,三是由于 Mongoose 默认生成的主键类型就是 ObjectId ,能够减小不少配置的操做。
更多信息请关注这里。