MongoDb 聚合数据操作 - 对象到数组

MongoDb Aggregation Data manipulation - Objects to Arrays

我有以下示例数据集

[{
  "_id": {
    "$oid": "60f83d3cd66842301905aa77"
  },
  "id": 527438,
  "name": "CryptoPunk #4050",
  "asset_contract": {
    "name": "CryptoPunks",
    "address": "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb"
  },
  "traits": [
    {
      "trait_type": "type",
      "value": "Male",
      "display_type": null,
      "max_value": null,
      "trait_count": 6039,
      "order": null
    },
    {
      "trait_type": "accessory",
      "value": "Mohawk",
      "display_type": null,
      "max_value": null,
      "trait_count": 441,
      "order": null
    },
    {
      "trait_type": "accessory",
      "value": "Earring",
      "display_type": null,
      "max_value": null,
      "trait_count": 2459,
      "order": null
    },
    {
      "trait_type": "accessory",
      "value": "Frown",
      "display_type": null,
      "max_value": null,
      "trait_count": 261,
      "order": null
    }
  ],
  "token_id": "4050",
  "permalink": "https://opensea.io/assets/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb/4050",
  "background_color": null,
  "image_url": "https://lh3.googleusercontent.com/sO18rDQYhC5yIcj12RVsv31pbbsZo_2muQQbTJMQHn47EKGhnirs8mxzohm58HAZ7taBoe4pU6x1qntlExk_TtJ-",
  "image_preview_url": "https://lh3.googleusercontent.com/sO18rDQYhC5yIcj12RVsv31pbbsZo_2muQQbTJMQHn47EKGhnirs8mxzohm58HAZ7taBoe4pU6x1qntlExk_TtJ-=s250",
  "animation_url": null,
  "vault_contract": "0x269616d549d7e8eaa82dfb17028d0b212d11232a"
},{
  "_id": { "$oid": "60f83d3cbc3f0161da2141f7" },
  "id": 17736625,
  "name": "OJ Simpson",
  "asset_contract": {
    "name": "Hashmasks",
    "address": "0xc2c747e0f7004f9e8817db2ca4997657a7746928"
  },
  "traits": [
    {
      "trait_type": "Character",
      "value": "Male",
      "display_type": null,
      "max_value": null,
      "trait_count": 8659,
      "order": null
    },
    {
      "trait_type": "Mask",
      "value": "Doodle",
      "display_type": null,
      "max_value": null,
      "trait_count": 2187,
      "order": null
    },
    {
      "trait_type": "Eye Color",
      "value": "Dark",
      "display_type": null,
      "max_value": null,
      "trait_count": 7419,
      "order": null
    },
    {
      "trait_type": "Item",
      "value": "No Item",
      "display_type": null,
      "max_value": null,
      "trait_count": 14533,
      "order": null
    },
    {
      "trait_type": "Skin Color",
      "value": "Dark",
      "display_type": null,
      "max_value": null,
      "trait_count": 3784,
      "order": null
    },
    {
      "trait_type": "Token ID",
      "value": 3535,
      "display_type": "number",
      "max_value": null,
      "trait_count": 0,
      "order": null
    },
    {
      "trait_type": "Background",
      "value": "Doodle",
      "display_type": null,
      "max_value": null,
      "trait_count": 5538,
      "order": null
    }
  ],
  "token_id": "3535",
  "permalink": "https://opensea.io/assets/0xc2c747e0f7004f9e8817db2ca4997657a7746928/3535",
  "background_color": null,
  "image_url": "https://lh3.googleusercontent.com/NZQu7CNjgJ_1uhbUVwEb-14rZPJmPCaqaXy0qnUpgm5Qll0BvmmF7tPMjBhFH6ZZp_qzOPxHi0NFmRkOjHoBQ0BODcWI8NlyBXLu",
  "image_preview_url": "https://lh3.googleusercontent.com/NZQu7CNjgJ_1uhbUVwEb-14rZPJmPCaqaXy0qnUpgm5Qll0BvmmF7tPMjBhFH6ZZp_qzOPxHi0NFmRkOjHoBQ0BODcWI8NlyBXLu=s250",
  "animation_url": null,
  "vault_contract": "0xc7a8b45e184138114e6085c82936a8db93dd156a"
}]

