Node.js Mongoose嵌套数组按对象属性筛选数据求助
Hey there! I get that as a Node.js/Mongoose newbie, filtering nested arrays can be tricky. Let's figure out how to fix your query so you only get the valid categories and their valid sub-services.
The issue with your current find() query is that MongoDB only uses those valid: true conditions to select which Vendor documents to return—it doesn't filter out individual invalid elements inside the categories or sub_services arrays. To achieve your desired result, we need to use MongoDB's aggregation framework, which lets us clean up nested arrays directly in the database.
Solution 1: Aggregation Framework (Recommended for Performance)
This approach does all the filtering on the database side, which is much more efficient than fetching extra data and filtering it in your code. Here's the revised query:
router.get('/get_service_details', function(req, res) { Vendor.aggregate([ // Step 1: Match the specific Vendor document you need { $match: { _id: req.user._id } }, // Step 2: Filter out invalid categories { $project: { services: { meta_data: "$services.meta_data", // Keep your meta_data intact categories: { $filter: { input: "$services.categories", as: "category", cond: { $eq: ["$$category.valid", true] } } } } } }, // Step 3: Filter invalid sub-services from remaining categories { $addFields: { "services.categories": { $map: { input: "$services.categories", as: "category", in: { service_category: "$$category.service_category", valid: "$$category.valid", sub_services: { $filter: { input: "$$category.sub_services", as: "sub", cond: { $eq: ["$$sub.valid", true] } } } } } } } } ]) .exec(function(err, result) { if (err) { console.log(err); return res.status(500).send(err); } // Aggregation returns an array—send the first (and only) matching document res.send(result[0]); }); });
Breakdown of How This Works:
$match: Narrows down to exactly the Vendor document linked to the current user, just like your original query.- First
$project: Uses$filterto remove any categories wherevalidisfalse. $addFields+$map: Iterates over each remaining valid category, then uses another$filterto strip out sub-services withvalid: false. We reconstruct each category object to keep only the fields we need with the filtered sub-services.
Solution 2: Post-Query JavaScript Filtering (Simpler for Small Datasets)
If you're working with small datasets and prefer a more straightforward approach, you can fetch the full document first and filter the arrays in your code:
router.get('/get_service_details', function(req, res) { Vendor.findOne({ _id: req.user._id }, 'services') .exec(function(err, vendor) { if (err) { console.log(err); return res.status(500).send(err); } // Filter out invalid categories vendor.services.categories = vendor.services.categories.filter(category => category.valid); // Filter invalid sub-services for each valid category vendor.services.categories.forEach(category => { category.sub_services = category.sub_services.filter(sub => sub.valid); }); res.send(vendor); }); });
Note: This method pulls the full document from the database first, so it's not ideal for large arrays. Stick with the aggregation approach if you expect your data to grow.
Either solution will give you your desired output: only valid categories with their valid sub-services.
内容的提问来源于stack exchange,提问作者Achira Shamal

