PyMongo/MongoDB展开数组后存在大量空值时如何实现分组?
如何从MongoDB嵌套属性数组提取指定字段生成单行记录?
没问题,我来帮你搞定这个问题!你之前遇到的None值问题,核心是因为$unwind拆分后每个文档只有单个属性有效,$first/$last没法正确聚合所有目标属性的值。这里有两种更简便且靠谱的方案:
方案一:直接将属性数组转为键值对对象(推荐)
这个方案不需要$unwind和$group,一步到位把属性数组转成可直接提取的对象,完美避免多余的None值:
list(coll.aggregate([ { "$addFields": { # 把attributes数组转换成键值对对象 "attrObj": { "$arrayToObject": { "$map": { "input": "$masterVariant.attributes", "as": "attr", "in": {"k": "$$attr.name", "v": "$$attr.value"} } } } } }, { "$project": { "_id": 1, "id": 1, # 提取目标属性,用$ifNull处理属性不存在的情况 "propertyA": {"$ifNull": ["$attrObj.propertyA", "None"]}, "propertyE": {"$ifNull": ["$attrObj.propertyE", "None"]} } } ]))
原理说明:
$map会遍历masterVariant.attributes里的每个元素,把每个属性转换成{"k": "属性名", "v": "属性值"}的格式;$arrayToObject把这个转换后的数组直接变成一个键值对对象,比如{"propertyA": "CJS", "propertyB": 300, ...};- 最后
$project阶段就可以像访问普通对象属性一样提取propertyA和propertyE,用$ifNull保证属性不存在时返回None。
执行后直接得到你想要的单行结果:
| id | propertyA | propertyE |
|---|---|---|
| xxx1234 | 'CJS' | 'dasdags' |
方案二:优化原有$unwind+$group流程
如果你因为其他聚合需求必须保留$unwind,可以修改$group逻辑,先收集所有属性再转成对象:
list(coll.aggregate([ {"$unwind": "$masterVariant.attributes"}, { "$group": { "_id": "$_id", "id": {"$first": "$id"}, # 收集所有属性的键值对到数组 "attrArray": { "$push": {"k": "$masterVariant.attributes.name", "v": "$masterVariant.attributes.value"} } } }, { "$addFields": { # 把数组转成键值对对象 "attrObj": {"$arrayToObject": "$attrArray"} } }, { "$project": { "id": 1, "propertyA": {"$ifNull": ["$attrObj.propertyA", "None"]}, "propertyE": {"$ifNull": ["$attrObj.propertyE", "None"]} } } ]))
为什么原有方案不行?
你之前的$project会给每个unwind后的文档只设置一个有效属性,其他为None,而$first只会取分组后第一个文档的属性值——如果第一个文档是propertyA那条,propertyE就会是None,反之亦然,自然得不到正确的结果。而上面的方法是把所有属性先收集起来再整合,就不会有这个问题。
内容的提问来源于stack exchange,提问作者RyuHayabusa
相关产品推荐
相关产品推荐

