Как мне обновить / сохранить документ в 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 ]

4 голосов
/ 06 октября 2017

это сработало для меня.

app.put('/student/:id', (req, res) => {
    Student.findByIdAndUpdate(req.params.id, req.body, (err, user) => {
        if (err) {
            return res
                .status(500)
                .send({error: "unsuccessful"})
        };
        res.send({success: "success"});
    });

});
3 голосов
/ 08 июня 2017

Вот самый простой способ создания / обновления, в то же время вызывая промежуточное ПО и средства проверки.

Contact.findOne({ phone: request.phone }, (err, doc) => {
    const contact = (doc) ? doc.set(request) : new Contact(request);

    contact.save((saveErr, savedContact) => {
        if (saveErr) throw saveErr;
        console.log(savedContact);
    });
})
3 голосов
/ 13 сентября 2011
ContactSchema.connection.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);
            }
        });
    }
});

3 голосов
/ 27 октября 2014

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

// Create or update a Person by unique email.
// @param person - a new or existing Person
function savePerson(person, done) {
  var fieldsToUpdate = ['name', 'phone', 'address'];

  Person.findOne({
    email: person.email
  }, function(err, toUpdate) {
    if (err) {
      done(err);
    }

    if (toUpdate) {
      // Mongoose object have extra properties, we can either omit those props
      // or specify which ones we want to update.  I chose to update the ones I know exist
      // to avoid breaking things if Mongoose objects change in the future.
      _.merge(toUpdate, _.pick(person, fieldsToUpdate));
    } else {      
      toUpdate = person;
    }

    toUpdate.save(function(err, updated, numberAffected) {
      if (err) {
        done(err);
      }

      done(null, updated, numberAffected);
    });
  });
}
2 голосов
/ 19 февраля 2017

Я просто вернулся к этой проблеме через некоторое время и решил опубликовать плагин, основанный на ответе Аарона Маста.

https://www.npmjs.com/package/mongoose-recursive-upsert

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

Model.upsert({unique: 'value'}, updateObject});
2 голосов
/ 10 мая 2015

При наличии генераторов становится еще проще:

var query = {'username':this.req.user.username};
this.req.newData.username = this.req.user.username;
this.body = yield MyModel.findOneAndUpdate(query, this.req.newData).exec();
2 голосов
/ 07 ноября 2014
//Here is my code to it... work like ninj

router.param('contractor', function(req, res, next, id) {
  var query = Contractors.findById(id);

  query.exec(function (err, contractor){
    if (err) { return next(err); }
    if (!contractor) { return next(new Error("can't find contractor")); }

    req.contractor = contractor;
    return next();
  });
});

router.get('/contractors/:contractor/save', function(req, res, next) {

    contractor = req.contractor ;
    contractor.update({'_id':contractor._id},{upsert: true},function(err,contractor){
       if(err){ 
            res.json(err);
            return next(); 
            }
    return res.json(contractor); 
  });
});


--
2 голосов
/ 07 мая 2018

После ответа Traveling Tech Guy , который уже великолепен, мы можем создать плагин и прикрепить его к mongoose после его инициализации, чтобы .upsert() был доступен на всех моделях.

plugins.js

export default (schema, options) => {
  schema.statics.upsert = async function(query, data) {
    let record = await this.findOne(query)
    if (!record) {
      record = new this(data)
    } else {
      Object.keys(data).forEach(k => {
        record[k] = data[k]
      })
    }
    return await record.save()
  }
}

db.js

import mongoose from 'mongoose'

import Plugins from './plugins'

mongoose.connect({ ... })
mongoose.plugin(Plugins)

export default mongoose

Тогда вы можете сделать что-то вроде User.upsert({ _id: 1 }, { foo: 'bar' }) или YouModel.upsert({ bar: 'foo' }, { value: 1 }), когда захотите.

1 голос
/ 21 сентября 2017

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

router.post('/user/createOrUpdate', function(req,res){
    var request_data = req.body;
    var userModel = new User(request_data);
    var upsertData = userModel.toObject();
    delete upsertData._id;

    var currentUserId;
    if (request_data._id || request_data._id !== '') {
        currentUserId = new mongoose.mongo.ObjectId(request_data._id);
    } else {
        currentUserId = new mongoose.mongo.ObjectId();
    }

    User.update({_id: currentUserId}, upsertData, {upsert: true},
        function (err) {
            if (err) throw err;
        }
    );
    res.redirect('/home');

});
1 голос
/ 31 августа 2017
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
    if(err) return res.json(err);

    res.json({ success: true });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...