【问题标题】:How to update some but not all fields in Mongoose如何更新猫鼬中的一些但不是所有字段
【发布时间】:2021-02-25 07:55:59
【问题描述】:

这是用户架构:

var UserSchema = new Schema({
    username: { type: String, required: true, index:{unique: true} },
    firstName: { type: String, required: true },
    lastName: { type: String, required: true },
    email: { type: String, required: true, index:{unique: true} }, 
    password: { type: String, required: true, select: false }
});

这里是 http PUT 请求:

  // update user information
  api.put('/users/:username', function(req, res) {

    User.findOne({username: req.params.username}, function(err, user) {

      if (err){
        res.send(err);
        return;
      }

      if (!user){
        res.status(404).send({
          success: false,
          message: "user not found"
        });
      } else {
        user.username = req.body.username;
        user.email = req.body.email;
        user.password = req.body.password;
        user.firstName = req.body.firstName;
        user.lastName = req.body.lastName;

        user.save(function(err) {
          if (err){
            res.send(err);
            return;
          }

          res.json({
            success: true,
            message: "user information updated."
          });
        });
      }
    });
  });

问题是,如果用户只想更新有限的字段,比如只更新用户名,那么上面的代码是行不通的,报错如下:

{
  "message": "User validation failed",
  "name": "ValidationError",
  "errors": {
    "lastName": {
      "properties": {
        "type": "required",
        "message": "Path `{PATH}` is required.",
        "path": "lastName"
      },
      "message": "Path `lastName` is required.",
      "name": "ValidatorError",
      "kind": "required",
      "path": "lastName"
    },
    "firstName": {
      "properties": {
        "type": "required",
        "message": "Path `{PATH}` is required.",
        "path": "firstName"
      },
.........

那么我怎样才能实现允许用户更新某些但不是所有字段?

感谢任何 cmets 和建议!

【问题讨论】:

    标签: node.js mongodb express mongoose


    【解决方案1】:

    在更新对象中使用findOneAndUpdate 和运算符$set

    User.findOneAndUpdate({username: req.params.username}, { $set: req.body }, { new: true }, callback);
    

    $set 将允许您仅修改 req.body 对象中提供的字段。

    【讨论】:

    • 如果不假设 req.body 已被清理或模型具有足够的验证规则,这可能是一个非常糟糕的模式。虽然是的,但它会更新req.body 中提供的字段,但您无法控制这些字段是什么。任何更新都可以在没有上述清理验证的情况下更新任何字段。
    • @JasonCust 你上面的解决方案根本不起作用
    【解决方案2】:

    这是一个很好的折衷方案:

    指定用户可以更新的字段

    let fieldToUpdate = {
        name: req.body.name,
        email: req.body.email,
      };
    

    然后删除所有包含假值的键

      for (const [key, value] of Object.entries(fieldToUpdate)) {
        if (!value) {
          delete fieldToUpdate[key];
        }
      }
    

    然后使用 $set 运算符更新值

    const user = await User.findByIdAndUpdate(
        req.user.id,
        { $set: { ...fieldToUpdate } },
        {
          runValidators: true,
          new: true,
        }
      );
    

    【讨论】:

      【解决方案3】:

      我的解决办法是:

      const dot = require('dot-object'); // this package works like magic
      
      const updateData = { some: true, fields: true };
      
      User.updateOne(
        { _id: req.user._id },
        { $set: dot.dot(updateData) },
        (err, results) => {
          if (err) res.json({ err: true });
          else res.json({ success: true });
        }
      );
      

      我在:https://github.com/Automattic/mongoose/issues/5285 上找到了这个提示(点包)

      【讨论】:

        【解决方案4】:

        据我了解,您希望能够更新任意数量的字段。下面的代码来自过去的项目。

        型号

        const ingredientSchema = mongoose.Schema({
            _id: mongoose.Schema.Types.ObjectId,
            name: { type:String, required: true },   
            quantity: { type: Number, default: 0}
        });
        

        HTTP 放置

        router.put('/:ingredientId', (req, res, next) => {
            // extracting ingredient id from url parameters
            const id = req.params.ingredientId;
        
            //creating a map from the passed array
            const updateOps = {};
            for(const ops of req.body){
                updateOps[ops.propName] = ops.value;
            }
        
            //updating the found ingredient with the new map
            Ingredient.update({_id: id}, { $set: updateOps})
                .exec()
                .then(result =>{
                    console.log(result);
                    //returning successful operation information
                    res.status(200).json(result);
                })
                //catching any errors that might have occured from above operation
                .catch(err => {
                console.log(err);
                    //returning server error
                    res.status(500).json({
                        error: err
                    });
                });
        });
        

        PUT 请求 (json)

        [
            {"propName": "name", "value": "Some other name"},
            {"propName": "quantity", "value": "15"},
        ]
        

        或者如果你想更新一个字段

        [
            {"propName": "name", "value": "Some other name"}
        ]
        

        基本上,您有一个包含这些属性/字段名称及其新值的数组。如果您愿意,您可以通过这种方式仅更新其中一个或全部。或者我都不相信。

        希望这会有所帮助!有什么问题就问吧!

        【讨论】:

          【解决方案5】:

          您可以使用'findOneAndUpdate' 方法。

          User.findOneAndUpdate({username: req.params.username}, {username: req.body.username}, function(err, user) {
            //...
          });
          

          【讨论】:

          • 非常感谢@Jason Cust,我想我有一些问题要理解findOneAndUpdate(...),您提供的sn-p 只能更改用户名,对吗?如果我不知道用户想要更新哪个字段怎么办?谢谢。
          • 可以将原始帖子正文作为更新对象传递,但您真的想这样做吗?如果您只想更新某些字段怎么办?您可以检查正文以查看存在的内容,并仅允许可以更新的字段包含在更新对象中。
          • 是的,我认为你是对的,这真的很有道理。现在可以了,再次感谢您。
          • @PrathameshMore 你能扩展你的评论吗?
          猜你喜欢
          • 2020-07-13
          • 2016-02-17
          • 1970-01-01
          • 2021-12-10
          • 1970-01-01
          • 2017-11-22
          • 2018-08-26
          • 2020-02-17
          • 1970-01-01
          相关资源
          最近更新 更多