Как мне обновить / сохранить документ в Mongoose? - PullRequest
313 голосов
/ 01 сентября 2011

Возможно, пришло время, возможно, я тону в редких документациях и не могу обернуться вокруг концепции обновления в Mongoose:)

Вот сделка:

У меня естьсхема и модель контакта (сокращенные свойства):

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var mongooseTypes = require("mongoose-types"),
    useTimestamps = mongooseTypes.useTimestamps;


var ContactSchema = new Schema({
    phone: {
        type: String,
        index: {
            unique: true,
            dropDups: true
        }
    },
    status: {
        type: String,
        lowercase: true,
        trim: true,
        default: 'on'
    }
});
ContactSchema.plugin(useTimestamps);
var Contact = mongoose.model('Contact', ContactSchema);

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

mongoose.connect(connectionString);
var contact = new Contact({
    phone: request.phone,
    status: request.status
});

А теперь мырешить проблему:

  1. Если я позвоню contact.save(function(err){...}) Я получу сообщение об ошибке, если контакт с таким же номером телефона уже существует (как и ожидалось - уникальный)
  2. Я могу 'Вызовите update() для контакта, так как этот метод не существует в документе
  3. Если я вызову обновление для модели:
    Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
    Я попаду в бесконечный цикл некоторых видов,поскольку реализация обновления Mongoose явно не хочет использовать объект в качестве второго параметра.
  4. Если я делаю то же самое, но во втором параметре я передаю ассоциативный массив свойств запроса {status: request.status, phone: request.phone ...}, это работает - нотогда я не имеюпривязанность к конкретному контакту и не может выяснить его свойства createdAt и updatedAt.

Итак, в конце концов, после всего, что я пытался: учитывая документ contact, как мне его обновитьесли он существует, или добавить его, если его нет?

Спасибо за ваше время.

Ответы [ 23 ]

366 голосов
/ 12 июля 2014

Mongoose теперь поддерживает это изначально с findOneAndUpdate (вызывает MongoDB findAndModify ).

Опция upsert = true создает объект, если он не существует. по умолчанию равно false .

var query = {'username':req.user.username};
req.newData.username = req.user.username;
MyModel.findOneAndUpdate(query, req.newData, {upsert:true}, function(err, doc){
    if (err) return res.send(500, { error: err });
    return res.send("succesfully saved");
});

В старых версиях Mongoose не поддерживает эти ловушки с помощью этого метода:

  • по умолчанию
  • сеттеры
  • валидаторы
  • 1022 * промежуточный слой *
178 голосов
/ 22 октября 2011

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

var contact = new Contact({
  phone: request.phone,
  status: request.status
});

// Convert the Model instance to a simple object using Model's 'toObject' function
// to prevent weirdness like infinite looping...
var upsertData = contact.toObject();

// Delete the _id property, otherwise Mongo will return a "Mod on _id not allowed" error
delete upsertData._id;

// Do the upsert, which works like this: If no Contact document exists with 
// _id = contact.id, then create a new doc using upsertData.
// Otherwise, update the existing doc with upsertData
Contact.update({_id: contact.id}, upsertData, {upsert: true}, function(err{...});

Я создал проблему на странице проекта Mongoose с просьбой добавить информацию об этом в документы.

89 голосов
/ 20 сентября 2011

Вы были близки с

Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})

но ваш второй параметр должен быть объектом с оператором модификации, например

Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})
66 голосов
/ 05 сентября 2011

Ну, я ждал достаточно долго и ответа не было.В конце концов отказались от всего подхода обновления / вставки и пошли с:

ContactSchema.findOne({phone: request.phone}, function(err, contact) {
    if(!err) {
        if(!contact) {
            contact = new ContactSchema();
            contact.phone = request.phone;
        }
        contact.status = request.status;
        contact.save(function(err) {
            if(!err) {
                console.log("contact " + contact.phone + " created at " + contact.createdAt + " updated at " + contact.updatedAt);
            }
            else {
                console.log("Error: could not save contact " + contact.phone);
            }
        });
    }
});

Это работает?Ага.Доволен ли я этим?Возможно нет.2 вызова БД вместо одного.
Надеемся, что в будущей реализации Mongoose появится функция Model.upsert.

22 голосов
/ 31 января 2017

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

app.put('url', (req, res) => {

    const modelId = req.body.model_id;
    const newName = req.body.name;

    MyModel.findById(modelId).then((model) => {
        return Object.assign(model, {name: newName});
    }).then((model) => {
        return model.save();
    }).then((updatedModel) => {
        res.json({
            msg: 'model updated',
            updatedModel
        });
    }).catch((err) => {
        res.send(err);
    });
});
14 голосов
/ 16 апреля 2015

