Документ не обновлен в findOneAndUpdate

У меня есть пост-маршрут, который получает данные из запроса PUT в экспресс-приложении, целью которого является обновление документа mongoose на основе введенной формы ввода. "Базовая" модель - это Profile, и у меня есть две модели дискриминатора Helper и Finder которые условно добавляют поля в схему Profile.

Таким образом, req.body.profile будет содержать различные поля в зависимости от дискриминатора он ассоциирован с, но всегда будет содержать поля (username, email city, accountType), присутствующие в "базовой" модели, Profile.

Перед отправкой запроса PUT пример документа в Profile выглядит следующим образом:

{ jobTitle: '',
  lastPosition: '',
  email: '',
  city: '',
  accountType: 'helper',
  _id: 5c77883d8db04c921db5f635,
  username: 'here2help',
  __v: 0 }

Это выглядит хорошо для меня и предполагает, что модель создается так, как я хочу (с базовыми полями из Profile и теми, которые связаны с моделью Helper - модели см. Ниже).

Мой маршрут POST выглядит следующим образом:

router.put("/profile/:id", middleware.checkProfileOwnership, function(req, res){

    console.log(req.body.profile);

    Profile.findOneAndUpdate(req.params.id, req.body.profile, function(err, updatedProfile){

        if(err){
            console.log(err.message);
            res.redirect("/profile");
        } else {
            console.log(updatedProfile);
            res.redirect("/profile/" + req.params.id);
        }

    });
});

Информация, которую я получаю из формы (console.log(req.body.profile)), - это то, что я ожидаю увидеть:

{ accountType: 'helper',
  username: 'here2help',
  email: '[email protected]',
  city: 'New York',
  jobTitle: 'CEO',
  lastPosition: 'sales rep'}

Однако после обновления документа с помощью req.body.profile в Profile.findOneAndUpdate() я не вижу обновленного возвращенного документа:

console.log(updatedProfile)

{ jobTitle: '',
  lastPosition: '',
  email: '[email protected]',
  city: 'New York',
  accountType: 'helper',
  _id: 5c77883d8db04c921db5f635,
  username: 'here2help',
  __v: 0 }

Итак, поля, которые определены в моей "Базовой" модели (т. ProfileSchema Те, которые определены в ProfileSchema - см. Ниже), обновляются (например, city), но те, которые есть в моих дискриминаторах - нет - см. Ниже.

Обновленная информация явно присутствует в req, но не распространяется на модель Profile Как это может быть?

Я также пытался использовать findByIdAndUpdate но я получаю тот же результат.

Вот схемы, которые я определяю:

Профиль - моя "базовая" схема:

var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");

var profileSchema = new mongoose.Schema({ 
    username: String,
    complete: { type: Boolean, default: false },
    email: { type: String, default: "" },
    city: { type: String, default: "" }
}, { discriminatorKey: 'accountType' });

profileSchema.plugin(passportLocalMongoose);

module.exports = mongoose.model("Profile", profileSchema);

искатель

var Profile = require('./profile');

var Finder = Profile.discriminator('finder', new mongoose.Schema({
    position: { type: String, default: "" },
    skills: Array
}));

module.exports = mongoose.model("Finder");

помощник

var Profile = require('./profile');

var Helper = Profile.discriminator('helper', new mongoose.Schema({
    jobTitle: { type: String, default: "" },
    lastPosition: { type: String, default: "" }
}));

module.exports = mongoose.model("Helper");

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

Пожалуйста, дайте мне знать, если это неясно, или мне нужно добавить больше информации.

Ответ 1

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

Дискриминаторы строят монго-запросы на основе объекта, который вы используете. Например, если вы включаете отладку на mongo с помощью mongoose.set('debug', true) и запускаете Profile.findOneAndUpdate(), вы должны увидеть что-то вроде:

Mongoose: profiles.findAndModify({
  _id: ObjectId("5c78519e61f4b69da677a87a")
}, [], {
  '$set': {
    email: '[email protected]',
    city: 'New York',
    accountType: 'helper',
    username: 'User NAme', __v: 0 } }, { new: true, upsert: false, remove: false, projection: {} })

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

Если вы используете Helper, вы получите что-то вроде:

profiles.findAndModify({
  accountType: 'helper',
  _id: ObjectId("5c78519e61f4b69da677a87a")
}, [], {
  '$set': {
    jobTitle: 'CTO',
    email: '[email protected]',
    city: 'New York',
    accountType: 'helper ', 
    username: 'User Name', __v: 0 } }, { new: true, upsert: false, remove: false, projection: {} })

Обратите внимание, что он добавляет поле дискриминатора в критерии фильтра, это задокументировано:

Модели дискриминатора являются специальными; они прикрепляют ключ дискриминатора к запросам. Другими словами, find(), count(), aggregate() и т.д. Достаточно умны, чтобы учитывать дискриминаторы.

Поэтому при обновлении вам нужно использовать поле дискриминатора, чтобы узнать, какую схему использовать при вызове оператора обновления:

app.put("/profile/:id", function(req, res){
console.log(req.body);

if(ObjectId.isValid(req.params.id)) {

  switch(req.body.accountType) {
    case 'helper':
    schema = Helper;
    break;
    case 'finder':
    schema = Finder;
    break;
    default:
    schema = Profile;
  }      

  schema.findOneAndUpdate({ _id: req.params.id }, { $set : req.body }, { new: true, upsert: false, remove: {}, fields: {} }, function(err, updatedProfile){
      if(err){
          console.log(err);
          res.json(err);
      } else {
          console.log(updatedProfile);
          res.json(updatedProfile);
      }

  });
} else {
  res.json({ error: "Invalid ObjectId"});
} });

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

Вы не можете обновить поле дискриминатора

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

db.profile.findOneAndUpdate({ _id: req.params.id }, { $set : req.body }, { new: true, upsert: false, remove: {}, fields: {} }, function(err, updatedProfile){
              if(err) {
                res.json(err);
              } else {
                console.log(updatedProfile);
                res.json(updatedProfile);      
              }
          });

Ответ 2

В методе findOneAndUpdate() метод findOneAndUpdate() имеет четыре параметра:

A.findOneAndUpdate(conditions, update, options, callback) // executes

И вам нужно выполнить так

var query = { name: 'borne' };
Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)

или даже

// is sent as
Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)

Это помогает предотвратить случайную перезапись документа { name: 'jason bourne' }.