<p>mongoose 4.0.1: предварительное «обновление» промежуточного программного обеспечения, этот объект не возвращает объект модели</p>

Созданный на 30 мар. 2015  ·  40Комментарии  ·  Источник: Automattic/mongoose

Привет, у меня проблема с последней версией Mongoose. Я создаю API с Express и Mongoose 4.0.1, и я не уверен, что делаю что-то не так, но факт в том, что всякий раз, когда я пытаюсь использовать новое промежуточное программное обеспечение обновления pre таким же образом Я использую промежуточное ПО сохранения $# pre 1$#$, объект this не возвращает обновляемый объект, вместо этого он возвращает объект Query .

Пример того, что я пытаюсь объяснить:

ExerciseSchema.pre('save', function (next, done) {
        var self = this;
        console.log('exercise:', self); // returns exercise object
        // some validations here
       next();
});
ExerciseSchema.pre('update', function (next, done) {
        var self = this;
        console.log('exercise:', self); // returns Query object instead of exercise object
        // some validations here
       next();
});

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

{ _mongooseOptions: {},
  mongooseCollection: 
   { collection: { s: [Object] },
     opts: { bufferCommands: true, capped: false },
     name: 'exercises',
     conn: 
     ... },
   ...
}

Глядя на исходный код, некоторые из его свойств определены внутри функции Query , определенной в ./node_modules/mongoose/lib/query.js :

Это что-то неожиданное или я что-то не так делаю? Было бы интересно иметь решение, потому что мне не нравится идея проверки в промежуточном программном обеспечении при сохранении объекта и необходимости выполнять проверки непосредственно на контроллере при обновлении.

То же самое происходит с промежуточным программным обеспечением findOneAndUpdate pre , но я не ожидаю, что оно вернет упражнение, прежде чем его найдет. На самом деле и по моему честному мнению, я думаю, было бы интересно, чтобы промежуточное ПО findAndUpdate и findOneAndUpdate запускало промежуточное ПО find(One) и update в указанном порядке вместо того, чтобы иметь собственное промежуточное ПО.

Заранее спасибо за помощь.

won't fix

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

Ах, хорошо, я вижу, вы используете findOneAndUpdate() вместо update() . Это две разные функции с разными хуками, поэтому, если вы хотите добавить операции обновления в findOneAndUpdate в предварительном хуке, вы должны сделать

schema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { $set: { key: 'value' } });
});

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

По дизайну - обновляемый документ может даже не находиться в памяти сервера. Чтобы сделать это, мангусту пришлось бы выполнить findOne() для загрузки документа перед выполнением update(), что неприемлемо.

Дизайн позволяет вам манипулировать объектом запроса, добавляя или удаляя фильтры, обновляя параметры, параметры и т. д. Например, автоматически вызывая .populate() с помощью find() и findOne() , устанавливая multi: true опция по умолчанию на некоторых моделях, контроль доступа и другие возможности .

findOneAndUpdate() — это немного неправильное название, оно использует базовую команду mongodb findAndModify , это не то же самое, что findOne() + update() . Как отдельная операция, она должна иметь собственное промежуточное ПО.

Как бы вы тогда предложили обновить поле документа во время «предварительного» обновления промежуточного программного обеспечения?

this.update({ field: val }); добавит { $set: { field: val } } к операции обновления до того, как это произойдет.

Спасибо за ответ, на какой версии это должно работать? потому что я использую 4.0.2, а это не так. Должен ли я вызывать «exec» в запросе?

если я запущу this.update, разве я не буду вызывать этот метод http://mongoosejs.com/docs/api.html#query_Query -update?

В итоге я сделал это.findOneAndUpdate({matcher: "myvalue"}); надеюсь, что это правильный подход.

Хм 4.0.2 должно работать. Как выглядит ваш код?

Я только что сделал что-то вроде этого:

var schema = mongoose.Schema({
    name: String,
    description: String,
    matcher: String,

});

