如何在MongoDB聚合中匹配crew任意数组内指定_id的文档
解决方案
要在聚合管道中筛选出crew对象下任意数组包含指定_id的文档,核心思路是将未知结构的crew对象转换为可遍历的键值对数组,再检查其中的数组字段是否包含目标ID。
实现逻辑
- 使用
$objectToArray将crew对象转换为[{k: 字段名, v: 数组}]格式的数组,实现对未知键名的遍历。 - 通过
$map遍历每个键值对,提取数组中所有元素的_id,并用$in判断目标ID是否存在于该列表中。 - 用
$anyElementTrue判断是否存在至少一个数组字段匹配目标ID,只要有一个匹配则保留文档。
聚合管道代码示例
假设目标ID为"1234567890abcdef12345678",将筛选阶段放在排序、分组之前:
const mongoose = require('mongoose'); const ScheduleTask = mongoose.model('schedule_task'); // 目标ID,注意转换为ObjectId类型 const targetUserId = mongoose.Types.ObjectId("1234567890abcdef12345678"); const pipeline = [ // 核心筛选阶段 { $match: { $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$crew" }, as: "entry", in: { $in: [ targetUserId, { $map: { input: "$$entry.v", as: "item", in: "$$item._id" } } ] } } } } } }, // 后续的排序、分组操作 { $sort: { createdAt: -1 } }, { $group: { _id: "$taskType", total: { $sum: 1 } } } ]; // 执行聚合查询 const filteredResults = await ScheduleTask.aggregate(pipeline);
增强容错性
如果crew字段可能不存在或部分键的值不是数组,可以添加类型校验,避免报错:
{ $match: { $expr: { $and: [ // 确保crew是对象类型 { $eq: [{ $type: "$crew" }, "object"] }, { $anyElementTrue: { $map: { input: { $objectToArray: "$crew" }, as: "entry", in: { $and: [ // 确保当前键的值是数组 { $eq: [{ $type: "$$entry.v" }, "array"] }, { $in: [targetUserId, { $map: { input: "$$entry.v", as: "item", in: "$$item._id" } }] } ] } } } } ] } } }
这个方案无需预先知晓crew下的具体数组键名,完全适配动态结构的字段。
内容的提问来源于stack exchange,提问作者Vaggelis
相关产品推荐
相关产品推荐

