Как обновить / вставить документ в Мангусте?



возможно, это время, возможно, это я тону в скудной документации и не в состоянии обернуть голову вокруг концепции обновления в Мангусте:)



вот в чем дело:



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



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);
mongoose.model('Contact', ContactSchema); //is this line superflous??
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{...})

    Я попадаю в бесконечный цикл некоторых видов, так как реализация обновления Мангуста явно не хочет, чтобы объект был вторым параметром.

  4. если я делаю то же самое, но во втором параметре я пройти ассоциативный массив свойств запроса {status: request.status, phone: request.phone ...} это работает - но тогда у меня нет ссылки на конкретный контакт и я не могу узнать его createdAt и updatedAt свойства.


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



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

650   23  

23 ответов:

Мангуст теперь поддерживает Это изначально с 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");
});

Edit: Мангуст не поддерживает эти крючки с помощью этого метода:

  • по умолчанию
  • сеттеры
  • валидаторы
  • middleware

Я просто сжег твердые 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{...});

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

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

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

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

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

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

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 вызова DB вместо одного.
Надеюсь, что будущая реализация Мангуста придумает

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

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);
    });
});

Я создал учетную запись 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;
}

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

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

это решение может потребовать 2 дБ вызовов, однако вы получите преимущество конечно,

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

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

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

обновление-01/16/2016 Я добавил дополнительное условие, если есть массив примитивных значений, Мангуст не понимает, что массив обновляется без использования функции "set".

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

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

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

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

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!' });
            });

        });
    });

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

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

upsert используется для правильной работы на 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);
    });

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

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"});
    });

});
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);
            }
        });
    }
});

для тех, кто прибывает сюда все еще ищет хорошее решение для" upserting " с поддержкой крючков, это то, что я тестировал и работал. Он по-прежнему требует вызовов 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);
    });
  });
}
//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); 
  });
});


--

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

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();

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

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"
               })
       }
     }); 
   }); 
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
    if(err) return res.json(err);

    res.json({ success: true });
});

никакое другое решение не работает для меня. Я использую запрос post и обновляю данные, если они найдены, вставьте его, также _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');

});

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

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

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

Model.upsert({unique: 'value'}, updateObject});

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

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);
    });
})

этот coffeescript работает для меня с Node-трюк заключается в том, что _id get лишается своей оболочки ObjectID при отправке и возврате от клиента, и поэтому его нужно заменить для обновлений (когда нет _id, save вернется к insert и add one).

app.post '/new', (req, res) ->
    # post data becomes .query
    data = req.query
    coll = db.collection 'restos'
    data._id = ObjectID(data._id) if data._id

    coll.save data, {safe:true}, (err, result) ->
        console.log("error: "+err) if err
        return res.send 500, err if err

        console.log(result)
        return res.send 200, JSON.stringify result

чтобы построить на том, что Мартин Куздович опубликовал выше. Я использую следующее, чтобы сделать обновление с помощью мангуста и глубокого слияния объектов json. Вместе с моделью.функция save () в мангусте это позволяет Мангусту выполнять полную проверку даже того, который полагается на другие значения в json. для этого требуется пакет deepmerge https://www.npmjs.com/package/deepmerge. но это очень легкий вес упаковки.

var merge = require('deepmerge');

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

    const modelId = req.body.model_id;

    MyModel.findById(modelId).then((model) => {
        return Object.assign(model, merge(model.toObject(), req.body));
    }).then((model) => {
        return model.save();
    }).then((updatedModel) => {
        res.json({
            msg: 'model updated',
            updatedModel
        });
    }).catch((err) => {
        res.send(err);
    });
});

после Путешествующий Техникответ, который уже удивительным, мы можем создать плагин и прикрепить его к Мангусту, как только мы инициализируем его так, что .upsert() будет доступно на всех моделях.

Плагины.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 }) когда вы хотите.

прочитав сообщения выше, я решил использовать этот код:

    itemModel.findOne({'pid':obj.pid},function(e,r){
        if(r!=null)
        {
             itemModel.update({'pid':obj.pid},obj,{upsert:true},cb);
        }
        else
        {
            var item=new itemModel(obj);
            item.save(cb);
        }
    });

если r равно null, мы создаем новый элемент. В противном случае используйте upsert в update, поскольку обновление не создает новый элемент.

Comments

    Ничего не найдено.