var generateMatcherUpdate= function(next) {
     var matcher = "generate matcher function"
        this.update({matcher: matcher});

    next();
};

 schema.pre('update', generateMatcherUpdate);

Ах, хорошо, я вижу, вы используете findOneAndUpdate() вместо update() . Это две разные функции с разными хуками, поэтому, если вы хотите добавить операции обновления в findOneAndUpdate в предварительном хуке, вы должны сделать

schema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { $set: { key: 'value' } });
});

Документация man mongoose ненавистно плоха. Может кто-нибудь, пожалуйста, положить это туда?

@askdesigners что именно туда добавили?

Извините, я написал это в момент стресса :)

Просто хрупкость совместимости версий на самом деле. Это не очевидная проблема, и многие люди ломают голову.

Не беспокойся. Какие у вас проблемы с совместимостью версий?

А что, если я хочу очистить значение в pre.update... Кажется, я не могу найти способ сделать это.
Спасибо!

schema.pre('update', function() {
  var v = this.getUpdate().valueToSanitize;
  this.update({}, { $set: { valueToSanitize: sanitize(v) } });
});

Хорошо, я попробую это!
Спасибо!!

Привет, @vkarpov15 , я наткнулся на кое-что, что я бы назвал неожиданным поведением, или, по крайней мере, это должно быть задокументировано.

Если я сделаю что-то вроде

MyModel.findOneAndUpdate({a: 1}, {v: '__3'}, function (err, model) {
    console.log(model);
});

И на своей модели реализую ваше предложение

mySchema.pre('findOneAndUpdate', function() {
  var v = this.getUpdate().v;
  this.findOneAndUpdate({}, { $set: { v: sanitize(v) } });
});

Журнал выведет «__3», так как исходный запрос на обновление будет выполнен после ловушки. Я могу изменить другие свойства схемы, но все поля, которые обновляются в исходном запросе, останутся прежними, даже если они были изменены в предварительной ловушке.

@CMatias , это тот случай, когда вам нужно быть осторожным с использованием $set или непоследовательно. Mongoose не обязательно знает, следует ли обернуть $set , поэтому в вашем случае вы должны сделать this.findOneAndUpdate({}, { v: sanitize(v) });

@vkarpov15 спасибо, сработало. Не могли бы вы кратко объяснить, в чем разница между использованием $set и его неиспользованием? Не могу найти много информации об этом.

Поэтому, если вы используете собственный драйвер mongodb напрямую и делаете coll.findOneAndUpdate({ a: 1 }, { __v: 3 }) , mongodb возьмет первый документ с a = 1 и заменит его документом { __v: 3 } по модулю _id . Другими словами, он перезапишет существующий документ. Чтобы просто установить ключ '__v', вам нужно сделать coll.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) .

Такое поведение всегда вызывало споры и приводило к ошибкам, поэтому по умолчанию mongoose предотвращает перезапись документа. Другими словами, MyModel.findOneAndUpdate({ a: 1 }, { __v: 3 }) становится MyModel.collection.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) _если_ вы не установите параметр overwrite: true . Тем не менее, вы можете делать странные вещи, такие как MyModel.update({}, { $set: { b: 2 } }).findOneAndUpdate({ a: 1 }, { __v: 3 }, { overwrite: true }) , которые делают его довольно запутанным относительно текущего состояния getUpdate() , поэтому мы просто оставляем статус update как есть. для промежуточного программного обеспечения.

Я пытаюсь понять все это, но мне трудно определить, могу ли я изменить документ, который будет возвращен из поиска, используя эти методы промежуточного программного обеспечения. В частности, я хотел бы «украсить» ответ, добавив дополнительные поля.

Например, если у меня есть схема:

const Thing = new mongoose.Schema({
  name: {type: String}
});

