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) ] }] },
}
}
}}
]);