子文档(Subdocument) - 图1mongoose

子文档(Subdocument) - 图2mongoose

子文档(Subdocument)

子文档是指嵌套在另一个文档中的文档。 在 Mongoose 中,这意味着你可以在里嵌套另一个 schema。 Mongoose 子文档有两种不同的概念:子文档数组和单个嵌套子文档。

  1. var childSchema = new Schema({ name: 'string' });
  2. var parentSchema = new Schema({
  3. // Array of subdocuments
  4. children: [childSchema],
  5. // Single nested subdocuments. Caveat: single nested subdocs only work
  6. // in mongoose >= 4.2.0
  7. child: childSchema
  8. });

子文档与普通 document 类似。嵌套 schema 可以有自己的 中间件自定义检验逻辑、 虚拟值以及其他顶层 schemas 可用的特性。两者主要的不同点是 子文档不能单独保存,他们会在他们的顶级文档保存时保存。

  1. var Parent = mongoose.model('Parent', parentSchema);
  2. var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  3. parent.children[0].name = 'Matthew';
  4. // `parent.children[0].save()` 无操作,虽然他触发了中间件
  5. // 但是**没有**保存文档。你需要 save 他的父文档
  6. parent.save(callback);

子文档跟普通 document 一样有 savevalidate 中间件。 调用父文档的 save() 会触发其所有子文档的 save() 中间件, validate() 中间件同理。

  1. childSchema.pre('save', function (next) {
  2. if ('invalid' == this.name) {
  3. return next(new Error('#sadpanda'));
  4. }
  5. next();
  6. });
  7. var parent = new Parent({ children: [{ name: 'invalid' }] });
  8. parent.save(function (err) {
  9. console.log(err.message) // #sadpanda
  10. });

子文档的 pre('save')pre('validate') 中间件执行于 顶层 document 的 pre('save') 之前, 顶层 document 的 pre('validate') 之后。 因为 save() 前的验证就是一个内置中间件。(待修改)

  1. // 一下代码顺序打出 1-4
  2. var childSchema = new mongoose.Schema({ name: 'string' });
  3. childSchema.pre('validate', function(next) {
  4. console.log('2');
  5. next();
  6. });
  7. childSchema.pre('save', function(next) {
  8. console.log('3');
  9. next();
  10. });
  11. var parentSchema = new mongoose.Schema({
  12. child: childSchema,
  13. });
  14. parentSchema.pre('validate', function(next) {
  15. console.log('1');
  16. next();
  17. });
  18. parentSchema.pre('save', function(next) {
  19. console.log('4');
  20. next();
  21. });

查找子文档

每个子文档都有一个默认的 _id 。Mongoose document 数组有一个特别的 id 方法, 这个方法只要传入 _id 就能返回文档数组中特定文档。

  1. var doc = parent.children.id(_id);

添加子文档到数组

Mongoose 数组方法有 pushunshiftaddToSet、 及其他:

  1. var Parent = mongoose.model('Parent');
  2. var parent = new Parent;
  3. // create a comment
  4. parent.children.push({ name: 'Liesl' });
  5. var subdoc = parent.children[0];
  6. console.log(subdoc) // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
  7. subdoc.isNew; // true
  8. parent.save(function (err) {
  9. if (err) return handleError(err)
  10. console.log('Success!');
  11. });

create 方法可以新建子文档但不加入数组。

  1. var newdoc = parent.children.create({ name: 'Aaron' });

删除子文档

每个子文档都有 remove 方法。 另外,对于子文档数组,有一个等效方法 .pull()。 对于单个嵌套子文档,remove() 与把这个文档的值设为 null 等效。

  1. // 等效于 `parent.children.pull(_id)`
  2. parent.children.id(_id).remove();
  3. // 等效于 `parent.child = null`
  4. parent.child.remove();
  5. parent.save(function (err) {
  6. if (err) return handleError(err);
  7. console.log('the subdocs were removed');
  8. });

代替声明语法的写法

如果你用对象的数组创建 schema ,mongoose 会自动 为你把对象转换成 schema:

  1. var parentSchema = new Schema({
  2. children: [{ name: 'string' }]
  3. });
  4. // Equivalent
  5. var parentSchema = new Schema({
  6. children: [new Schema({ name: 'string' })]
  7. });

下一步

这章我们介绍了子文档,下一个章节将会介绍 querying