Spec-Zone.ru › Mongoose

Поддокументы

Поддокументы — это документы, вложенные в другие документы. В Mongoose это означает, что вы можете вкладывать схемы в другие схемы. Mongoose имеет два различных понятия поддокументов: массивы поддокументов и одиночные вложенные поддокументы.

const childSchema = new Schema({ name: 'string' });

const parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments
  child: childSchema
});

Обратите внимание, что загруженные документы не являются поддокументами в Mongoose. Данные поддокументов встроены в документ верхнего уровня. Ссылающиеся документы являются отдельными документами верхнего уровня.

const childSchema = new Schema({ name: 'string' });
const Child = mongoose.model('Child', childSchema);

const parentSchema = new Schema({
  child: {
    type: mongoose.ObjectId,
    ref: 'Child'
  }
});
const Parent = mongoose.model('Parent', parentSchema);

const doc = await Parent.findOne().populate('child');
// NOT a subdocument. `doc.child` is a separate top-level document.
doc.child;

Что такое поддокумент?

Поддокументы похожи на обычные документы. Вложенные схемы могут иметь средства промежуточного программного обеспечения, собственную логику валидации, виртуальные поля и любые другие функции, которые могут использовать схемы верхнего уровня. Основное различие заключается в том, что поддокументы не сохраняются индивидуально, они сохраняются всякий раз, когда сохраняется их родительский документ верхнего уровня.

const Parent = mongoose.model('Parent', parentSchema);
const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] });
parent.children[0].name = 'Matthew';

// `parent.children[0].save()` is a no-op, it triggers middleware but
// does **not** actually save the subdocument. You need to save the parent
// doc.
await parent.save();

Поддокументы имеют save и validate средства промежуточного программного обеспечения, как и документы верхнего уровня. Вызов save() для родительского документа запускает save() средства промежуточного программного обеспечения для всех его поддокументов, и то же самое относится к validate() средствам промежуточного программного обеспечения.

childSchema.pre('save', function(next) {
  if ('invalid' == this.name) {
    return next(new Error('#sadpanda'));
  }
  next();
});

const parent = new Parent({ children: [{ name: 'invalid' }] });
try {
  await parent.save();
} catch (err) {
  err.message; // '#sadpanda'
}

Средства промежуточного программного обеспечения поддокументов pre('save') и pre('validate') выполняются до средств промежуточного программного обеспечения документа верхнего уровня pre('save'), но после средств промежуточного программного обеспечения документа верхнего уровня pre('validate'). Это связано с тем, что валидация перед save() фактически является частью встроенных средств промежуточного программного обеспечения.

// Below code will print out 1-4 in order
const childSchema = new mongoose.Schema({ name: 'string' });

childSchema.pre('validate', function(next) {
  console.log('2');
  next();
});

childSchema.pre('save', function(next) {
  console.log('3');
  next();
});

const parentSchema = new mongoose.Schema({
  child: childSchema
});

parentSchema.pre('validate', function(next) {
  console.log('1');
  next();
});

parentSchema.pre('save', function(next) {
  console.log('4');
  next();
});

Поддокументы и вложенные пути

Во Mongoose вложенные пути немного отличаются от поддокументов. Например, ниже приведены две схемы: одна со child в качестве поддокумента и одна с child в качестве вложенного пути.

// Subdocument
const subdocumentSchema = new mongoose.Schema({
  child: new mongoose.Schema({ name: String, age: Number })
});
const Subdoc = mongoose.model('Subdoc', subdocumentSchema);

// Nested path
const nestedSchema = new mongoose.Schema({
  child: { name: String, age: Number }
});
const Nested = mongoose.model('Nested', nestedSchema);

Эти две схемы выглядят похоже, и документы в MongoDB будут иметь одинаковую структуру при обеих схемах. Но есть несколько различий, специфичных для Mongoose:

Во-первых, экземпляры Nested никогда не имеют child === undefined. Вы всегда можете установить подсвойства child, даже если вы не устанавливаете child свойство. Но экземпляры Subdoc могут иметь child === undefined.

const doc1 = new Subdoc({});
doc1.child === undefined; // true
doc1.child.name = 'test'; // Throws TypeError: cannot read property...

const doc2 = new Nested({});
doc2.child === undefined; // false
console.log(doc2.child); // Prints 'MongooseDocument { undefined }'
doc2.child.name = 'test'; // Works

