1

I have a MongoDb object with contains an array of objectIds

const User = mongoose.model(
    "User",
    new mongoose.Schema({
        name: String,
        email: String,
        evals:[],
        password: String,
        position: String,
        roles: [
            {
                type: mongoose.Schema.Types.ObjectId,
                ref: "Role"
            }
        ],
        username: {
            String
        }
    })
);

module.exports = User;

The Role is currently just a name and description:

const Role = mongoose.model(
    "Role",
    new mongoose.Schema({
        name: String,
        description: String
    })
);

module.exports = Role;

I want to return the name of the role rather than the object ID by calling a function like this:

exports.getRoles = (req, res) => {
  User.findOne({
    $or: [
      { _userIds: req.params.userId }]
  }).then((user) => {
    const roleList = [];
    for (let role of user.roles) {
      Role.findOne({ "_id": role.toString() }).then((role) => {
      console.log(role.name);
      roleList.push(role.name)
      }
      )};
    res.json(roleList);
  })
}

As you can already see, this returns an empty array because it's not getting the results of the nested promises. I've gone around and around, and even written helper functions that achieve (unfortunately) the exact same results.

So, how am I supposed to get the results of the final array from the promises? I can see from the console.logs that the array is being filled, but the res.json(roleList) is occuring first.

Also, am I making this way too hard, and is there a simple way that MongoDb does this automagically? I could probably do this easily in SQL with a query, but that's not an option here.

Rick Dawson
  • 59
  • 1
  • 7
  • is the description of the role necessary? you could omit that information and store it in a seperate file as i am guessing it will be around 5 roles/descriptions max? – Fiehra Dec 17 '22 at 13:08

1 Answers1

3

Your for loop does not wait for the Role.findOne promise to be resolved, therefore roleList is still empty when you output it with res.json(roleList).

The solution is to make roleList a list of Role.findOne promises and use Promise.all to wait for all of them to resolve:

User.findOne({
  $or: [{ _userIds: req.params.userId }]
}).then((user) => {
  const roleListPromises = [];
  for (let role of user.roles) {
    roleListPromises.push(Role.findOne({ "_id": role.toString() }).then((role) => {
      console.log(role.name);
      return role.name;
    }));
  }
  return Promise.all(roleListPromises);
}).then((roleList) => {
  res.json(roleList);
});
Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31