Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose + Node.js, Object.assign (copy of data returned from database) shows additional data

Today I found an interesting thing, that I never knew before. I need help in understanding why this happened:

User.findOne({email: req.body.email}, function(err, usr){
     return res.json({
          RAW: usr,
          COPY: Object.assign({}, usr, {some: 'change'})
     }) 
})

This yield

{
    "RAW": {
        "createdAt": "2018-06-25T09:16:35.516Z",
        "_id": "5b30b2f36c492c55a818b455",
        "email": "[email protected]",
        "password": "$2b$08$k5IRBF.1i.q.7D/BD4HCVuOdnIRDQOHaT6icNwIyc1XfeklUwyF5.",
        "__v": 0
    },
    "COPY": {
        "$__": {
            "strictMode": true,
            "selected": {},
            "getters": {},
            "_id": "5b30b2f36c492c55a818b455",
            "wasPopulated": false,
            "activePaths": {
                "paths": {
                    "createdAt": "init",
                    "_id": "init",
                    "email": "init",
                    "password": "init",
                    "__v": "init"
                },
                "states": {
                    "ignore": {},
                    "default": {},
                    "init": {
                        "_id": true,
                        "email": true,
                        "password": true,
                        "createdAt": true,
                        "__v": true
                    },
                    "modify": {},
                    "require": {}
                },
                "stateNames": [
                    "require",
                    "modify",
                    "init",
                    "default",
                    "ignore"
                ]
            },
            "pathsToScopes": {},
            "emitter": {
                "domain": null,
                "_events": {},
                "_eventsCount": 0,
                "_maxListeners": 0
            },
            "$options": true
        },
        "isNew": false,
        "_doc": {
            "createdAt": "2018-06-25T09:16:35.516Z",
            "_id": "5b30b2f36c492c55a818b455",
            "email": "[email protected]",
            "password": "$2b$08$k5IRBF.1i.q.7D/BD4HCVuOdnIRDQOHaT6icNwIyc1XfeklUwyF5.",
            "__v": 0
        },
        "$init": true,
        "some": "change"
    }
}

See the difference between RAW and COPY. According to my understanding

  1. Object.assign() just creates a copy of an Object with the new memory address.
  2. Then RAW.email should be EQUAL to COPY.email (but it's not, WHY?)

Clearly, the COPY contains information from MongoDB, if RAW already had those data (hidden) then how can RAW.email gets the data whereas COPY.email is undefined.

HOW does this abstraction work in case of RAW?

like image 269
Siddhartha Chowdhury Avatar asked Jun 25 '18 10:06

Siddhartha Chowdhury


1 Answers

As Express res.json documentation states,

Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a JSON string using JSON.stringify().

And JSON.stringify uses toJSON method where available to get object values. Mongoose documents support it.

Since toJSON isn't own enumerable method of usr, it's omitted when it's shallowly copied with Object.assign({}, usr), so the object is treated as is by JSON.stringify and internal document properties are exposed.

It likely should be:

 res.json({
      RAW: usr,
      COPY: Object.assign({}, usr.toJSON(), {some: 'change'})
 }) 
like image 64
Estus Flask Avatar answered Oct 11 '22 12:10

Estus Flask