Search code examples
node.jsdatabasemongoosemodel

Why populate and exec do not work with aync/await functions?


I have 2 mongoose models Task and User, and I have established a relationship between them. In the task,

I referenced the owner of that task using the ref as follows:

const taskSchema = new Schema({
    description: {
        type: String,
        trim: true,
        required: true
    },
    completed: {
        type: Boolean,
        default: false,
    },
    owner: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    }
});

And in the User I use the virtual property feature to get the user's tasks when needed :

userSchema.virtual("tasks", {
    ref: "Task",
    localField: "_id",
    foreignField: "owner",
});

I would like to know why the exec function is not working with async/await following function :

const main = async () => {
    const task = await Task.findById("6420aeeebda4e48924667034");

    task.populate('owner').exec()
        .then((task) => {
            console.log(task.owner)
        });
}

where it returns an error saying: exec is not a function.

while the following non async function works :

const main =  () => {
        Task.findById("6420aeeebda4e48924667034").populate('owner').exec()
        .then((task) => {
            console.log(task.owner)
        });
}

Same thing when trying to populate the tasks of a user that are associated with him/her.

Also, I would like to know why the mongoose latest version docs uses the following example that by the way did not work, and the IDE indicated that the exec returns a Promise :

Person.
  findOne({ name: 'Ian Fleming' }).
  populate('stories'). // only works if we pushed refs to children
  exec(function(err, person) {
    if (err) return handleError(err);
    console.log(person);
  });

Solution

  • Your example is mixing together two different function syntaxes. It should work if you leave out .exec():

    const main = async () => {
        const task = await Task.findById("6420aeeebda4e48924667034");
        await task.populate('owner')
        console.log(task.owner)
    }
    

    Here I am calling populate on an already loaded document. This returns a promise if no callback is given. No .exec() needed.

    exec is only needed if you are calling populate on a query such as the other two examples you included. In this case populate returns a query, which could be chained with more filters / populates etc., so it needs the exec call.

    The two populate links I give are to two different parts of the mongoose documentation.