【问题标题】:findOneAndUpdate overwriting attributes in 2+ level deep object passed as docfindOneAndUpdate 覆盖作为 doc 传递的 2 级以上深度对象中的属性
【发布时间】:2015-05-17 04:41:21
【问题描述】:

假设我有这个架构:

var UserSchema = new Schema({
    name : {
        firstName : String,
        lastName : String
    }
});

我创建了这个用户:

User.create({
    name : {
        firstName : Bob,
        lastName : Marley
    }
}, function (err, user) {
    if(err) {
        return console.error(err);
    } else {
        return console.log(user);
    }
});

我注意到如果我这样做:

User.findOneAndUpdate({_id: userId}, { name: { firstName : "Damian" } }, function (err, user) {
    if(err) {
        return console.error(err);
    } else {
        return console.log(user);
    }
});

我现在的用户是:

user = {
    name : {
        firstName : "Damian"
    }
}

但是,如果我这样做:

User.findOneAndUpdate({_id: userId}, { "name.firstName" : "Damian" }, function (err, user) {
    if(err) {
        return console.error(err);
    } else {
        return console.log(user);
    }
});

我的用户是:

user = {
    name : {
        firstName : "Damian",
        lastName : "Marley"
    }
}

有没有办法将未填写所有属性的对象传递给findOneAndUpdate,并保留以前存在的属性,而不删除它们? (与 Mongo 中的 $set 功能相同)。这真的很烦人...

【问题讨论】:

    标签: node.js mongoose


    【解决方案1】:

    使用flat 将不完整的嵌套对象展平,如下所示:

    var flatten = require('flat')
    
    flatten({
        name : {
            firstName : "Damian"
        }
    })
    
    // { 
    //   'name.firstName': 'Damian'
    // } 
    

    现在您可以像在第二个示例中一样调用findOneAndUpdate

    【讨论】:

    • 糟糕,我实际上误解了您的答案(建议从架构中删除其中一个字段......)并发布了我自己的答案 - 最后实际上使用了同一个库。它更复杂,所以我保留它,但我不确定是否真的更有帮助。 +1 并抱歉重复您的解决方案(由于疏忽大意)。
    • 请注意,扁平化可能会导致一些奇怪的事情,例如数据中的数组在 Mongo 文档中显示为对象。考虑像@​​987654324@ 这样的更新。然后flatten({things: [1, 2]}) 为您提供{ 'things.0': 1, 'things.1': 2 }db.getCollection('...').updateOne({_id: ObjectId('...')}, {$set: { 'things.0': 1, 'things.1': 2 }}) 将导致things(如果它不存在)被创建为具有键'0'和'1'的对象,而不是数组。我以痛苦的方式发现了这一点。使用 flat 的 safe 选项来防止这种情况发生。
    【解决方案2】:

    如果您将实际的完整嵌套对象传递给findOneAndUpdate()(或任何其他mongoose 更新方法):

    Model.findOneAndUpdate({ ... }, {
      name: {
        firstName : "Damian"
      }
    })
    

    您将覆盖 整个(嵌套)对象,从而从文档中删除其所有其他属性(在本例中为:lastName)。

    要仅更新嵌套对象的特定属性,而不是整个对象,您需要使用嵌套属性的完整路径。

    { "name.firstName" : "Damian" }
    

    (这将使嵌套的name 对象中的lastName 保持不变)

    但是,每当您更新嵌套对象的属性时,手动处理它可能会很烦人。

    幸运的是,展平您传递给 findOneAndUpdate() 的更新对象根本不是问题 - 所以它永远不会超过一层深度。

    有很多方法可以做到这一点,在这个问题中进行了辩论:Fastest way to flatten / un-flatten nested JSON objects

    最快的解决方案似乎是使用现成的flat Node.js 库。

    var flatten = require('flat')
    
    User.findOneAndUpdate(
      { _id: userId },
      // same as passing '{ "name.firstName": "Damian" }':
      flatten({
        name: {
          firstName : "Damian"
        }
      }), function (err, user) {
      if(err) {
        return console.error(err);
      } else {
        return console.log(user);
      }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 2021-06-20
      • 1970-01-01
      • 1970-01-01
      • 2015-04-25
      • 2010-12-02
      相关资源
      最近更新 更多