Mongoose: pre, post промежуточное ПО не выполняются на findByIdAndUpdate

Созданный на 15 июн. 2012  ·  102Комментарии  ·  Источник: Automattic/mongoose

Потому что представлен метод findAndUpdate для сокращения следующего кода:

 Model.findById(_id, function (err, doc) {
      if (doc) {
          doc.field = 'value';
          doc.save(function (err) {
                 // do something;
          });
      }
 });

к этому:

   .findByIdAndUpdate(_id, {$set: {field: 'value'}}, function (err, doc) {
        // do something 
    });

Нам нужно использовать пре- и пост-промежуточное ПО точно так же. В настоящее время промежуточное ПО pre, post не выполняется, когда я делаю findByIdAndUpdate.

Самый полезный комментарий

Привет,

Я знаю, что вопрос закрыт, но я не совсем удовлетворен ответом. Разве не должно быть по крайней мере промежуточное программное обеспечение после сохранения или обновления для findOneAndUpdate и других подобных операций? Кажется, все в порядке, так как документ возвращен. Невозможно для промежуточного программного обеспечения или для Model.update, я согласен.

Это значительно улучшит возможности плагинов, таких как mongoosastic, которые в настоящее время слепы к некоторым операциям, которые он должен поддерживать.

Если нет промежуточного программного обеспечения, есть ли у кого-нибудь идеи о том, как управлять некоторыми операциями после обновления в плагине?

Спасибо

Все 102 Комментарий

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

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

Так? Если я хочу вызвать промежуточное программное обеспечение до и после публикации, мне нужно использовать первый подход?

да это верно. Model.update,findByIdAndUpdate,findOneAndUpdate,findOneAndRemove,findByIdAndRemove — все команды выполняются непосредственно в базе данных.

Это обязательно должно быть разъяснено в руководстве , особенно если вы говорите о проверке на той же странице и описываете findByIdAndUpdate как «лучшее».

если вы нажмете на ссылку «лучше», вы перейдете к полной документации
метод, где он объясняет проверку и т. д.

не стесняйтесь отправлять запрос на включение, чтобы добавить что-то, что вы считаете лучше. это
довольно легко сделать так:
https://github.com/LearnBoost/mongoose/blob/master/CONTRIBUTING.md

В среду, 31 октября 2012 г., в 18:03, Джесси Фултон извещения@github.com написал :

Это обязательно нужно уточнить в руководстве http://mongoosejs.com/docs/documents.html ,
особенно если вы говорите о проверке на той же странице и
описание findByIdAndUpdate как "лучшее"


Ответьте на это письмо напрямую или просмотрите его на Gi tHubhttps://github.com/LearnBoost/mongoose/issues/964#issuecomment -9967865.

Аарон
@ааронхекманн https://twitter.com/#!/ааронхекманн

Добавлены примечания в документ промежуточного программного обеспечения.

Запрос на извлечение: https://github.com/LearnBoost/mongoose/pull/1750

Привет,

Я знаю, что вопрос закрыт, но я не совсем удовлетворен ответом. Разве не должно быть по крайней мере промежуточное программное обеспечение после сохранения или обновления для findOneAndUpdate и других подобных операций? Кажется, все в порядке, так как документ возвращен. Невозможно для промежуточного программного обеспечения или для Model.update, я согласен.

Это значительно улучшит возможности плагинов, таких как mongoosastic, которые в настоящее время слепы к некоторым операциям, которые он должен поддерживать.

Если нет промежуточного программного обеспечения, есть ли у кого-нибудь идеи о том, как управлять некоторыми операциями после обновления в плагине?

Спасибо

Некоторые методы @albanm полностью обходят mongoose , поэтому вы не получаете перехватчики промежуточного программного обеспечения. Насколько я знаю, единственный способ заставить хуки выполняться — это использовать отдельные вызовы find() и save() , как указано выше.

Я понимаю, и это имеет смысл. О промежуточном программном обеспечении не может быть и речи, поскольку перед определенными методами нет выборки. Но тем не менее, mongoose должен уметь обертывать операции обновления, которые также возвращают обновленные документы и запускают некоторые почтовые перехватчики.

