Search code examples
node.jsdatabasemongodbmongoosemongodb-query

Unable to search for userID within nested arrays in Mongoose MongoDB


I am currently facing an issue with my Mongoose MongoDB setup and struggling to search for a userID within a nested array. Here's a breakdown of the situation:

I have two MongoDB models, "Patients" and "Teams". In the "Patients" model, I have a field called "team," which is a mongoose.Types.ObjectId referencing the "Teams" model. Within the "Teams" model, I have a field called "members," which is an array of objects containing the "id," "name," and "role" of team members.

Now, in my API endpoint, I am receiving a userID from the logged-in user, and I want to find all patients that have either the userID in their own "members" array or in the "members" array of the referenced team.

I have tried using various query methods like $elemMatch and $in, but so far, I haven't been successful in achieving the desired results. I have also experimented with aggregation, but unfortunately, it hasn't resolved the issue either.

Here's a simplified version of my API code:

const getAllPatients = async (req, res) => {
  try {
    const { page = 1, limit = 10, userID, tags = null, ...rest } = req.query;
    const filter = {
      ...(tags && { tags: { $in: tags.split(",") } }),
      ...(!isEmptyObject(rest) && { $or: getSearchFilter(rest) }),
    };

    // The following block is where I'm facing the issue
    const countPromise = await Patients.count({
      $or: [
        {
          members: { $all: [userID] }, // This works for searching in patient's own members array
        },
        {
          "team.members": { $all: [userID] }, // This is where I want to search in the referenced team's members array
        },
      ],
    });

    const patientsPromise = await Patients.find({
      $or: [
        {
          members: { $all: [userID] }, // Again, this works for searching in patient's own members array
        },
        // {
        //   "team.members": { $all: [userID] }, // This didn't work as expected
        // },
      ],
      ...filter
    });

    const result = await Promise.all([countPromise, patientsPromise]);
    const count = result[0];
    const patients = result[1];

    return res.status(200).json({
      data: patients,
      totalPages: Math.ceil(count / limit),
      currentPage: parseInt(page),
    });
  } catch (e) {
    console.log(e.message,"error");
    return res.status(500).json({ error: e.message || `Something went wrong` });
  }
};

Patient Model Schema :

const mongoose = require("mongoose");

const patientsSchema = new mongoose.Schema(
  {
    firstName: {
      type: String,
      required: true,
    },
    lastName: {
      type: String,
      required: true,
    },
    gender: {
      type: String,
      // required: true,
    },
    birthday: {
      type: String,
    },
    email: {
      type: String,
      required: true,
      validate: {
        validator: function (v) {
          return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v);
        },
        message: (props) => `Email ${props.value} is not valid`,
      },
    },
    phone: {
      type: String,
      required: true,
    },
    address1: {
      type: String,
    },
    address2: {
      type: String,
    },
    state: {
      type: String,
      // required: true,
    },
    city: {
      type: String,
      // required: true,
    },
    zip: {
      type: String,
      // required: true,
    },
    tags: {
      type: [mongoose.Types.ObjectId],
      ref: "Tags",
    },
    members: {
      type: [String],
      // required: true,
    },
    team: {
      type: mongoose.Types.ObjectId,
      ref: "Teams",
      // required: true,
    },
    chatBotEnabled: { type: Boolean, default: true },
    smsConsent: { type: Boolean, default: false },
    path: {},
  },
  {
    timestamps: true,
  }
);

module.exports = mongoose.model("Patients", patientsSchema);

Team Model Schema :

const mongoose = require("mongoose");

const teamsSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
      unique: true
    },
    members: {
      type: [{
        "id": String,
        "name": String,
        "role": String
    }],
      required: true,
    },
    description:{
      type:String,
      required: true
    }
  },
  {
    timestamps: true,
  }
);

module.exports = mongoose.model("Teams", teamsSchema);

I would really appreciate it if someone could guide me on how to properly search for the userID within the nested "members" array of the "Teams" model so that I can fetch the relevant patients accordingly.

Thank you in advance for your time and assistance!


Solution

  • try this changes in your code this will work

     const getAllPatients = async (req, res) => {
     try {
    const { page = 1, limit = 10, userID, tags = null, ...rest } = req.query;
    const filter = {
      ...(tags && { tags: { $in: tags.split(",") } }),
      ...(!isEmptyObject(rest) && { $or: getSearchFilter(rest) }),
    };
    
    // The following block is where I'm facing the issue
    const countPromise = await Patients.count({
      $or: [
        {
          members: { $all: [userID] }, 
        },
        {
         $match: { "team": userID }, // or use below one
         // $match: { "team": mongoose.Types.ObjectId(userID) },
        },
      ],
    });
    
    const patientsPromise = await Patients.find({
      $or: [
        {
          members: { $all: [userID] }, 
        },
         {
           $match: { "team": userID }, // or use below one
         // $match: { "team": mongoose.Types.ObjectId(userID) },
         },
      ],
      ...filter
    });
    
    const result = await Promise.all([countPromise, patientsPromise]);
    const count = result[0];
    const patients = result[1];
    
    return res.status(200).json({
      data: patients,
      totalPages: Math.ceil(count / limit),
      currentPage: parseInt(page),
    });
    } catch (e) {
    console.log(e.message,"error");
    return res.status(500).json({ error: e.message || `Something went wrong` 
    });
    }
    };