简体   繁体   中英

Why can't I delete a mongoose model's object properties?

When a user registers with my API they are returned a user object. Before returning the object I remove the hashed password and salt properties. I have to use

user.salt = undefined;
user.pass = undefined;

Because when I try

delete user.salt;
delete user.pass;

the object properties still exist and are returned.

Why is that?

To use delete you would need to convert the model document into a plain JavaScript object by calling toObject so that you can freely manipulate it:

user = user.toObject();
delete user.salt;
delete user.pass;

Non-configurable properties cannot be re-configured or deleted.

You should use strict mode so you get in-your-face errors instead of silent failures:

(function() {
    "use strict";
     var o = {};
     Object.defineProperty(o, "key", {
         value: "value",
         configurable: false,
         writable: true,
         enumerable: true
     });
     delete o.key;
})()
// TypeError: Cannot delete property 'key' of #<Object>

Rather than converting to a JavaScript object with toObject() , it might be more ideal to instead choose which properties you want to exclude via theQuery.prototype.select() function.

For example, if your User schema looked something like this:

const userSchema = new mongoose.Schema({

    email: {

        type: String,
        required: true,
    },
    name: {

        type: String,
        required: true
    },
    pass: {

        type: String,
        required: true
    },
    salt: {

        type: String,
        required: true
    }
});

module.exports = {

    User: mongoose.model("user", userSchema)
};

Then if you wanted to exclude the pass and salt properties in a response containing an array of all users, you could do so by specifically choosing which properties to ignore by prepending a minus sign before the property name:

users.get("/", async (req, res) => {

    try {

        const result = await User
            .find({})
            .select("-pass -salt");

        return res
            .status(200)
            .send(result);
    }
    catch (error) {

        console.error(error);
    }
});

Alternatively, if you have more properties to exclude than include, you can specifically choose which properties to add instead of which properties to remove:

        const result = await User
            .find({})
            .select("email name");

The delete operation could be used on javascript objects only. Mongoose models are not javascript objects. So convert it into a javascript object and delete the property. The code should look like this:

const modelJsObject = model.toObject();
delete modlelJsObject.property;

But that causes problems while saving the object. So what I did was just to set the property value to undefined.

model.property = undefined;

Old question, but I'm throwing my 2-cents into the fray....

You question has already been answered correctly by others, this is just a demo of how I worked around it.

I used Object.entries() + Array.reduce() to solve it. Here's my take:

// define dis-allowed keys and values
const disAllowedKeys = ['_id','__v','password'];
const disAllowedValues = [null, undefined, ''];

// our object, maybe a Mongoose model, or some API response
const someObject = {
  _id: 132456789,
  password: '$1$O3JMY.Tw$AdLnLjQ/5jXF9.MTp3gHv/',
  name: 'John Edward',
  age: 29,
  favoriteFood: null
}; 

// use reduce to create a new object with everything EXCEPT our dis-allowed keys and values!
const withOnlyGoodValues = Object.entries(someObject).reduce((ourNewObject, pair) => {
  const key = pair[0];
  const value = pair[1]; 
  if (
    disAllowedKeys.includes(key) === false &&
    disAllowedValues.includes(value) === false
  ){
    ourNewObject[key] = value; 
  }
  return ourNewObject; 
}, {}); 

// what we get back...
// {
//   name: 'John Edward',
//   age: 29
// }

// do something with the new object!
server.sendToClient(withOnlyGoodValues);

This can be cleaned up more once you understand how it works, especially with some fancy ES6 syntax. I intentionally tried to make it extra-readable, for the sake of the demo.

Read docs on how Object.entries() works: MDN - Object.entries() Read docs on how Array.reduce() works: MDN - Array.reduce()

除了调用toObject之外的另一个解决方案是直接从_doc对象访问_doc并使用 ES6 扩展运算符删除不需要的属性,如下所示:

user = { ...user._doc, salt: undefined, pass: undefined }

I use this little function just before i return the user object. Of course i have to remember to add the new key i wish to remove but it works well for me

const protect = (o) => {
    const removes = ['__v', '_id', 'salt', 'password', 'hash'];
    m = o.toObject();
    removes.forEach(element => {
        try{
            delete m[element]
        }
        catch(O_o){}
    });
    
return m
}

and i use it as I said, just before i return the user.

return res.json({ success: true, user: await protect(user) });

Alternativly, it could be more dynamic when used this way:

const protect = (o, removes) => {
        m = o.toObject();
        removes.forEach(element => {
            try{
                delete m[element]
            }
            catch(O_o){}
        });
        
    return m
    }

 return res.json({ success: true, user: await protect(user, ['salt','hash']) });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM