MongoDB聚合$project阶段动态获取对象键值实现地铁城市判断的问题及修复
问题分析与修复方案
你的问题核心在于MongoDB聚合管道的表达式无法直接通过JavaScript对象的动态键访问文档字段。你写的cityPassConfig["$cityId"].metro是在JS上下文执行的,这里的"$cityId"会被当作字符串字面量,而不是聚合管道中当前文档的cityId字段值,所以自然无法匹配到正确的城市配置。
下面提供两种简洁可行的修复方案:
方案一:利用$getField直接映射(推荐)
这种方法先把城市配置转换成键为cityId、值为1/0的映射对象,再用MongoDB的$getField操作符根据文档的cityId动态获取对应值:
// 1. 预处理cityConfiguration,生成metro映射表 const cityMetroMap = {}; Object.entries(cityConfiguration).forEach(([key, config]) => { // 过滤掉_id和configType字段,只保留城市配置 if (key !== '_id' && key !== 'configType') { cityMetroMap[key] = config.metro ? 1 : 0; } }); // 2. 修复后的聚合查询 return await BusDetails.aggregate([ { $match: { busNumber: 'KA25D5057' } }, { $project: { totalCollection: 1, passengerCount: 1, // 用$getField根据当前文档的cityId获取对应metro值 metro: { $getField: { field: '$cityId', input: cityMetroMap } } } } ]);
方案二:通过数组过滤匹配(适合复杂配置场景)
如果后续需要用到城市配置的更多字段,可以先把配置转换成数组,再用$filter和$arrayElemAt匹配对应城市:
// 1. 把cityConfiguration转换成聚合可用的数组格式 const cityConfigArray = Object.entries(cityConfiguration) .filter(([key]) => key !== '_id' && key !== 'configType') .map(([cityId, config]) => ({ cityId, metro: config.metro })); // 2. 修复后的聚合查询 return await BusDetails.aggregate([ { $match: { busNumber: 'KA25D5057' } }, { $project: { totalCollection: 1, passengerCount: 1, metro: { $cond: [ { $let: { vars: { // 过滤出当前文档cityId对应的城市配置 matchedCity: { $arrayElemAt: [ { $filter: { input: cityConfigArray, cond: { $eq: ['$$this.cityId', '$cityId'] } } }, 0 ] } }, // 判断匹配到的城市是否为metro城市 in: { $eq: ['$$matchedCity.metro', true] } } }, 1, 0 ] } } } ]);
测试验证
对于你提供的示例文档(cityId: "17"),两种方案都会返回metro: 0,符合预期;如果cityId是"16",则会返回metro: 1。
内容的提问来源于stack exchange,提问作者Roomi
相关产品推荐
相关产品推荐

