1

I'm trying to get a property from MongoDB (mongoose) using the Model.findById() method. When I console.log the response, everything looks fine. But I cannot access it's properties.

//Fetch the conversations
var conversations = await ChatConversation.find({ members: req.token.mid }, 'members is_group').exec();
//Loop through the conversations to and modify each conversation
conversations = await Promise.all(conversations.map(async (conversation) => {
    if(conversation.is_group === false){
        // A conversation contains 2 members, get the user who is not logged in
        var other_id = conversation.members[0] == req.token.mid ? conversation.members[1] : conversation.members[0];
        //Fetch the other participant's details
        var other_participant = await User.findById(other_id, 'name photo');
        
        /*  PROBLEM HERE */
        console.log(other_participant)

        //Assign the newly fetch values
        conversation.name = other_participant.name;
        conversation.picture = other_participant.picture;
    }
    return conversation;
}))

This is the code snippet. The log I get is { _id: 60b23d231d6fb73eb43eb5f5, name: 'Ajith Gopi', photo: '' }

But if I try to access the name property like other_participant.name it gives undefined

I have tried to get the keys and values of other_participant using Object.keys(other_participant) and Object.values(other_participant), I get the following

Object.keys(other_participant):

[
  '$__',    'isNew',
  'errors', '$locals',
  '$op',    '_doc',
  '$init'
]

Object.values(other_participant):

[
  InternalCache {
    strictMode: true,
    selected: { name: 1, photo: 1 },
    shardval: undefined,
    saveError: undefined,
    validationError: undefined,
    adhocPaths: undefined,
    removing: undefined,
    inserting: undefined,
    saving: undefined,
    version: undefined,
    getters: {},
    _id: 60b23d231d6fb73eb43eb5f5,
    populate: undefined,
    populated: undefined,
    wasPopulated: false,
    scope: undefined,
    activePaths: StateMachine {
      paths: [Object],
      states: [Object],
      stateNames: [Array]
    },
    pathsToScopes: {},
    cachedRequired: {},
    session: undefined,
    '$setCalled': Set(0) {},
    ownerDocument: undefined,
    fullPath: undefined,
    emitter: EventEmitter {
      _events: [Object: null prototype] {},
      _eventsCount: 0,
      _maxListeners: 0,
      [Symbol(kCapture)]: false
    },
    '$options': { skipId: true, isNew: false, willInit: true, defaults: true }
  },
  false,
  undefined,
  {},
  null,
  { _id: 60b23d231d6fb73eb43eb5f5, name: 'Ajith Gopi', photo: '' },
  true
]

Mongoose Schema:

const User = mongoose.model('User', new mongoose.Schema({
    phone: Number,
    name: String,
    photo: String,
    date_created: { type: Date, default: Date.now },
}), 'users');

I'm not sure, But I suspect it has something to do with the Promise.all()...

NB: I have also tried replacing findById() with findOne(), And tried adding exec() in the end too... Same results

Ajith Gopi
  • 1,509
  • 1
  • 12
  • 20

2 Answers2

3

There is another solution to get POJO objects from Mongoose document: the lean() method

The lean option tells Mongoose to skip hydrating the result documents. This makes queries faster and less memory intensive, but the result documents are plain old JavaScript objects (POJOs), not Mongoose documents.

var other_participant = await User.findById(other_id, 'name photo').lean();
Đăng Khoa Đinh
  • 5,038
  • 3
  • 15
  • 33
  • It's working! Thanks a lot... I'm making a large scale application and this helped a lot :) – Ajith Gopi May 29 '21 at 20:39
  • I have one more question... Why can I access `conversation.is_group` without using lean() or toObject? Is it because I'm iterating through the result and It gives the actual object on every iteration? – Ajith Gopi May 29 '21 at 20:41
  • By default, we receive mongoose documents (which is different from Javascript object as you noticed), and all the properties reside in `document._doc`. You can try `conversation._doc.is_group` – Đăng Khoa Đinh May 29 '21 at 20:43
  • 1
    No, I mean.. I'm not using lean() or toObject for my first query (line 2). But on line 5, I'm directly accessing `conversation.is_group` and it's working fine. How might it be working... I wonder o.O – Ajith Gopi May 29 '21 at 20:46
  • Suprise moment... I don't know too :)) – Đăng Khoa Đinh May 29 '21 at 20:52
  • 1
    Got it... The iterator is overloaded in ES6 class... https://stackoverflow.com/questions/28739745/how-to-make-an-iterator-out-of-an-es6-class – Ajith Gopi May 29 '21 at 20:58
1

From the mongoose docs, I have found out that mongoose findById returns a query object. To access the elements, Either the query has to be converted to a JS object using .toObject(), or the individual properties can be accessed using .get('PROPERTY_NAME').

I have made it

var other_participant = (await User.findById(other_id, 'name photo').exec()).toObject();

And now I can directly access the properties.

Ajith Gopi
  • 1,509
  • 1
  • 12
  • 20