Я думаю, что @albanm прав. Люди могут захотеть иметь одинаковые функции, когда используют одну и ту же функцию. Как насчет того, чтобы обернуть эти методы «непосредственного обновления» некоторым перехватом проверки существования каких-либо перехватчиков? Если хук существует, используйте его или вызывайте оригинальные методы обновления в противном случае.

+1

+1

+1

+1

:+1:

+1

:-1:
Я получаю запрос функции, но чем дольше я работаю с промежуточным ПО, тем чаще мне приходится обходить промежуточное ПО при вызове сценариев обновления базы данных и других элементов, которые не являются обычными для моих обычных операций. Используя статические методы, становится очень легко создать пользовательскую оболочку, которая уже реализует вышеуказанные запросы функций. Поскольку mongoose сейчас открыт для идей для версии 4, маловероятно, что какое-либо изменение API такого масштаба произойдет в версии 3. Прошу перенести это в обсуждение версии 4.

Я бы, однако, :+1: если бы я мог отключить промежуточное ПО при вызове сохранения. Я часто сталкиваюсь с объектом мангуста, который был передан из другой функции, и просто хочу выполнить .save - в этой ситуации выполнение .save предпочтительнее написания нового запроса. Если это возможно, подскажите

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

+1

Простой способ добавить хуки для этих методов — перезаписать встроенные функции:

_update = UserModel.update
UserModel.update = (conditions, update) ->
  update.updatedAt or= new Date
  _update.apply this, arguments

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

Вы можете попробовать эту модель лимбо . Это простая оболочка модели мангуста, поддерживающая привязку static/method/overwrite ко всем схемам и вызывающая методы rpc для запроса mongodb.

+1, нужна эта функция...

Хотя я вроде как понимаю аргументацию, отсутствие крючков для атомарных обновлений ИМХО делает Mongoose несколько бессмысленным на практике. Когда я использую атомарные обновления, любые проверки, значения по умолчанию и т. д. не выполняются, поэтому вся цель использования ODM терпит поражение. Использование поиска/сохранения выполнит эту работу, но есть ли гарантия, что это всегда будет использоваться?

Более того, обычно я стараюсь избегать поиска/сохранения, так как это не атомарная операция. MongoDB компенсирует отсутствие поддержки транзакций, предоставляя мощные функции атомарных запросов и обновлений. Поэтому я бы использовал эти атомарные операции, но без поддержки промежуточного программного обеспечения Mongoose не будет иметь большой ценности по сравнению с собственным MongoClient .

Даже примеры в http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning будут использовать update , следовательно, обходить промежуточное ПО. Я могу правильно использовать управление версиями или промежуточное программное обеспечение, но не комбинировать оба? Действительно, какой смысл в нем?

Я даже не до конца понимаю технические причины: если update & co. обернуть операции базы данных, почему мы не можем перехватить вызов и передать объекты запроса, чтобы мы могли выполнить некоторую проверку/настройку, прежде чем мы действительно выполним обновление?

@joerx +1 было бы достаточно .. :) но ваши рассуждения безупречны.

В ветке 3.9.x есть поддержка пре- и пост-хуков для find и findOne — должно быть легко добавить поддержку для findOneAndUpdate и update .

Эта функция объединена?

Таким образом, хуки pre('findOneAndUpdate') и post('findOneAndUpdate') находятся в мастере, а хука обновления пока нет. Пока нет релиза, который бы содержал ни один из них.

Итак, теперь он запускает предварительное сохранение после .update()?

Нет. Существует отдельный хук update() для Query.update() . Перехватчики сохранения отличаются от перехватчиков запросов.

@ vkarpov15 Не могли бы вы дать ссылку на вспомогательную документацию для хука обновления? Есть информация о том, когда выйдут релизы pre('findOneAndUpdate') и post('findOneAndUpdate') ?

@karlstanton использует 4.0.0-rc2, npm install mongoose@unstable :)

'use strict';

var Promise  = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));

var counterSchema = new mongoose.Schema({
    total: {
        type:    Number,
        default: 0
    }
});

