基于Mongoose与Node.js实现MongoDB多集合用户筛选
Let's break down how to build the MongoDB aggregation pipeline to meet your exact requirements. We need to filter users who match both your profile criteria and possess all the specified talent categories, then return their full details along with the media from those matching talents.
Full Aggregation Pipeline
Here's the complete pipeline that ties everything together:
db.user.aggregate([ // Stage 1: Filter users by profile attributes { $match: { gender: { $regex: "^male$", $options: "i" }, // Case-insensitive match for gender complexion: { $regex: "^white$", $options: "i" }, $expr: { $and: [ // Calculate age from dob and check range { $gte: [{ $dateDiff: { startDate: "$dob", endDate: "$$NOW", unit: "year" } }, 18] }, { $lte: [{ $dateDiff: { startDate: "$dob", endDate: "$$NOW", unit: "year" } }, 25] }, // Convert height/weight from string to number and check range { $gte: [{ $toDouble: "$height" }, 5] }, { $lte: [{ $toDouble: "$height" }, 6] }, { $gte: [{ $toDouble: "$weight" }, 50] }, { $lte: [{ $toDouble: "$weight" }, 80] } ] } } }, // Stage 2: Join with talent collection and filter matching categories { $lookup: { from: "talent", localField: "_id", foreignField: "userId", let: { targetCategories: ["5f19359250bcf9158c6be573", "5f19357b50bcf9158c6be572"] }, pipeline: [ { $match: { $expr: { $in: ["$category", "$$targetCategories"] } } }, // Optional: Join with category collection to include category details { $lookup: { from: "category", localField: "category", foreignField: "_id", as: "categoryDetails" } }, { $unwind: "$categoryDetails" } // Flatten category details array ], as: "matchingTalents" } }, // Stage 3: Ensure the user has ALL required talent categories { $match: { $expr: { $eq: [ { $size: { $setUnion: ["$matchingTalents.category", []] } }, // Count unique matching categories { $size: ["5f19359250bcf9158c6be573", "5f19357b50bcf9158c6be572"] } // Count required categories ] } } }, // Stage 4: Format output to match your expected structure { $project: { _id: 0, user: { // Exclude the matchingTalents field from the user object (optional) $mergeObjects: [ "$$ROOT", { matchingTalents: "$$REMOVE" } ] }, medias: { // Combine all media arrays from matching talents into a single list $reduce: { input: "$matchingTalents.media", initialValue: [], in: { $concatArrays: ["$$value", "$$this"] } } } } } ])
Key Explanations
Profile Filtering:
- We use
$dateDiffto calculate the user's age from theirdobfield and validate it against your min/max range. $toDoubleconverts string values forheightandweightto numbers (since your sample data has empty strings, you might want to add a fallback with$condif needed, e.g.,$cond: [{ $eq: ["$height", ""] }, 0, { $toDouble: "$height" }]).- Case-insensitive regex matches for
genderandcomplexionmake the filter more flexible.
- We use
Talent & Category Joining:
- The
$lookupwith a sub-pipeline filters talents to only those in your target categories, and optionally joins with thecategorycollection to include category details if you need them.
- The
Enforce All Required Categories:
$setUnionremoves duplicate category IDs from the matched talents, then we check if the count matches the number of required categories. This ensures the user has all the specified talents, not just one.
Output Formatting:
$mergeObjectscleans up the user object by removing the temporarymatchingTalentsfield.$reduceconcatenates all media arrays from the matching talents into a singlemediasarray, matching your expected output structure.
Edge Cases to Consider
- If some users don't have a
dob,height, orweightfield, add$exists: truechecks to the$matchstage to avoid errors. - If empty strings for height/weight should be excluded, add
$ne: ["$height", ""]to the$exprconditions.
内容的提问来源于stack exchange,提问作者Lijomon C John