Значения по умолчанию для поддокументов

Пути поддокументов по умолчанию не определены, и Mongoose не применяет значения по умолчанию для поддокументов, если вы не зададите путь поддокумента с ненулевым значением.

const subdocumentSchema = new mongoose.Schema({
  child: new mongoose.Schema({
    name: String,
    age: {
      type: Number,
      default: 0
    }
  })
});
const Subdoc = mongoose.model('Subdoc', subdocumentSchema);

// Note that the `age` default has no effect, because `child`
// is `undefined`.
const doc = new Subdoc();
doc.child; // undefined

Однако, если вы установите doc.child на любой объект, Mongoose применит значение по умолчанию age при необходимости.

doc.child = {};
// Mongoose applies the `age` default:
doc.child.age; // 0

Mongoose применяет значения по умолчанию рекурсивно, что означает, что есть хороший обходной путь, если вы хотите убедиться, что Mongoose применяет значения по умолчанию для поддокументов: сделайте значение по умолчанию для пути поддокумента пустым объектом.

const childSchema = new mongoose.Schema({
  name: String,
  age: {
    type: Number,
    default: 0
  }
});
const subdocumentSchema = new mongoose.Schema({
  child: {
    type: childSchema,
    default: () => ({})
  }
});
const Subdoc = mongoose.model('Subdoc', subdocumentSchema);

// Note that Mongoose sets `age` to its default value 0, because
// `child` defaults to an empty object and Mongoose applies
// defaults to that empty object.
const doc = new Subdoc();
doc.child; // { age: 0 }

Поиск поддокумента

Каждый поддокумент имеет _id по умолчанию. Массивы документов Mongoose имеют специальный метод id для поиска в массиве документов документа с заданным _id.

const doc = parent.children.id(_id);

Добавление поддокументов в массивы

Методы MongooseArray, такие как push, unshift, addToSet и другие, прозрачно преобразуют аргументы в соответствующие типы:

const Parent = mongoose.model('Parent');
const parent = new Parent();

// create a comment
parent.children.push({ name: 'Liesl' });
const subdoc = parent.children[0];
console.log(subdoc); // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
subdoc.isNew; // true

await parent.save();
console.log('Success!');

Вы также можете создать поддокумент без его добавления в массив, используя метод create() массивов документов.

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

Удаление поддокументов

Каждый поддокумент имеет свой собственный метод deleteOne. Для поддокумента массива это эквивалентно вызову .pull() для поддокумента. Для одиночного вложенного поддокумента deleteOne() эквивалентно установлению поддокумента в null.

// Equivalent to `parent.children.pull(_id)`
parent.children.id(_id).deleteOne();
// Equivalent to `parent.child = null`
parent.child.deleteOne();

await parent.save();
console.log('the subdocs were removed');

Родители поддокументов

Иногда вам нужно получить родителя поддокумента. Вы можете получить доступ к родителю с помощью функции parent().

const schema = new Schema({
  docArr: [{ name: String }],
  singleNested: new Schema({ name: String })
});
const Model = mongoose.model('Test', schema);

const doc = new Model({
  docArr: [{ name: 'foo' }],
  singleNested: { name: 'bar' }
});

doc.singleNested.parent() === doc; // true
doc.docArr[0].parent() === doc; // true

Если у вас есть глубоко вложенный поддокумент, вы можете получить доступ к документу верхнего уровня с помощью функции ownerDocument().

const schema = new Schema({
  level1: new Schema({
    level2: new Schema({
      test: String
    })
  })
});
const Model = mongoose.model('Test', schema);

const doc = new Model({ level1: { level2: 'test' } });

doc.level1.level2.parent() === doc; // false
doc.level1.level2.parent() === doc.level1; // true
doc.level1.level2.ownerDocument() === doc; // true

Альтернативный синтаксис объявления для массивов

Если вы создаёте схему с массивом объектов, Mongoose автоматически преобразует объект в схему для вас:

const parentSchema = new Schema({
  children: [{ name: 'string' }]
});
// Equivalent
const parentSchema = new Schema({
  children: [new Schema({ name: 'string' })]
});

Далее

Теперь, когда мы разобрались с поддокументами, давайте посмотрим на запросы.

© 2010 LearnBoost
Licensed under the MIT License.
https://mongoosejs.com/docs/subdocs.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API