counterSchema.post('findOneAndUpdate', function (doc) {
    console.log(doc.total);
});

var Counter = mongoose.model('Counter', counterSchema);

Promise.coroutine(function *() {
    yield mongoose.connectAsync(process.env.MONGODB_URI);
    console.log('Connected');
    let counter = yield Counter.createAsync({});
    console.log(`${counter.total}`);
    for (let i = 0; i < 10; i++) {
        yield Counter.findOneAndUpdateAsync({ _id: counter.id }, { $inc: { total: 1} });
    }
})();
0
0
1
2
3
4
5
6
7
8
9

Кажется, что это шаг позади. Что мне не хватает?

AH, критическое изменение флага new по умолчанию. Хотя есть что сказать о согласованности с базовым драйвером, я должен сказать, что это действительно нелогично, особенно при рассмотрении этого нового хука.

@никогдафокс :)

Да , @neverfox хороший улов. Это изменение задокументировано в примечаниях к выпуску, и вы можете увидеть больше обсуждений, почему это было изменено в # 2262.

Причина в том, что new по умолчанию имеет значение false в драйвере узла mongodb, оболочке mongodb, фактическом сервере mongodb и т. д., а слои-оболочки, устанавливающие нестандартные значения по умолчанию, усложняют жизнь разработчикам. Мой канонический пример модуля, который делает это ужасно неправильно, — это gulp-uglify , который переопределяет кучу значений по умолчанию uglify-js.

Вижу, что вопрос закрыт, но функционал на месте версии 4.0.2 вышел, просто еще в нестабильной версии? Кажется, он не выполняется в findOneAndUpdate с Scema.pre('update') или Schema.pre('findOneAndUpdate') с версией 4.0.2. Я пропустил что-то, что я должен передать функции?

Как вы объявляете предварительный хук @CaptainStaplerz ?

