MongoDB聚合操作:将NFT元数据traits数组转换为结构化对象
MongoDB聚合管道实现NFT元数据traits字段转换
你可以根据你的MongoDB版本和环境限制选择以下两种实现方案:
方案1:原生操作符实现(兼容性好,无需开启JS执行权限)
适用于MongoDB 4.4及以上版本,全程使用聚合原生操作符,性能更优:
const aggregationPipeline = [ // 拆分traits数组为单个属性文档 { $unwind: "$traits" }, // 生成格式化后的属性键和对应值 { $addFields: { traitKey: { $replaceAll: { input: { $toLower: "$traits.trait_type" }, find: " ", replacement: "_" } }, traitValue: "$traits.value" } }, // 按文档ID和属性键分组,收集同属性的所有值,同时保留其他原始字段 { $group: { _id: { docId: "$_id", key: "$traitKey" }, id: { $first: "$id" }, name: { $first: "$name" }, asset_contract: { $first: "$asset_contract" }, token_id: { $first: "$token_id" }, permalink: { $first: "$permalink" }, background_color: { $first: "$background_color" }, image_url: { $first: "$image_url" }, image_preview_url: { $first: "$image_preview_url" }, animation_url: { $first: "$animation_url" }, vault_contract: { $first: "$vault_contract" }, valueList: { $push: "$traitValue" } } }, // 处理值格式:单值直接返回,多值保留数组 { $addFields: { kvPair: { k: "$_id.key", v: { $cond: { if: { $eq: [{ $size: "$valueList" }, 1] }, then: { $first: "$valueList" }, else: "$valueList" } } } } }, // 按文档ID重新聚合,收集所有属性键值对 { $group: { _id: "$_id.docId", id: { $first: "$id" }, name: { $first: "$name" }, asset_contract: { $first: "$asset_contract" }, token_id: { $first: "$token_id" }, permalink: { $first: "$permalink" }, background_color: { $first: "$background_color" }, image_url: { $first: "$image_url" }, image_preview_url: { $first: "$image_preview_url" }, animation_url: { $first: "$animation_url" }, vault_contract: { $first: "$vault_contract" }, traits: { $push: "$kvPair" } } }, // 将键值对数组转换为目标对象结构 { $addFields: { traits: { $arrayToObject: "$traits" } } } ]
方案2:JS自定义函数实现(代码简洁,仅单个阶段)
适用于允许运行JS脚本的MongoDB环境,代码更短易维护:
const aggregationPipeline = [ { $addFields: { traits: { $function: { body: function(traits) { const result = {} traits.forEach(trait => { // 格式化键名:转小写+空格替换为下划线 const key = trait.trait_type.toLowerCase().replaceAll(' ', '_') // 处理值:同键存在则转为数组存储 if (Object.prototype.hasOwnProperty.call(result, key)) { result[key] = Array.isArray(result[key]) ? [...result[key], trait.value] : [result[key], trait.value] } else { result[key] = trait.value } }) return result }, args: ["$traits"], lang: "js" } } } } ]
两种方案都完全符合你要求的转换规则,输出结果和你给出的期望结构一致。
内容的提问来源于stack exchange,提问作者justinavery
相关产品推荐
相关产品推荐

