Home > Back-end >  $arrayToObject with duplicated key
$arrayToObject with duplicated key

Time:07-29

considering the following document

    "logs": {
      "events": {
        "type": "call_action",
        "attributes": [
          {
            "key": "module",
            "value": "module1"
          },
          {
            "key": "data",
            "value": "data_value_module_1"
          },
          {
            "key": "module",
            "value": "module2"
          },
          {
            "key": "data",
            "value": "data_value_module_2"
          }
        ]
      }
    },

I am trying to use $arraytoObject to convert key and value

$project: {
  "attributes": {
    $arrayToObject: {
      $map: {
        "input": "$logs.events.attributes",
        "as": "el",
        "in": {
          "k": "$$el.key",
          "v": "$$el"
        }
      }
    }
  }
}

but it overwrite duplicated keys as module and data.

How can I use a kind of $unwind or something like that?

The result should be like that

[
  {
    "logs": {
      "events": {
        "type": "call_action",
        "attributes": {
          "module": "module1"
          "value": "data_value_module_1"
        }
      }
    }
  },
  {
    "logs": {
      "events": {
        "type": "call_action",
        "attributes": {
          "module": "module2"
          "value": "data_value_module_2"
        }
      }
    }
  }
]

Thank you

CodePudding user response:

Based on this answer, one option is:

db.collection.aggregate([
  {$set: {
      "logs.events.attributes": {
        $reduce: {
          input: "$logs.events.attributes",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              [
                {
                  k: "$$this.key",
                  v: "$$this.value",
                  mod: {$mod: [{$size: "$$value"}, 2]}
                }
              ]
            ]
          }
        }
      }
    }
  },
  {$project: {
      _id: 0,
      type: "$logs.events.type",
      firstEvent: {
        $filter: {
          input: "$logs.events.attributes",
          cond: {$eq: ["$$this.mod", 0]}
        }
      },
      secondEvent: {
        $filter: {
          input: "$logs.events.attributes",
          cond: {$eq: ["$$this.mod", 1]}
        }
      }
    }
  },
  {$project: {
      "logs.events": {
        type: "$type",
        attributes: {$zip: {inputs: ["$firstEvent", "$secondEvent"]}}
      }
    }
  },
  {$unwind: "$logs.events.attributes"},
  {$set: {
      "logs.events.attributes": {
        $map: {
          input: "$logs.events.attributes",
          in: {k: "$$this.k", v: "$$this.v"}}
      }
    }
  },
  {$set: {"logs.events.attributes": {$arrayToObject: $logs.events.attributes"}}}
])

See how it works on the playground example

Or with a small variation (I generally prefer not to $unwind and $group again, but in this case it seems reasonable):

db.collection.aggregate([
  {$set: {
      "logs.events.attributes": {
        $reduce: {input: "$logs.events.attributes",
          initialValue: [],
          in: {$concatArrays: [
              "$$value",
              [
                {
                  k: "$$this.key",
                  v: "$$this.value",
                  group: {$floor: [{$divide: [{$size: "$$value"}, 2]}]}
                }
              ]
            ]
          }
        }
      }
    }
  },
  {$unwind: "$logs.events.attributes"},
  {$group: {
      _id: "$logs.events.attributes.group",
      data: {$push: {k: "$logs.events.attributes.k", v: "$logs.events.attributes.v"}},
      type: {$first: "$logs.events.type"}
    }
  },
  {$project: {_id: 0, "logs.events": {attributes: {$arrayToObject: "$data" }, type: "$type"}}}
])

See how it works on the playground example - simplified

  • Related