Доступен ли хук промежуточного программного обеспечения findOneAndUpdate в версии 4.0.2? Я обновился с 3.8 до последней версии mongoose 4.0.2, чтобы использовать это, и промежуточное ПО Document.schema.post('findOneAndUpdate', функция (doc) не запускается, как save() или remove()

@honitus покажи мне свой код

@vkarpov15 - Спасибо за быстрый ответ, пожалуйста

блог.контроллер,js

// Updates an existing Blog in the DB, adds comment
exports.update = function(req, res) {

Blog.findOneAndUpdate(
     {"_id": req.body._id,},
      {"$push": {"comments": buildComment}},
     {safe: true, upsert: true}, function (err, workspace) {
      if (err) {
         return handleError(res, err);
       }
       return res.send(200);
      }
   );

}

blog.socket.js

/**
 * Broadcast updates to client when the model changes
 */

'use strict';

var Blog= require('./blog.model');

exports.register = function(socket) {
//SAVE WORKS
  Blog.schema.post('save', function (doc) {
    onSave(socket, doc);
  });

// IS NOT TRIGGERED :(
 Blog.schema.post('findOneAndUpdate', function (doc) {
    onComment(socket, doc);
  });

  Blog.schema.post('remove', function (doc) {
    onRemove(socket, doc);
  });
}

//SAVE WORKS when a new blog is created
function onSave(socket, doc, cb) {
  socket.emit('blog:save', doc);
}

// IS NOT TRIGGERED :(
function onComment(socket, doc, cb) {
  socket.emit('blog:findOneAndUpdate', doc);
}

function onRemove(socket, doc, cb) {
  socket.emit('blog:remove', doc);
}

@vkarpov15 Спасибо за открытие. Означает ли это, что хука для findOneAndUpdate нет в 4.0.2, и вы планируете включить его в 4.0.3.

@vkarpov15 vkarpov15 Вот код, в котором я объявляю хуки:

...
var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
...
// Not executed
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
// Not executed
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

И вот где я вызываю обновление:

...
router.route('/:id')
.put(function(req, res, next) {
  TodoModel.findOneAndUpdate({_id: req.params.id, user: req.user.id}, req.body, {new: true}, function(err, post) {
    if(err) return next(err);
    if(post) {
      res.status(200).json(post);
    }
    else {
      next(newSystemError(errorCodes.TODO_NOT_FOUND, undefined, req.params.id));
    }
  });
});

Насколько я знаю, там должен быть хук findOneAndUpdate() , если он не работает, это баг.

@CaptainStaplerz попробуйте:

TodoSchema.pre('update', function(next) {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
  next();
});

Кроме того, выполняется ли оператор console.log или это просто часть Date.now(), которая дает неожиданные результаты?

@vkarpov15
Я изменил свой исходный код, добавив изменения, внесенные вами в Добавление реализации #964 https://github.com/Automattic/mongoose/commit/e98ef98e857965c4b2ae3339fdd7eefd2a5a9913

Теперь это работает как шарм. Поэтому я думаю, что исправление не проверено в основном

@honitus , вы уверены, что используете mongoose 4.0.2? Это изменение фактически зафиксировано в версии 4.0.0 и выше.

honitus$ npm просмотреть версию мангуста
4.0.2

@honitus , что вы делаете неправильно, так это то, что вы добавляете крючки в схему после компиляции своей модели. Ожидается, что Model.schema.pre('remove'); не будет работать, см. «Компиляция вашей первой модели» в документации по модели . Сначала прикрепите хуки к схеме, и тогда все должно работать — это единственная разница, которую я вижу между вашим кодом и нашими тестами.

@CaptainStaplerz единственный способ воспроизвести ваш код, который я могу найти, - это пустое обновление. Код ниже работает

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

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

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

Хук предварительного обновления больше не выполняется. Это соответствует тому, что вы видите?

@vkarpov15
Model.schema.pre('удалить'); это одна из заглушек, автоматически созданных угловым полным стеком
Я добавил реализацию #964 e98ef98.
Все, что я сделал, это добавил одну строку ниже
this._schema.s_.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,
См. строку 1526 в 4.0.2 — this.model.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,

Замените строку 1526 на this.schema.s.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,
и это работает.

Это по дизайну. Предполагается, что Mongoose не позволяет изменять хуки схемы после компиляции модели (то есть вызов mongoose.model() ), поэтому это больше не работает.

@ vkarpov15 Мне удалось заставить хук работать со следующим, должно быть, это была глупая опечатка или что-то еще:

TodoSchema.pre('findOneAndUpdate', function(next) {
  console.log('------------->>>>>> update updatedAt: ', this.updatedAt);
  this.updatedAt = Date.now();
  next();
});

Однако «это», похоже, не относится к обновленной модели (но относится к хуку «сохранить»?), таким образом, this.updatedAt относится к неопределенной.

Как обновить поле «updatedAt» в хуке «findOneAndUpdate»?

Я должен добавить больше документов, разъясняющих это: обновляемый документ может не существовать в памяти, когда вы вызываете findOneAndUpdate , поэтому объект this относится к запросу , а не к документу в промежуточном программном обеспечении запроса . Попробуйте this.update({ $set: { updatedAt: Date.now() } });

@vkarpov15 Мне не удалось заставить this.update({ $set: { updatedAt: Date.now() } }); работать, но я смог успешно обновить документ на хуке findOneAndUpdate следующим образом: this._update['$setOnInsert'].updatedAt=Date.now(); .

Я бы настоятельно рекомендовал не зависеть от такой настройки внутреннего состояния. Я очень удивлен, что вы не смогли заставить this.update() работать. Можете ли вы показать мне, как выглядит ваш крючок?

Конечно! (Обратите внимание, что я использую 4.0.2)

tagSchema.pre('findOneAndUpdate',function(next){
  var self = this;

  //NOTE THAT 'this' in the findOneAndUpdate hook refers to the query, not the document
  //https://github.com/Automattic/mongoose/issues/964

  geoData.country.findOne({'_id':self._update['$setOnInsert'].countryCode}).select('_id name cca2 cca3 ccn3').lean().exec(function(err,country){
    if (err){throw err;}
    if (!country){throw 'no coutnry';}
    self._update['$setOnInsert'].country=country;
    next();
  });
});

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

Да, я был сбит с толку и подумал, что вы используете update . Сценарий ниже

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.findOneAndUpdate({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

Работает правильно и выполняет нужный запрос:

Mongoose: todos.findAndModify({}) [] { '$set': { note: '1', updatedAt: new Date("Thu, 07 May 2015 20:36:39 GMT") } } { new: false, upsert: false }
Done

Поэтому, пожалуйста, используйте

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

Вместо того, чтобы вручную манипулировать внутренним состоянием mquery - это обычно плохая идея, если вы действительно не знаете, что делаете.

Это работает или нет? Единственный хук, который работает для меня, это «сохранить», остальное полностью игнорируется. У меня стоит 4.0.6. Спасибо

@agjs приведите пример кода, пожалуйста.

UserController.prototype.updateAvatar = function (req, res) {
    return new Promise(function (resolve, reject) {
        CompanyDetails.update({
            _author: req.user._id
        }, {
            avatarPath: req.files.file
        }, function (error, updated) {
            if (error) {
                reject(error);
            } else {
                resolve(updated);
            }
        });
    }).then(function (resolved) {
        res.sendStatus(204).send(updated);
    }).catch(function (error) {
        next(error);
    })

};

CompanyAvatarSchema.pre('update', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});

У меня есть еще один прехук на другой модели с помощью model.create и pre-save, и он работает нормально.

При любом обновлении он просто не видит хук, даже не записывает его в console.log. Я пробовал findOneAndUpdate и т. д., но на самом деле не работает... Забавно, я просмотрел всю эту ветку и, как обычно, проверил официальную документацию, и вы, ребята, утверждаете даже там, что это работает.

Какая связь между CompanyAvatarSchema и CompanyDetails в приведенном выше коде?

Сведения о компании имеют CompanyAvatar.schema в качестве поддокумента.

avatarPath: {
        type: [CompanyAvatar.schema],
        required: true
    }

Кроме того, его не только предварительный хук, но и проверка также полностью игнорируются. Этот вложенный документ заполняется, но игнорирует как проверку, так и предварительную перехватку. Я все гуглил, также пробовал ЭТО , но ничего не работает. Когда я просто для теста меняю свой запрос на создание и вызов модели с новым ака var parent = new Parent(), он работает.

Вы вызываете CompanyDetails.update() , но предварительная ловушка определена в отдельной схеме. ПО промежуточного слоя запросов не запускает банкомат pre('update') вложенной схемы.

Кроме того, предоставьте более подробный пример кода для вашего случая «проверка полностью игнорируется».

Вот схема аватара моей компании, сделанная для проверки и пре-хуков для пользователей, которые обновляют свой профиль (фото аватара):

'use strict';

let mongoose = require('mongoose'),
    mkdirp = require('mkdirp'),
    fs = require('fs'),
    path = require('path'),
    Schema = mongoose.Schema;

let CompanyAvatarSchema = new Schema({
    name: String,
    width: Number,
    height: Number,
    size: Number,
    type: String
});


CompanyAvatarSchema.path('type').validate(function (type) {
    return /^image\//.test(type);
}, 'Image type not allowed!');

CompanyAvatarSchema.path('size').validate(function (size) {
    return size < 5;
}, 'Image too big!');



CompanyAvatarSchema.virtual('path').set(function (path) {
    return this._path = path;
}).get(function () {
    return this._path;
});


CompanyAvatarSchema.virtual('public_path').get(function () {
    var parent = this.ownerDocument();
    var PATH = path.normalize('images/uploads/avatars/' + parent._id);
    if (this.name) {
        return path.join(PATH, this.name);
    }
});

CompanyAvatarSchema.set('toJSON', {
    getters: true
});

CompanyAvatarSchema.pre('findOneAndUpdate', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});


let runValidatorsPlugin = function (schema, options) {
    schema.pre('findOneAndUpdate', function (next) {
        this.options.runValidators = true;
        next();
    });
};

CompanyAvatarSchema.plugin(runValidatorsPlugin);

let CompanyAvatar = mongoose.model('CompanyAvatar', CompanyAvatarSchema);
module.exports = CompanyAvatar;

Вот схема company_details, где company_avatar является вложенным документом:

let CompanyDetailsSchema = new mongoose.Schema({
    _author: [{
        type: Schema.Types.ObjectId,
        ref: 'CompanyAccount'
    }],
    company_name: {
        type: String,
        es_indexed: true,
        es_boost: 2.0
    },
    contact_email: {
        type: String,
        es_indexed: true
    },
    website: {
        type: String,
        es_indexed: true
    },
    country: {
        type: String,
        es_indexed: true
    },
    industry: {
        type: String,
        es_indexed: true
    },
    address: {
        type: String,
        es_indexed: true
    },
    about: {
        type: String,
        es_indexed: true
    },
    avatarPath: {
        type: [CompanyAvatar.schema],

    }
});

А вот контроллер профиля обновления и avatarPath, которые должны быть проверены/перехвачены перед выполнением этого обновления:

UserController.prototype.updateAvatar = function (req, res, next) {
    let updates = {
        $set: {
            avatarPath: req.files.file
        }
    };
    return new Promise(function (resolve, reject) {
        CompanyDetails.findOneAndUpdate({
            _author: req.user._id
        }, updates, function (error) {
            if (error) {
                reject(error);
            } else {
                resolve('done');
            }
        });
    }).then(function () {
        res.sendStatus(204);
    }).catch(function (error) {
        next(error);
    });

};

По сути, мой mongodb заполняется полями из req.files.file, но в остальном проверка игнорируется, и перехватчики не работают.

Проблема в том, что промежуточное ПО pre('findOneAndUpdate') определено во вложенной схеме. Прямо сейчас mongoose запускает промежуточное ПО запросов только для схем верхнего уровня, поэтому промежуточное ПО, определенное на CompanyDetailsSchema , будет запущено re: #3125

+1

@vkarpov15 Я понимаю

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

работает, чтобы установить для свойства жесткое значение, но как мне _прочитать и изменить_ свойство, например, хешировать пароль?

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(.....?....) });
});

