MongoDB聚合:类数组字符串转换及size按周统计
MongoDB按周统计类数组字符串中Size的数量
问题场景
现有MongoDB文档数据,其中product字段存储为JSON格式的类数组字符串,而非真实嵌套数组,数据示例如下:
[ { "uuid": "yxcvb", "product": "[{\"pid\":\"4f76c06825aa486db9a7a5cedbc2ea19\",\"size\":\"S\"},{\"pid\":\"d3055e0a1bb040dba65674b9733bf4ce\",\"size\":\"M\"}]", "timestamp":{"$date":"2023-01-01T11:25:47+00:00"} }, { "uuid": "asdfg", "product": "[{\"pid\":\"369b33cc768d496193b18d657d706920\",\"size\":\"M\"}]", "timestamp":{"$date":"2023-01-02T11:25:47+00:00"} }, { "uuid": "qwert", "product": "[{\"pid\":\"13325cb71fd7413c8f3e18caf20b5d6d\",\"size\":\"S\"},{\"pid\":\"ac431397d49d449cab44d98dc13ec57c\",\"size\":\"M\"},{\"pid\":\"ba35b9f804a44e15bf197d3ef671dc34\",\"size\":\"L\"}]", "timestamp":{"$date":"2023-01-03T11:25:47+00:00"} } ]
需求是按timestamp的周分组,统计各size的数量,期望输出格式如下:
[ { "week": 52, "sizeS": 1 }, { "week": 52, "sizeM": 1 }, { "week": 1, "sizeS": 1 }, { "week": 1, "sizeM": 2 }, { "week": 1, "sizeL": 1 } ]
解决方案
方案一:使用$jsonParse转换类数组字符串(MongoDB 4.4+)
利用MongoDB 4.4新增的$jsonParse操作符,直接将product字段的JSON字符串解析为真实数组,后续通过标准聚合步骤完成统计:
db.collection.aggregate([ // 1. 将product的JSON字符串解析为数组 { $addFields: { product: { $jsonParse: "$product" } } }, // 2. 展开product数组中的每个元素 { $unwind: "$product" }, // 3. 提取timestamp对应的ISO周数(2023-01-01属于2022年第52周) { $addFields: { week: { $isoWeek: "$timestamp" } } }, // 4. 按周和size分组,统计数量 { $group: { _id: { week: "$week", size: "$product.size" }, count: { $sum: 1 } } }, // 5. 格式化输出为期望的键值对结构 { $replaceRoot: { newRoot: { $mergeObjects: [ { week: "$_id.week" }, { $arrayToObject: [[{ k: { $concat: ["size", "$_id.size"] }, v: "$count" }]] } ] } } }, // 可选:按周排序 { $sort: { week: 1 } } ])
方案二:正则提取size值(兼容低版本MongoDB)
若无法使用$jsonParse,可通过正则表达式提取所有size的值,再完成统计:
db.collection.aggregate([ // 1. 用正则提取所有size的值,生成数组 { $addFields: { sizes: { $map: { input: { $regexFindAll: { input: "$product", regex: '"size":"(S|M|L)"' } }, as: "match", in: { $arrayElemAt: ["$$match.captures", 0] } } } } }, // 2. 展开sizes数组 { $unwind: "$sizes" }, // 3. 提取ISO周数 { $addFields: { week: { $isoWeek: "$timestamp" } } }, // 4. 按周和size分组统计 { $group: { _id: { week: "$week", size: "$sizes" }, count: { $sum: 1 } } }, // 5. 格式化输出结构 { $replaceRoot: { newRoot: { $mergeObjects: [ { week: "$_id.week" }, { $arrayToObject: [[{ k: { $concat: ["size", "$_id.size"] }, v: "$count" }]] } ] } } }, { $sort: { week: 1 } } ])
说明
$isoWeek返回日期对应的ISO周数,符合示例中2023-01-01为第52周的结果;- 方案二中的正则可根据实际size取值范围调整(如包含XL等);
- 最终输出通过
$arrayToObject和$mergeObjects将统计结果转换为期望的键名格式。
内容的提问来源于stack exchange,提问作者TMPS
相关产品推荐
相关产品推荐

