Mongoose 使用之 Population

何为 Population

用过 MongoDB 的人应该都知道它是没有关系型数据库里的 join 特性的,这意味着当我们使用 MongoDB 读取某个 document 及其关联的 document 的字段的时候,变得尤为麻烦。

基于此,Mongoose 封装了一个 population 的功能,当你在定义 Schema 的时候指定了某个 field 是引用了另一个 Schema ,那么你在获取 document 的时候就可以使用 populate 方法让 Mongoose 帮你通过引用 Schema 和 id 找到关联的另一个 document,并且用该 document 的内容替换掉原来引用字段的内容,使引用的 ducoment 使用起来就像是内嵌的 document 一样方便。

定义带有引用字段的 Schema

首先,如果要使用这个功能,必须给你的“外键字段”定义 Mongoose 的“外键约束”(这里借用关系型数据库的术语,只是为了帮助理解)。假设我们有一个用户的 Schema ,其中有一个字段是followings,保存用户关注了的其他用户,类似微博。Mongoose 的 Schema 定义如下:

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:

function cb(err) {
    if(err) throw err;
    lucy.followings.push(lily._id);// line 1
    lucy.save(cb1);
}

这样就保存完成了。不过好像我们没用引用字段之前也是这么保存的吧?有没有更直接一点的?有,你其实可以将 line 1 的语句改成以下形式, Mongoose 也会自动帮你做内部解析的:

lucy.followings.push(lily);

这样看起来代码就更接近自然语言了,有点ORM的味道。

查询 document

当然,上面的例子可能对开发者来说没有什么太大的用处,那么就来看看 population 在获取 document 的时候能给我们带来什么好处吧。

假设我们现在要找到 lucy 的信息以及她所关注的用户,我们可以这样:

User.findOne({name: 'lucy'}).exec(cb2);

但是这样拿出来的 lucy 的 followings 是一组存储了 ObjectId 的数组,还得去一个个查找对应的用户,多麻烦啊。所以这个时候用 population 就简单多了,直接指定你想要 populate 哪个引用字段:

User.findOne({name: 'lucy'}).populate('followings').exec(cb2);

这样的话,在回调函数 cb2 中你得到的就是一个存储了若干个 User 对象的数组了。

populate 方法

populate 方法可以用在 document 上、 model 上或者是 query 对象上,这意味着你几乎可以在任何地方调用这个方法以填充你的引用字段。

当然,populate 方法在不同对象上参数不大一样,但是都接收一个option的参数,你可以用这些参数指定:

  • path: 以空格分隔的引用字段的名称
  • select: 填充引用 document 中的哪些字段
  • match: 可选,指定附加的查询条件
  • model: 可选,指定引用的 model
  • options: 可选,指定附加的其他查询选项,如排序以及条数限制等等

引用字段的类型

目前,Mongoose 只支持以下几种引用字段的类型:

  • ObjectId
  • Number
  • String
  • Buffer

而且自然而然,引用 document 的主键类型必须和引用字段类型相对应。在生产环境中,推荐主键类型和引用类型都使用 ObjectId ,一是因为 ObjectId 不包含业务含义,二是 ObjectId 不大可能重复,三是因为 Mongoose 默认生成的主键类型就是 ObjectId ,可以减少很多配置的操作。

更多信息请关注这里。

你可能感兴趣的:(mongodb,mongoose,Population)