2

I'm new to mongoose, and I'm trying to create an app that uses the OpenWeatherMap API. After requesting the data form the API, I save them to my MongoDB and then I want to return the results as a json, so I call the following function:

async function saveForecast(data) {

// Code here for creating the "forecastList" from the data and fetching the "savedLocation" from the DB

const newForecast = new Forecast({
    _id: new mongoose.Types.ObjectId(),
    location: savedLocation,
    city: {
        id: data.city.id,
        name: data.city.name,
        coordinates: data.city.coord,
        country: data.city.country
    },
    forecasts: forecastList
});

try {
    const savedForecast = await newForecast.save();
    return savedForecast.populate('location').lean().exec(); //FIXME: The lean() here throws  TypeError: savedForecast.populate(...).lean is not a function
} catch (err) {
    console.log('Error while saving forecast. ' + err);
}
}

The "newForecast" is being saved successfully in the DB, however when I try adding the .lean() after my populate I get the following error: TypeError: savedForecast.populate(...).lean is not a function

I've used lean() on find queries and it works fine, but I can't get it to work with my "newForecast" object, even though the "savedForecast" is a mongoose document, as the debugger shows me.

Any ideas why lean() is not working? Thank you!

2
  • which version of mongoose are you using? Commented Oct 23, 2018 at 13:54
  • I'm using Mongoose 5.2.15, and after updating to 5.3.6 the same error occurs. Commented Oct 23, 2018 at 15:07

1 Answer 1

5

The issue comes from the fact that Document does not have lean() method.

await newForecast.save(); does not return a Query but a Document. Then running populate on a Document also returns Document. To convert Document to a plain JS object you have to use Document.prototype.toObject() method:

try {
    const savedForecast = await newForecast.save();
    return savedForecast.populate('location').toObject(); // Wrong! `location` is not populated!
} catch (err) {
    console.log('Error while saving forecast. ' + err);
}

However this code will execute wrongly - population will not be called, because populate must receive a callback argument or execPopulate (which returns a Promise) has to be called on it. As far as you are using async/await I would suggest to use execPopulate instead of a callback. And last but not least - populated location needs to be leaned:

try {
    const savedForecast = await newForecast.save();
    return await savedForecast
      .populate({ path: 'location', options: { lean: true }})
      .execPopulate()
      .then(populatedForecast => populatedForecast.toObject());
} catch (err) {
    console.log('Error while saving forecast. ' + err);
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so much! It finally works. I have misunderstood the use of lean() (and .toObject() for that matter). The weird thing is that my populate was working fine (when not calling .toObject() afterwards), so I got everything mixed up with promises returned by Mongoose. Again, thank you :)
I know this is an old post. But I couldn't answer to my situation. When I log the populated field, I can see it as with all its keys on terminal. But if I try to access fields of it like appointment.populatedField.firstName it says Property 'firstName' does not exist on type 'string'. Populated field seen as string. After wasting many hours, I still have no idea what's the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.