Есть предположения? Это довольно распространенный вариант использования, верно? Или люди обычно находят(), а затем отделяют save()?

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(this.getUpdate().$set.password) });
});

Это должно сработать, @willemmulder.

@vkarpov15 отлично, спасибо! Я попробую это сегодня вечером. Это также должно работать для pre('update') , верно?

@ vkarpov15 Итак, я только что попробовал, используя

schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

и если я console.log this я получаю

_update: { '$set': { password: '$2a$10$CjLYwXFtx0I94Ij0SImk0O32cyQwsShKnWh1248BpYsJLIHh7jb66', postalAddress: [Object], permissions: [Object], firstName: 'Willem', lastName: 'Mulder', email: '...@...', _id: 55ed4e8b6de4ff183c1f98e8 } },

что кажется прекрасным (и я даже пытался установить это свойство непосредственно раньше), но, в конце концов, оно на самом деле не записывает хэшированное значение в базу данных, а просто «сырое» значение. Есть ли что-нибудь, что я мог бы попробовать?

Это странно. Вы можете попробовать включить режим отладки мангуста с помощью require('mongoose').set('debug', true); и посмотреть, какой запрос отправляется в базу данных, что может пролить свет.

Спасибо за предложение. Только что сделал это:

Я запускаю это:

schema.pre('update', function(next) {
    this.update({}, { password: bcrypt.hashSync(this.getUpdate().$set.password) } );
    console.log(this.getUpdate());
    next();
});