我想更新到

[{
    "_id": {
      "$oid": "60f83d3cd66842301905aa77"
    },
    "id": 527438,
    "name": "CryptoPunk #4050",
    "asset_contract": {
      "name": "CryptoPunks",
      "address": "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb"
    },
    "traits":
      {
        "type": "Male",
        "accessory": ["Mohawk", "Earing", "Frown"], 
      },
    "token_id": "4050",
    "permalink": "https://opensea.io/assets/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb/4050",
    "background_color": null,
    "image_url": "https://lh3.googleusercontent.com/sO18rDQYhC5yIcj12RVsv31pbbsZo_2muQQbTJMQHn47EKGhnirs8mxzohm58HAZ7taBoe4pU6x1qntlExk_TtJ-",
    "image_preview_url": "https://lh3.googleusercontent.com/sO18rDQYhC5yIcj12RVsv31pbbsZo_2muQQbTJMQHn47EKGhnirs8mxzohm58HAZ7taBoe4pU6x1qntlExk_TtJ-=s250",
    "animation_url": null,
    "vault_contract": "0x269616d549d7e8eaa82dfb17028d0b212d11232a"
  },{
  "_id": { "$oid": "60f83d3cbc3f0161da2141f7" },
  "id": 17736625,
  "name": "OJ Simpson",
  "asset_contract": {
    "name": "Hashmasks",
    "address": "0xc2c747e0f7004f9e8817db2ca4997657a7746928"
  },
  "traits": {
      "character": "Male",
      "mask": "Doodle",
      "eye_color": "Dark",
      "item": "No Item",
      "skin_color": "Dark",
      "token_id": 3535,
      "background": "Doodle",
    },
  "token_id": "3535",
  "permalink": "https://opensea.io/assets/0xc2c747e0f7004f9e8817db2ca4997657a7746928/3535",
  "background_color": null,
  "image_url": "https://lh3.googleusercontent.com/NZQu7CNjgJ_1uhbUVwEb-14rZPJmPCaqaXy0qnUpgm5Qll0BvmmF7tPMjBhFH6ZZp_qzOPxHi0NFmRkOjHoBQ0BODcWI8NlyBXLu",
  "image_preview_url": "https://lh3.googleusercontent.com/NZQu7CNjgJ_1uhbUVwEb-14rZPJmPCaqaXy0qnUpgm5Qll0BvmmF7tPMjBhFH6ZZp_qzOPxHi0NFmRkOjHoBQ0BODcWI8NlyBXLu=s250",
  "animation_url": null,
  "vault_contract": "0xc7a8b45e184138114e6085c82936a8db93dd156a"
}]

其背后的逻辑是

所以,

      "trait_type": "type",
      "value": "Male",

//becomes

            "type": "Male"

所以,

{
      "trait_type": "accessory",
      "value": "Mohawk",
      "display_type": null,
      "max_value": null,
      "trait_count": 441,
      "order": null
    },
    {
      "trait_type": "accessory",
      "value": "Earring",
      "display_type": null,
      "max_value": null,
      "trait_count": 2459,
      "order": null
    },

// becomes

            "accessory": ["Mohawk", "Earring"]

