Mongoose findOneAndUpdate Upsert _id null?

Я хотел бы иметь один метод, который либо создает, либо обновляет документ для политики. Поиск и использование различных методов, таких как этот, я придумал нулевой _id для моего документа. Использование findByIdAndUpdate имеет аналогичный аффект.

Я вижу документ, вставленный в коллекцию, но поле _id равно null:

exports.savePolicy = function (plcy, callback) {
    console.log('priority is : ' + plcy.priority)
    try {
        var policy = new Policy(plcy);
        var query = {_id: plcy._id};  //this may be null
        var update = {
            name: plcy.name || defaults.policyDefaults.name,
            longDescription: plcy.longDescription || defaults.policyDefaults.longDescription,
            shortDescription: plcy.shortDescription || defaults.policyDefaults.shortDescription,
            priority: plcy.priority, colorHex: plcy.colorHex || defaults.policyDefaults.colorHex,
            settings: plcy.settings || [],
            parentPolicyId: plcy.parentPolicyId || null
        }

        Policy.findOneAndUpdate(query, update, {upsert: true}, function (err, data) {
            callback(err, data);
        });

    } catch (e) {
        log.error('Exception while trying to save policy: ' + e.message);
        callback(e, null);
    }

Есть ли что-то, что можно сделать для того, чтобы _id не был пустым, если это не обновление?

Ответ 1

null является допустимым значением _id в MongoDB, поэтому, если вы не хотите, чтобы он использовался в новых документах, вы должны убедиться, что значение null заменено новым ObjectID в query:

var query = {_id: plcy._id};
if (!query._id) {
    query._id = new mongoose.mongo.ObjectID();
}

// the rest stays the same...

Ответ 2

У меня была такая же проблема, и я не мог понять, как заставить ее работать. В конце концов я написал свой собственный метод upsert.

var upsert = function(model, data, f){
  if (!data._id) {
    model.create(data, f);
  } else {
    var id = data._id;
    delete data._id;
    model.findOneAndUpdate({_id: id}, data, f);
  }
}

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

upsert(Team, team, f);

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

Ответ 3

Я не использую Mongoose, но сталкиваюсь с аналогичной проблемой с MongoDB. Для действия Upsert при вставке нового объекта MongoDB установил null в _id.

Я звонил:

findOneAndUpdate({my_id:'my_unique_id'}, obj, {upsert: true})

где obj._id было undefined.

Проблема заключалась в том, что _id присутствовал в списке ключей Object.keys(obj). Я обнаружил, что я назначал obj._id = some_variable, где some_variable был undefined, и это вызывало _id для отображения в списке ключей.

Я применил обходное решение, позвонив прямо перед upsert:

if (_.isUndefined(obj._id)) {
  delete obj._id;
}

Ответ 4

Спасибо JohnnyHK за полезный ответ выше. Я придумал однострочный вкладыш, так как он так часто использовался:

query = args._id ? { _id: args._id } : { _id: new ObjectId() };

Он полагается на следующее:

const ObjectId = require('mongodb').ObjectID;

Ответ 5

Попробуйте установить для "нового" параметра значение true:

{ upsert: true, new: true }

Дополнительная информация: https://github.com/Automattic/mongoose/issues/2939

Ответ 6

Попробуйте установить для параметра upsert значение true в вашем вызове для обновления. Из документов mongoid: http://docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter

Дополнительно. Если установлено значение true, создается новый документ, если документ не соответствует критериям запроса. Значение по умолчанию - false, которое не вставляет новый документ, если совпадение не найдено.