Я создал учетную запись StackOverflow ПРОСТО, чтобы ответить на этот вопрос.После бесплодного поиска в сети я просто что-то написал сам.Вот как я это сделал, чтобы его можно было применить к любой модели мангуста.Либо импортируйте эту функцию, либо добавьте ее непосредственно в код, в котором выполняется обновление.

function upsertObject (src, dest) {

  function recursiveFunc (src, dest) {
    _.forOwn(src, function (value, key) {
      if(_.isObject(value) && _.keys(value).length !== 0) {
        dest[key] = dest[key] || {};
        recursiveFunc(src[key], dest[key])
      } else if (_.isArray(src) && !_.isObject(src[key])) {
          dest.set(key, value);
      } else {
        dest[key] = value;
      }
    });
  }

  recursiveFunc(src, dest);

  return dest;
}

Затем, чтобы вставить документ Mongoose, выполните следующие действия:

YourModel.upsert = function (id, newData, callBack) {
  this.findById(id, function (err, oldData) {
    if(err) {
      callBack(err);
    } else {
      upsertObject(newData, oldData).save(callBack);
    }
  });
};

Для этого решения может потребоватьсяТем не менее вы получаете 2 вызова БД:

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

Просто помните, что целевой объект всегда будет переопределять источник, даже если источник имеет существующее значение

Кроме того, для массивов, если существующий объект имеет более длинный массив, чем заменяющий его, тогда значения в конце старого массива останутся.Самый простой способ сохранить весь массив - это установить старый массив как пустой массив перед загрузкой, если это именно то, что вы собираетесь делать.

ОБНОВЛЕНИЕ - 16.01.2016 я добавилдополнительное условие, если существует массив примитивных значений, Mongoose не понимает, что массив обновляется без использования функции "set".

12 голосов
/ 18 ноября 2013

Мне нужно было обновить / вставить документ в одну коллекцию, и я создал новый объектный литерал, подобный следующему:

notificationObject = {
    user_id: user.user_id,
    feed: {
        feed_id: feed.feed_id,
        channel_id: feed.channel_id,
        feed_title: ''
    }
};

, составленный из данных, которые я получил откуда-то из моей базы данных, изатем вызовите update для Model

Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
    if(err){
        throw err;
    }
    console.log(num, n);
});

. Это вывод, который я получаю после первого запуска скрипта:

1 { updatedExisting: false,
    upserted: 5289267a861b659b6a00c638,
    n: 1,
    connectionId: 11,
    err: null,
    ok: 1 }

И это вывод, когда я запускаю скриптво второй раз:

1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }

Я использую версию Мангуста 3.6.16

9 голосов
/ 22 июля 2015
app.put('url', function(req, res) {

        // use our bear model to find the bear we want
        Bear.findById(req.params.bear_id, function(err, bear) {

            if (err)
                res.send(err);

            bear.name = req.body.name;  // update the bears info

            // save the bear
            bear.save(function(err) {
                if (err)
                    res.send(err);

                res.json({ message: 'Bear updated!' });
            });

        });
    });

Вот лучший подход к решению метода обновления в mongoose, вы можете проверить Scotch.io для более подробной информации. Это определенно сработало для меня !!!

8 голосов
/ 16 мая 2014

В версии 2.6 появилась ошибка, которая также распространяется на 2.7

Упсерт корректно работал на 2,4

https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnY https://jira.mongodb.org/browse/SERVER-13843

Взгляните, в нем содержится важная информация

ОБНОВЛЕНИЕ:

Это не значит, что upsert не работает. Вот хороший пример того, как его использовать:

User.findByIdAndUpdate(userId, {online: true, $setOnInsert: {username: username, friends: []}}, {upsert: true})
    .populate('friends')
    .exec(function (err, user) {
        if (err) throw err;
        console.log(user);

        // Emit load event

        socket.emit('load', user);
    });
5 голосов
/ 07 мая 2018

Вы можете просто обновить запись с этим и получить обновленные данные в ответ

router.patch('/:id', (req, res, next) => {
    const id = req.params.id;
    Product.findByIdAndUpdate(id, req.body, {
            new: true
        },
        function(err, model) {
            if (!err) {
                res.status(201).json({
                    data: model
                });
            } else {
                res.status(500).json({
                    message: "not found any relative data"
                })
            }
        });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...