Spec-Zone.ru › Mongoose

Среднесвязующий слой

Среднесвязующий слой (также называемый пре- и пост-хуками) — это функции, которым передаётся управление во время выполнения асинхронных функций. Среднесвязующий слой задаётся на уровне схемы и полезен для написания плагинов. Mongoose 4.x имеет 4 типа среднесвязующего слоя: среднесвязующий слой документа, среднесвязующий слой модели, среднесвязующий слой агрегации и среднесвязующий слой запроса. Среднесвязующий слой документа поддерживается для следующих функций документа. В функциях среднесвязующего слоя документа this относится к документу.

  • инициализация
  • валидация
  • сохранение
  • удаление

Среднесвязующий слой запроса поддерживается для следующих функций модели и запроса. В функциях среднесвязующего слоя запроса this относится к запросу.

  • подсчёт
  • поиск
  • поиск одного
  • поиск и удаление одного
  • обновление одного и поиск
  • обновление

Среднесвязующий слой агрегации предназначен для MyModel.aggregate(). Среднесвязующий слой агрегации выполняется при вызове exec() на объекте агрегации. В среднесвязующем слое агрегации this относится к объекту агрегации.

  • агрегировать

Среднесвязующий слой модели поддерживается для следующих функций модели. В функциях среднесвязующего слоя модели this относится к модели.

  • вставить множество

Все типы среднесвязующего слоя поддерживают пре- и пост-хуки. Более подробное описание работы пре- и пост-хуков приведено ниже.

Примечание: нет хука запроса для remove(), только для документов. Если вы установите хук «удаление», он будет срабатывать при вызове myDoc.remove(), а не при вызове MyModel.remove(). Примечание: функция create() вызывает хуки save().

Пре

Существует два типа pre хуков: последовательный и параллельный.

Последовательный

Последовательные функции среднесвязующего слоя выполняются одна за другой, когда каждый среднесвязующий слой вызывает next.

var schema = new Schema(..);
schema.pre('save', function(next) {
  // do stuff
  next();
});

Вызов next() не останавливает выполнение остальной части кода в вашей функции среднесвязующего слоя. Используйте шаблон раннего return, чтобы предотвратить выполнение остальной части вашей функции среднесвязующего слоя при вызове next().

var schema = new Schema(..);
schema.pre('save', function(next) {
  if (foo()) {
    console.log('calling next!');
    // `return next();` will make sure the rest of this function doesn't run
    /*return*/ next();
  }
  // Unless you comment out the `return` above, 'after next' will print
  console.log('after next');
});

Параллельный

Параллельные среднесвязующие слои предлагают более точный контроль потока.

var schema = new Schema(..);

// `true` means this is a parallel middleware. You **must** specify `true`
// as the second parameter if you want to use parallel middleware.
schema.pre('save', true, function(next, done) {
  // calling next kicks off the next middleware in parallel
  next();
  setTimeout(done, 100);
});

Связанный метод, в данном случае save, не будет выполнен, пока done не будет вызван каждым среднесвязующим слоем.

Примеры использования

Среднесвязующие слои полезны для разбивки логики модели на атомарные части и избежания вложенных блоков асинхронного кода. Вот ещё несколько идей:

  • сложная валидация
  • удаление зависимых документов
    • (удаление пользователя удаляет все его посты)
  • асинхронные значения по умолчанию
  • асинхронные задачи, которые запускаются определённым действием
    • триггеринг пользовательских событий
    • уведомления

Обработка ошибок

Если любой среднесвязующий слой вызывает next или done с параметром типа Error, поток прерывается, а ошибка передаётся в обратный вызов.

schema.pre('save', function(next) {
  // You **must** do `new Error()`. `next('something went wrong')` will
  // **not** work
  var err = new Error('something went wrong');
  next(err);
});

// later...

myDoc.save(function(err) {
  console.log(err.message) // something went wrong
});

Пост-среднесвязующий слой

пост-среднесвязующий слой выполняется после связанного метода и всех его pre среднесвязующих слоёв. post среднесвязующие слои не получают непосредственного управления потоком, например, им не передаются обратные вызовы next или done. post хуки — это способ регистрации традиционных обработчиков событий для этих методов.

schema.post('init', function(doc) {
  console.log('%s has been initialized from the db', doc._id);
});
schema.post('validate', function(doc) {
  console.log('%s has been validated (but not saved yet)', doc._id);
});
schema.post('save', function(doc) {
  console.log('%s has been saved', doc._id);
});
schema.post('remove', function(doc) {
  console.log('%s has been removed', doc._id);
});

Асинхронные пост-хуки

