node.jsmongodbmongoosepassport.js

Why does mongoose model's hasOwnProperty return false when property does exist?


I have this code :

user.findOne( { 'email' : email }, function( err, User )
            {
                if ( err )
                {
                    return done(err);
                }
                if ( !User )
                {
                    return done(null, false, { error : "User not found"});
                }
                if ( !User.hasOwnProperty('local') || !User.local.hasOwnProperty('password') )
                {
                    console.log("here: " + User.hasOwnProperty('local')); // displays here: false
                }
                if ( !User.validPass(password) )
                {
                    return done(null, false, { error : "Incorrect Password"});
                }
                return done(null, User);
            });

Since the app supports other kinds of authentication, I have a user model that has nested object called local which looks like

local : { password : "USERS_PASSWORD" }

So during login I want to check whether the user has provided a password but I encountered this interesting problem. My test object looks like this:

{ _id: 5569ac206afebed8d2d9e11e,
email: 'test@example.com',
phno: '1234567890',
gender: 'female',
dob: Wed May 20 2015 05:30:00 GMT+0530 (IST),
name: 'Test Account',
__v: 0,
local: { password: '$2a$07$gytktl7BsmhM8mkuh6JVc3Bs/my7Jz9D0KBcDuKh01S' } } 

but console.log("here: " + User.hasOwnProperty('local')); prints here: false

Where did I go wrong?


Solution

  • It's because the document object you get back from mongoose doesn't access the properties directly. It uses the prototype chain hence hasOwnProperty returning false (I am simplifying this greatly).

    You can do one of two things: use toObject() to convert it to a plain object and then your checks will work as is:

    var userPOJO = User.toObject();
    if ( !(userPOJO.hasOwnProperty('local') && userPOJO.local.hasOwnProperty('password')) ) {...}
    

    OR you can just check for values directly:

    if ( !(User.local && User.local.password) ) {...}
    

    Since neither properties can have a falsy value it should work for testing if they are populated.

    EDIT: Another check I forgot to mention is to use Mongoose's built in get method:

    if (!User.get('local.password')) {...}