查询

  • 它是一个聚合更新,即使管道不允许我们使用此处使用的组查找等阶段。 (你可以使用 $out 并在之后替换集合或 $merge 来替换文档(类似于更新))

  • 第一张地图

    • 对于每个特征(特征的文档成员),它使其进入数组
      [["trait_type": "type"] ["value": "Male"] ["display_type": null] ...]
    • 减少该数组以仅从它们构造 1 个文档
      {"type" "type","value" :"Male"}(小写字母和“_”也是如此)
  • 现在的特质很像

    "traits": [
      {
        "type": "type",
        "value": "Male"
      },
      {
        "type": "accessory",
        "value": "Mohawk"
      },
      {
        "type": "accessory",
        "value": "Earring"
      },
      {
        "type": "accessory",
        "value": "Frown"
      }
    ]
    
  • 查找虚拟集合[{}](我们这样做是为了在该数组中创建一个组)它就像一个技巧,允许我们在 1 个文档中使用阶段运算符

    • 查找管道展开并按类型分组
    "traits": [
      {
        "values": [
          "Mohawk",
          "Earring",
          "Frown"
        ],
        "type": "accessory"
      },
      {
        "values": [
          "Male"
        ],
        "type": "type"
      }
    ]
    
    • 然后它是一个替换根来做取类型的值,使它成为字段名和值作为值(if size=1 removes the array)
  • 查找后我们有

    "traits": [
      {
        "accessory": [
          "Mohawk",
          "Earring",
          "Frown"
        ]
      },
      {
        "type": "Male"
      }
    ]
    
  • 所以我们要做的就是减少特征并合并对象 (无论如何,键都是唯一的,因为我们按它们分组)

  • 我们得到了预期的输出(至少我认为它没问题)

Test code here

db.collection.aggregate([
  {
    "$set": {
      "traits": {
        "$map": {
          "input": "$traits",
          "as": "t",
          "in": {
            "$reduce": {
              "input": {
                "$map": {
                  "input": {
                    "$objectToArray": "$$t"
                  },
                  "as": "m",
                  "in": [
                    "$$m.k",
                    "$$m.v"
                  ]
                }
              },
              "initialValue": {},
              "in": {
                "$let": {
                  "vars": {
                    "type_value": "$$value",
                    "ta": "$$this"
                  },
                  "in": {
                    "$let": {
                      "vars": {
                        "key": {
                          "$arrayElemAt": [
                            "$$ta",
                            0
                          ]
                        },
                        "value": {
                          "$arrayElemAt": [
                            "$$ta",
                            1
                          ]
                        }
                      },
                      "in": {
                        "$switch": {
                          "branches": [
                            {
                              "case": {
                                "$eq": [
                                  "$$key",
                                  "value"
                                ]
                              },
                              "then": {
                                "$mergeObjects": [
                                  "$$type_value",
                                  {
                                    "value": "$$value"
                                  }
                                ]
                              }
                            },
                            {
                              "case": {
                                "$eq": [
                                  "$$key",
                                  "trait_type"
                                ]
                              },
                              "then": {
                                "$mergeObjects": [
                                  "$$type_value",
                                  {
                                    "type": {
                                      "$replaceAll": {
                                        "input": {
                                          "$toLower": "$$value"
                                        },
                                        "find": " ",
                                        "replacement": "_"
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          ],
                          "default": "$$type_value"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  {
    "$lookup": {
      "from": "dummy",
      "let": {
        "traits": "$traits"
      },
      "pipeline": [
        {
          "$set": {
            "traits": "$$traits"
          }
        },
        {
          "$unwind": {
            "path": "$traits"
          }
        },
        {
          "$replaceRoot": {
            "newRoot": "$traits"
          }
        },
        {
          "$group": {
            "_id": "$type",
            "values": {
              "$push": "$value"
            }
          }
        },
        {
          "$set": {
            "type": "$_id"
          }
        },
        {
          "$project": {
            "_id": 0
          }
        },
        {
          "$replaceRoot": {
            "newRoot": {
              "$cond": [
                {
                  "$eq": [
                    {
                      "$size": "$values"
                    },
                    1
                  ]
                },
                {
                  "$arrayToObject": {
                    "$let": {
                      "vars": {
                        "pair": [
                          [
                            "$type",
                            {
                              "$arrayElemAt": [
                                "$values",
                                0
                              ]
                            }
                          ]
                        ]
                      },
                      "in": "$$pair"
                    }
                  }
                },
                {
                  "$arrayToObject": {
                    "$let": {
                      "vars": {
                        "pair": [
                          [
                            "$type",
                            "$values"
                          ]
                        ]
                      },
                      "in": "$$pair"
                    }
                  }
                }
              ]
            }
          }
        }
      ],
      "as": "traits"
    }
  },
  {
    "$set": {
      "traits": {
        "$mergeObjects": "$traits"
      }
    }
  }
])