который возвращает это для console.log

{ '$set':
   { password: '$2a$10$I1oXet30Cl5RUcVMxm3GEOeTFOLFmPWaQvXbr6Z5368zbfpA8nFEK',
     postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' },
     permissions: [ '' ],
     __v: 0,
     lastName: '',
     firstName: '',
     email: '[email protected]',
     _id: 563b0410bd07ce2030eda26d } }

а затем это для отладки Mongoose

Mongoose: users.update({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { '$set': { password: 'test', postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' }, permissions: [ '\u001b[32m\'\'\u001b[39m' ], __v: 0, lastName: '', firstName: '', email: '[email protected]', _id: ObjectId("563b0410bd07ce2030eda26d") } } { overwrite: false, strict: true, multi: false, upsert: false, safe: true }
Mongoose: users.findOne({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { fields: { password: 0 } }

Любая подсказка?

Не уверен, открыл новую проблему для отслеживания.

@ vkarpov15 Спасибо, отследим другую проблему.

@vkarpov15 vkarpov15 Я думаю, что правильный способ установить параметры текущего запроса в предварительном хуке будет примерно таким:

  finishSchema.pre('findOneAndUpdate', function (next) {
    this.setOptions({
      new: true,
      runValidators: true
    });
    this.update({}, {
      lastEdited: Date.now()
    });
    next();
  });

но в документации http://mongoosejs.com/docs/api.html#query_Query -setOptions не упоминается ни один из этих параметров. Если бы это считалось хакерским решением, что было бы более подходящим?

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

Можете ли вы открыть для этого отдельную тему?

@ vkarpov15 Да, это работает. Думаю, я недостаточно ясно выразился.

setOptions $ правильно применяет new и runValidators , я просто спросил, следует ли предпочтительнее устанавливать эти параметры через setOptions , чем this.options .

setOptions() предпочтительнее IMO, но оба должны работать. Или вы могли бы просто сделать

this.update({}, { lastEdited: Date.now() }, { new: true, runValidators: true });
schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

Это будет обновлять пароль при каждом вызове update(). Итак, если я просто изменю значение других свойств, например, имя или возраст, пароль также будет обновлен, что неверно!?

@nlonguit Думаю, так и будет. Но вы можете получить доступ через this к полям, которые будут обновлены, и вы можете сделать что-то вроде:

if (this._fields.password) { // <- I'm sure about this one, check in debugger the properties of this 
    this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
}

if (this._update.$set.password) { this.update({}, { $set: { password: bcrypt.hashSync(this.getUpdate().$set.password)} }); }

Этот код работает хорошо для меня. Спасибо @akoskm

Интересно, можно ли также добавить предварительный хук для findByIdAndUpdate. Было бы неплохо иметь в наличии оба крючка.

Я сделал это так, и это работает: просто findById, затем сохраните без обновления каких-либо полей, затем используйте метод findByIdAndUpdate:

dbModel.findById(barId, function (err, bar) {
        if (bar) {

            bar.save(function (err) {
                if (err) throw err;
            });
        }
    });
    dbModel.findByIdAndUpdate(barId, {$set:req.body}, function (err, bar) {
        if (err) throw err;
        res.send('Updated');
    });`

Я пытаюсь установить для свойства длину массива.

schema.post('findOneAndUpdate', function(result) {
    console.log(result.comments.length);
    this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length });
});

Записывается правильная длина, хотя запрос никогда не устанавливает totalNumberOfComments, и поле остается равным 0 (поскольку схема ссылается на значение по умолчанию: 0).

Когда я console.log(this) в конце хука, я вижу, что мой query содержит следующее:

_update: { '$push': { comments: [Object] }, totalNumberOfComments: 27 }

Хотя, когда я включаю режим отладки, Mongoose никогда не регистрирует запрос.

Я что-то не так делаю, или это баг?

@zilions this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length }).exec(); должен фактически выполнить запрос :) Только будьте осторожны, вы получите бесконечную рекурсию, потому что ваш хук сохранения сообщения вызовет другой хук сохранения сообщения

@vkarpov15 Ах, верно! Тогда я могу просто использовать this.update({} {....}).exec() вместо этого :)
Вопрос, однако, при использовании этого, он отлично устанавливает поле totalNumberOfComments , хотя также выполняет исходное обновление findOneAndUpdate .

Например:

Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}})

Запустит следующий хук:

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

Тем не менее, хук снова будет с $push на comments и на myNewComment , таким образом делая дублирующую запись.

Поскольку технически вы выполняете один и тот же запрос -

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

по сути такой же, как

var query = Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}});
query.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
query.findOneAndUpdate().exec();

Если вы хотите создать новый запрос с нуля, просто выполните

schema.post('findOneAndUpdate', function(result) {
    this.model.update({}, { // <--- `this.model` gives you access to the `Post` model
        totalNumberOfComments: result.comments.length
    }).exec();
}));

просто не трогай крючок перед сохранением,

router.put('/:id', jsonParser, function(req, res, next) {

  currentCollection.findByIdAndUpdate(req.params.id, req.body, function (err, item) {
    if (err) {
        res.status(404);
        return res.json({'error': 'Server Error', 'trace': err});
    }
    item.save(); // <=== this is were you save your data again which triggers the pre hook :)
    res.status(200); 
    return res.json({'message': 'Saved successfully'});
  });
});

Я обнаружил, что порядок, в котором вы определяете модель и определяете хук pre , имеет значение. Разрешите продемонстрировать:

Не работает:

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

Работает:

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

Надеюсь, это поможет любому!

Я только что узнал то же самое, что и @nicky-lenaers.

Он отлично работает с 'safe' . 'delete' . и т. д., если вы определяете крючки после определения модели.

Есть ли обходной путь, определяющий хук 'findOneAndUpdate' после определения модели?

@albert-92 нет, сейчас нет

Для тех, кто пытается получить что-то, что раньше было похоже

SCHEMA.pre('validate', function(done) {
    // and here use something like 
    this.yourNestedElement 
    // to change a value or maybe create a hashed character    
    done();
});

Это должно работать

SCHEMA.pre('findOneAndUpdate', function(done){
    this._update.yourNestedElement
    done();
});

Я не могу получить почтовые крючки для обновления документа в коллекции.

`module.exports = функция (мангуст) {
вар mySchema = мангуст.Схема({
идентификатор: {тип: число, индекс: {уникальный: правда}},
поле1: {тип: строка},
поле2: {тип: строка}
}, {
коллекция: "мояСхема",
ключ версии: ложь
});

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        field2: 'New Value'
    }).exec();
});
return mySchema;

}`

mySchema.findOneAndUpdate({id: 1}, {field1: 'test'}, {new: true});

Устанавливает для поля в коллекции значение {id:1, field1: 'test'), но должно быть {id: 1, field1: 'test', field2: 'Новое значение'}
Не уверен, что я делаю неправильно

Я могу изменить результат findOneAndUpdate, выполнив это
mySchema.post('findOneAndUpdate', function (result) { result.field2 = 'something' });

Я думаю, возможно, вы пытаетесь обновить модель с помощью элемента, который уже существует в модели. Или, возможно, вы выбираете его неправильно. Попробуйте распечатать «это» в вашем mySchema.post. Также в вашем сообщении, похоже, нет done() или next(). Я не очень хорошо разбираюсь в этом вопросе, но я знаю, что распечатав это, вы, по крайней мере, получите представление о том, с чем имеете дело.

Разве смысл обновления не в том, чтобы изменить существующий документ в вашей модели?

это объект запроса

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

Ну, у вас есть this.model.update, который является схемой, а не моделью объекта. Я думаю.. Это означает, что вам придется использовать

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        $set: { field2: 'New Value'}
    }).exec();
});
return mySchema;

Это кажется немного обратным вызовом функции модели внутри модели. Поскольку вы можете просто использовать части «этого» объекта, который вам дан. вам может быть лучше просто использовать findOneAndUpdate вместо того, чтобы вызывать его, а затем вызывать другую функцию модели поверх него.
в менеджере

var data = yourNewData;
self.findOneAndUpdate({id: something._id}, data, {safe: false, new: true})
    .exec()
    .then(resolve)
    .catch(reject);

В моем примере выше я использовал this._update, потому что это был объект обновления, который нужно было использовать вне этого.

Я пробовал использовать $set. Это все еще не меняет мой документ в коллекции.

Где я могу найти все доступные pre и post хуки?

Документы промежуточного программного обеспечения http://mongoosejs.com/docs/middleware.html

+1

Была ли эта страница полезной?
0 / 5 - 0 рейтинги