MongoDB 3.6使用$reduce将嵌套字符串数组转为线性数组遇null问题
MongoDB 3.6嵌套数组拉平避免null导致$reduce失效解决方案
问题原因
当前查询结果为null的核心原因是构造的arr_strings数组中包含很多不存在的字段路径返回的null值(比如示例数据中不存在settings.short_form_btn、settings.description等路径,对应取值为null),而$concatArrays只要输入参数包含null就会返回null,最终导致整个$reduce运算结果为null。
解决方案
这里提供两种适配MongoDB 3.6版本的实现方式,均可解决null值导致的运算异常:
方案1:简化流程,多次$unwind过滤null(推荐,逻辑更直观)
不需要使用$reduce,通过多次展开+过滤+分组去重直接得到结果,修改后的聚合语句如下:
db.getCollection('draft_sections').aggregate([ // 原match逻辑保持不变 {"$match": { "$and": [ {"$or": [ {"settings.button.elem": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}, {"settings.short_form_btn.elem": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}, {"settings.list.items": {"$elemMatch": {"title.content": {"$exists": true, "$ne": ""}}}}, {"settings.list.items": {"$elemMatch": {"sub_title.content": {"$exists": true, "$ne": ""}}}}, {"settings.list.items": {"$elemMatch": {"desc.content": {"$exists": true, "$ne": ""}}}}, {"settings.list.items": {"$elemMatch": {"button.elem": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}}}, {"settings.list.items": {"$elemMatch": {"list.items": {"$elemMatch": {"title.content": {"$exists": true, "$ne": ""}}}}}}, {"settings.list.items": {"$elemMatch": {"list.items": {"$elemMatch": {"sub_title.content": {"$exists": true, "$ne": ""}}}}}}, {"settings.list.items": {"$elemMatch": {"list.items": {"$elemMatch": {"desc.content": {"$exists": true, "$ne": ""}}}}}}, {"settings.list.items": {"$elemMatch": {"list.items": {"$elemMatch": {"button.elem": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}}}}}, {"settings.description.settings.button.elem": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}, {"settings.social_list.settings.list.items": {"$elemMatch": {"title": {"$exists": true, "$ne": ""}}}}, ]}, {"language_code": "en"}, ], }}, // 原构造arr_strings的逻辑保持不变 {"$project": { "arr_strings": [ "$settings.button.elem.title", "$settings.list.items.title.content", "$settings.list.items.sub_title.content", "$settings.list.items.desc.content", "$settings.list.items.button.elem.title", "$settings.list.items.list.items.title.content", "$settings.list.items.list.items.sub_title.content", "$settings.list.items.list.items.desc.content", "$settings.list.items.list.items.button.elem.title", "$settings.description.settings.button.elem.title", "$settings.social_list.settings.list.items.title", ], }}, // 第一次展开外层arr_strings数组 {"$unwind": "$arr_strings"}, // 第二次展开每个字段对应的数组,保留空/ null值避免文档丢失 {"$unwind": { "path": "$arr_strings", "preserveNullAndEmptyArrays": true }}, // 过滤掉null值和空字符串 {"$match": { "arr_strings": {"$nin": [null, ""]} }}, // 分组去重得到最终一维数组 {"$group": { "_id": null, "strings": {"$addToSet": "$arr_strings"} }}, // 去掉_id字段 {"$project": { "_id": 0 }} ])
方案2:修改$reduce逻辑跳过null值
如果想保留原来的$reduce写法,可以在运算时判断元素是否为null,null值直接跳过不拼接,仅需修改最后$project阶段的逻辑即可:
{"$project": { "_id": 0, "strings": {"$setUnion": [ {"$reduce": { "input": "$all_arr_strings", "initialValue": [], "in": { "$cond": [ {"$eq": ["$$this", null]}, "$$value", {"$concatArrays": ["$$value", "$$this"]} ] } }}, ]}, }}
注:该方案如果存在深层嵌套数组还需要额外做展开处理,因此更推荐使用方案1。
内容的提问来源于stack exchange,提问作者Amandeep kaur
相关产品推荐
相关产品推荐