Хотя пост-среднесвязующий слой не получает управление потоком, вы по-прежнему можете убедиться, что асинхронные пост-хуки выполняются в определённом порядке. Если ваша функция пост-хука принимает как минимум 2 параметра, mongoose предположит, что второй параметр — функция next(), которую вы вызовете для запуска следующего среднесвязующего слоя в последовательности.

// Takes 2 parameters: this is an asynchronous post hook
schema.post('save', function(doc, next) {
  setTimeout(function() {
    console.log('post1');
    // Kick off the second post hook
    next();
  }, 10);
});

// Will not execute until the first middleware calls `next()`
schema.post('save', function(doc, next) {
  console.log('post2');
  next();
});

Хуки сохранения/валидации

Функция save() запускает хуки validate(), поскольку в mongoose есть встроенный хук pre('save'), который вызывает validate(). Это означает, что все хуки pre('validate') и post('validate') вызываются до любых хуков pre('save').

schema.pre('validate', function() {
  console.log('this gets printed first');
});
schema.post('validate', function() {
  console.log('this gets printed second');
});
schema.pre('save', function() {
  console.log('this gets printed third');
});
schema.post('save', function() {
  console.log('this gets printed fourth');
});

Примечания к findAndUpdate() и среднесвязующему слою запроса

Пре- и пост-хуки save() не выполняются для update(), findOneAndUpdate() и т. д. Более подробное обсуждение, почему, можно найти в этом вопросе на GitHub. Mongoose 4.0 имеет отдельные хуки для этих функций.

schema.pre('find', function() {
  console.log(this instanceof mongoose.Query); // true
  this.start = Date.now();
});

schema.post('find', function(result) {
  console.log(this instanceof mongoose.Query); // true
  // prints returned documents
  console.log('find() returned ' + JSON.stringify(result));
  // prints number of milliseconds the query took
  console.log('find() took ' + (Date.now() - this.start) + ' millis');
});

Среднесвязующий слой запроса отличается от среднесвязующего слоя документа тонким, но важным аспектом: в среднесвязующем слое документа this относится к обновляемому документу. В среднесвязующем слое запроса mongoose не обязательно имеет ссылку на обновляемый документ, поэтому this относится к объекту запроса, а не к обновляемому документу.

Например, если вы хотите добавить отметку времени updatedAt к каждому вызову update(), вы используете следующий пре-хук.

schema.pre('update', function() {
  this.update({},{ $set: { updatedAt: new Date() } });
});

Обработка ошибок в среднесвязующем слое

Новое в 4.5.0

Выполнение среднесвязующего слоя обычно останавливается в первый раз, когда какой-либо среднесвязующий слой вызывает next() с ошибкой. Однако существует особый вид пост-среднесвязующего слоя, называемый «среднесвязующим слоем обработки ошибок», который выполняется именно при возникновении ошибки.

Среднесвязующий слой обработки ошибок определяется как среднесвязующий слой, принимающий один дополнительный параметр: «ошибку», возникшую в качестве первого параметра функции. Среднесвязующий слой обработки ошибок затем может преобразовать ошибку по вашему желанию.

var schema = new Schema({
  name: {
    type: String,
    // Will trigger a MongoError with code 11000 when
    // you save a duplicate
    unique: true
  }
});

// Handler **must** take 3 parameters: the error that occurred, the document
// in question, and the `next()` function
schema.post('save', function(error, doc, next) {
  if (error.name === 'MongoError' && error.code === 11000) {
    next(new Error('There was a duplicate key error'));
  } else {
    next(error);
  }
});

// Will trigger the `post('save')` error handler
Person.create([{ name: 'Axl Rose' }, { name: 'Axl Rose' }]);

Среднесвязующий слой обработки ошибок также работает с среднесвязующим слоем запроса. Вы также можете определить пост-хук update() для перехвата ошибок дублирования ключей MongoDB.

// The same E11000 error can occur when you call `update()`
// This function **must** take 3 parameters. If you use the
// `passRawResult` function, this function **must** take 4
// parameters
schema.post('update', function(error, res, next) {
  if (error.name === 'MongoError' && error.code === 11000) {
    next(new Error('There was a duplicate key error'));
  } else {
    next(error);
  }
});

var people = [{ name: 'Axl Rose' }, { name: 'Slash' }];
Person.create(people, function(error) {
  Person.update({ name: 'Slash' }, { $set: { name: 'Axl Rose' } }, function(error) {
    // `error.message` will be "There was a duplicate key error"
  });
});

Далее

Теперь, когда мы рассмотрели среднесвязующие слои, давайте посмотрим на подход Mongoose к имитации JOIN с помощью своего вспомогательного средства запросов популяции.

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

Spec-Zone.ru

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