skip to Main Content

Let’s say I have the following collection with _id and traits.

[
    {
      _id: 1,
      traits: {
        Rarity: {
          infoin: 15,
        },
        Type: {
          iron: 3,
          sliver: 5,
          wood: 7,
        },
      },
    },
    {
      _id: 2,
      traits: {
        Cloth: {
          barron1: 11,
          barron2: 12,
        },
        Hair: {
          black: 6,
          yellow: 9,
          red: 8
        }
      },
    },
    ...
]

As you can see keys of traits are dynamic and the keys of sub-objects as well.

Here is the result I wanna get:

[
    {
      _id: 1,
      traits: 15,
    },
    {
      _id: 2,
      traits: 23
    }
]

Tip:

infocoin = iron + sliver + wood

barron1 + barron2 = black + yellow + red

2

Answers


    1. $set – Set traitObjs array field by converting the object to array via $objectToArray.

    2. $set – Set firstTraitValues field by getting the value of first document from traitObjs array, then converting from object to array via $objectToArray.

    3. $project – Decorate the output document. Set traits field by converting firstTraitValues array to number type with $reduce and $sum all the v values.

    db.collection.aggregate([
      {
        $set: {
          traitObjs: {
            $objectToArray: "$traits"
          }
        }
      },
      {
        $set: {
          firstTraitValues: {
            $objectToArray: {
              $first: "$traitObjs.v"
            }
          }
        }
      },
      {
        $project: {
          traits: {
            $reduce: {
              input: "$firstTraitValues",
              initialValue: 0,
              in: {
                $sum: [
                  "$$value",
                  "$$this.v"
                ]
              }
            }
          }
        }
      }
    ])
    

    Sample Mongo Playground


    Since all the values in the first key document and the second key document of traits are the same,

    infocoin = iron + sliver + wood

    barron1 + barron2 = black + yellow + red

    Hence the above approach just sums up all the values in the first key document of traits.

    Login or Signup to reply.
  1. This answer is really the same as @yong-shun’s answer but it composes everthing into one "$project". I don’t know if it would be more efficient or not.

    db.collection.aggregate([
      {
        "$project": {
          "traits": {
            "$reduce": {
              "input": {
                "$objectToArray": {
                  "$getField": {
                    "field": "v",
                    "input": { "$first": { "$objectToArray": "$traits" } }
                  }
                }
              },
              "initialValue": 0,
              "in": { "$sum": [ "$$value", "$$this.v" ] }
            }
          }
        }
      }
    ])
    

    Try it on mongoplayground.net.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search