【问题标题】:When updating a user profile, some info gets erased from DB, Mongodb更新用户配置文件时,一些信息会从 DB、Mongodb 中删除
【发布时间】:2019-03-21 16:59:31
【问题描述】:

当我更新我的用户资料时,我的用户模型上位置内部的字段附近被删除,这是我的用户模型,您可以在其中看到位置内部的附近:

const userSchema = new Schema({

  name: {
    type: String,
    required: 'Please supply a name',
    trim: true
  },
   location: {
    type: {
      type: String,
      default: 'Point'
    },
    coordinates: [{
      type: Number,
      required: 'You must supply coordinates!'
    }],
    address: {
      type: String,
      required: 'You must supply an address!'
    },
    vicinity: {
      type: String,
    },
  },
});

然后我有一个更新用户的控制器,其中我有一个更新对象,并且附近不存在,因为我不想更新它,我只想将附近保存在寄存器上,而不是更新它:

exports.updateAccount = async (req, res) => {
  req.body.location.type = 'Point';

  const updates = {
    email: req.body.email,
    name: req.body.name,
    photo: req.body.photo,
    genres: req.body.genres,
    musicLinks: req.body.musicLinks,
    location: {
      type: req.body.location.type,
      coordinates: [
        req.body.location.coordinates[0],
        req.body.location.coordinates[1],
      ],
      address: req.body.location.address,
      // vicinity: req.body.location.vicinity,
    }
  };

  if(!updates.photo) delete updates.photo

  const user = await User.findOneAndUpdate(
    { _id: req.user._id },
    { $set: updates },
    { new: true, runValidators: true, context: 'query' }
  );
  req.flash('success', 'Updated the profile!');
  res.redirect('back');
};

但是每次我更新用户个人资料时,邻近字段都会被删除,还要注意,在我为用户提交更新的表单中,没有邻近字段,因此它不会发送任何数据进行更新。

我猜是因为在控制器上我有一个更新对象,例如:

 location: {
      type: req.body.location.type,
      coordinates: [
        req.body.location.coordinates[0],
        req.body.location.coordinates[1],
      ],
      address: req.body.location.address,
      // vicinity: req.body.location.vicinity,
    }

及其缺失的 vecinity。数据库会尝试保存整个位置对象,但由于附近没有它会被删除。

如果是这样的话.. 我如何对 mongo db 说要保留 db 上的值而不是删除它?谢谢

【问题讨论】:

    标签: node.js mongodb express mongoose


    【解决方案1】:

    您遇到此问题的原因是您实际上是在使用新对象更新 location 字段。 location 对象下的每个键都被擦除然后重写 - 这就是 vicinity 丢失的原因。

    要实现您的目标,您可以使用嵌套键语法:

    const updates = {
      $set: {
        email: req.body.email,
        name: req.body.name,
        photo: req.body.photo,
        genres: req.body.genres,
        musicLinks: req.body.musicLinks,
        'location.type': req.body.location.type,
        'location.coordinates': [req.body.location.coordinates[0], req.body.location.coordinates[1]],
        'location.address': req.body.location.address,
      }
    };
    

    之后:

    const user = await User.findOneAndUpdate(
      { _id: req.user._id },
      updates,
      { new: true, runValidators: true, context: 'query' }
    );
    

    【讨论】:

      【解决方案2】:

      这是设计使然。

      实际上,您在 $set 的参数中包含了 location 的“完整对象”,而这确实只是覆盖该对象。这基本上就是 MongoDB 的工作原理。

      MongoDB 工作方式的另一部分是,当您打算部分使用“嵌套路径”更新文档时,您需要将该对象结构处理为 "Dot Notation" 表单。

      事实上,这是我很久以前为了做到这一点而整理的基本版本:

      var data = {
          email: "bill@example.com",
          name: "Bill S Preston",
          photo: "",
          genres: "Music, Lyrics",
          musicLinks: "",
          location: {
            type: "Point",
            coordinates: [
              0,
              0
            ],
            address: "somewhere"
          }
      };
      

      还有一个简单的功能:

      function dotNotate(obj,target,prefix) {
        target = target || {},
        prefix = prefix || "";
      
        Object.keys(obj).forEach(function(key) {
          if ( Array.isArray(obj[key] )) {
            return target[prefix + key] = obj[key];
          } else if ( typeof(obj[key]) === "object" ) {
            dotNotate(obj[key],target,prefix + key + ".");
          } else {
            return target[prefix + key] = obj[key];
          }
        });
      
        return target;
      }
      

      运行该函数会产生:

      var updates = dotNotate(data);
      
      {
              "email" : "bill@example.com",
              "name" : "Bill S Preston",
              "photo" : "",
              "genres" : "Music, Lyrics",
              "musicLinks" : "",
              "location.type" : "Point",
              "location.coordinates" : [
                      0,
                      0
              ],
              "location.address" : "somewhere"
      }
      

      当然{ $set: updates } 只会部分更新明确指定的字段而不覆盖。

      【讨论】:

      • 我如何使用你的功能?我假设 obj 是数据..但是我应该在目标和前缀参数中输入什么?非常感谢! :)
      • @GiorgioMartini 除非您特别想这样做(即嵌套在 another 对象中),否则您不需要这样做。根据答案中显示的示例,您的使用只需要提供当前正在获取的请求中的整个对象。这些额外的函数参数大部分在那里,因为它是递归的并且可以通过多个深度工作。
      猜你喜欢
      • 1970-01-01
      • 2013-09-29
      • 1970-01-01
      • 1970-01-01
      • 2018-07-12
      • 1970-01-01
      • 1970-01-01
      • 2020-07-31
      • 2013-10-02
      相关资源
      最近更新 更多