Mongoose: find all docs where _Id of nested sub-doc in array matches userId

Viewed 402

an abbreviated schema:

const ThingSchema = new mongoose.Schema({
  _id: {
    type: String,
  },
  widgets: [{
    user: {
      type: Schema.Types.ObjectId,
      ref: 'user',
    },
    lastViewedAt: {
      type: Date,
    },
  }],
}, { _id: false });

Given a userId, how would you get back all Things that a user belongs to where user is a sub-doc in an array of deeply nested objects?

I have gone between several approaches found on answers to similar "find in array of object" questions suggesting .where, $in, and .populate match not being able to get more than the full, unfiltered, collection of Things or nada. I welcome feedback as to why the particular methods I tried above would be a poor/good choice for the end goal of the filtered result I am after.


Update:

A partial solution I have makes use of aggregate. First I had to $unwind the widgets array. In the resulting collection generated by $unwind I then $matched on the user's _id.

There was a gotcha in that mongoose does not autocast _ids into mongo ObjectIds in aggregate calls unlike with other queries. I had to explicitly convert my _id string with mongoose.Types.ObjectId().

const things = await Thing.aggregate([
  { $unwind: '$widgets' },
  { $match: { 'widgets._id': mongoose.Types.ObjectId(userId) } }
]);

The only issue here is that, in the process of unwinding the widgets array, I have lost that full collection of users and only get the single user who's ID I am searching on in that collection now. I now would need to do something like grab the Things IDs and run an additional query and .populate() the users again.

I had similar results with this particularly verbose go at $filter

const things = await Thing.aggregate([
  { "$match": {
    "widgets._id": { "$in": [ mongoose.Types.ObjectId(userId) ] }
  }},
  { "$project": {
    "widgets": {
      "$filter": {
        "input": "$widgets",
        "as": "user",
        "cond": { "$or": [{ "$eq": [ "$$user._id", mongoose.Types.ObjectId(userId) ] }] },
      }
    }
  }}
]);
0 Answers
Related