Thing.post('find', function(doc, next){
  doc.newfield = "example text";
  next();
}

Это возможно?

Причина, по которой newfield не определена в схеме, заключается в том, что в зависимости от значений Thing требуются разные имена полей.

Спасибо!

мне нужен какой-то конкретный плагин для использования schema.pre('update')?

Нет @jrogatis

@vkarpov15

Глядя на пример, который вы привели выше...:

schema.pre('update', function() {
  var v = this.getUpdate().valueToSanitize;
  this.update({}, { $set: { valueToSanitize: sanitize(v) } });
});

Это не работает для меня. this.getUpdate().value не содержит моего свойства value , как следует из примера. Вместо этого он доступен по this.getUpdate().$set.property .

Это должно быть так? Я что-то неправильно понимаю? Изменился ли API? Есть ли документация по этому поводу?

@qqilihq , вы можете открыть отдельную проблему со сценарием воспроизведения?

@qqilihq nvm, только что увидел, что ты это сделал :P

@varunjayaraman На самом деле отдельная проблема, которую я открыл, отличается от другой :) Но я с удовольствием открою новую для комментария выше, как только у меня будет несколько свободных минут для компиляции примера.

@qqilihq а, хорошо, спасибо!

Точно то же самое для меня, как упомянул @qqilihq

image

Таким образом, желаемое поведение может быть достигнуто с помощью этого кода:

userSchema.pre('update', function (next) {
  const newEmail = this.getUpdate().$set.email
  if (newEmail) {
    this.update({}, {$set: {email: normalizeEmail(newEmail)}})
  }
  next()
})

Но похоже я пытаюсь использовать какие-то поля, которые для этого не предназначены.

Метод pre не должен работать с this.update , потому что это «предварительное» сохранение и обновлять пока нечего, а this.newProp = "value" работает отлично, спасибо, ребята.

Мой случай:

UserSchema.pre('save', function(next) {
    this.created = Date.now()
    next()
})

Я понимаю причину наличия запроса «этот» в хуке обновления «до», но почему хук обновления «после» ведет себя аналогичным образом? Не должно ли в сообщении об обновлении фактическая модель быть «этой»?

Таким образом, post find будет выполняться несколько раз или иметь this в виде массива из нескольких результатов? А что, если результатов нет, должен ли post find не запускаться?

Все это очень странно. ИМХО Все пре-хуки должны вести себя одинаково, с this , который является изменяемым объектом, и хорошими методами, такими как isModified, для проверки вещей.

На самом деле я думаю, что теперь понимаю. Я должен просто использовать метод pre 'save', а затем метод this.isNew. Спасибо!

@ajbraus это немного странно, но альтернатива - огромные накладные расходы на производительность. Предположим, у вас есть updateMany, который обновляет миллионы документов — мангусту придется загрузить их все в память.

Как предполагается проверять поля документа перед обновлениями? Скажем, мне нужно проверить, заполнены ли определенные поля, чтобы установить документ как «действительный», как я могу получить доступ ко всему документу, а не только к обновляемым полям? this относится к запросу здесь

@PDS42 вам нужно будет выполнить отдельный запрос, чтобы загрузить документ из mongodb

Есть ли конкретная ссылка в документации мангуста, где все это объясняется более подробно? Я хотел бы прочитать об этом и понять это более полно :).

РЕДАКТИРОВАТЬ:

хуки предварительного обновления: https://github.com/Automattic/mongoose/issues/2812
промежуточное ПО: https://mongoosejs.com/docs/middleware.html
объект запроса: https://mongoosejs.com/docs/api.html#Query
запросы: https://mongoosejs.com/docs/queries.html

У меня возникли проблемы с поиском документации о том, как именно следует использовать запросы, на которые ссылается «это», в промежуточном программном обеспечении обновления. Например, что происходит, когда:
this.update({field: "value"}, {$set: {updatedAt: new Date()}}); Добавляет ли это {field: "value"} к фильтру? Или это не влияет? Есть ли документация, которую я не вижу?

@ronakvora есть функция Query#getConditions() .

schema.pre('update', function() {
  console.log(this.getConditions()); // Will include `{ field: 'value' }`
});

Попался, это поможет мне поиграть с вещами :). Спасибо!

Ps это Ронак сверху. По-видимому, у меня есть две учетные записи, зарегистрированные на разных устройствах.

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