2

I'd like to flatten document array into a keyed objects using the aggregation functionality. Here’s an example of my documents:

[ 
    {
        "_id": 1,
        "texts": [
            { "language": "english", "text": "hello" },
            { "language": "german", "text": "hallo" },
            { "language": "french", "text": "bonjour" }
        ]
    }, …
]

Expected result:

[
     {
        "_id": 1,
        "texts": {
            "english": "hello",
            "german": "hallo",
            "french": "bonjour"
        }
    }, …
]

I’ve looked at different operators, e.g. $map, but this seems to be focued on transforming array to array. I’d probably need a JS equivalent of $reduce, but couldn’t find a way of accumulating my values into an object.

Any ideas?

3 Answers 3

4

You need $map and $arrayToObject which converts an array of k-v pairs into single object:

db.col.aggregate([
    {
        $addFields: {
            texts: {
                $arrayToObject: {
                    $map: {
                        input: "$texts",
                        as: "text",
                        in: {
                            k: "$$text.language",
                            v: "$$text.text"
                        }
                    }
                }
            }
        }
    }
])
Sign up to request clarification or add additional context in comments.

Comments

3

You can also use $zip with $arrayToObject.

Something like

db.col.aggregate([{
  "$project": {
    "texts":{
     "$arrayToObject":{
       "$zip": {
        "inputs": [
          "$texts.language",
          "$texts.text"
        ]
      }
    }
  }}
}])

1 Comment

Cool, even more concise. Will give it a try tomorrow and give feedback. Thx!
2

You can try this using $map and $arrayToObject aggregation in mongodb 3.4.4 and above

Just convert language field with key(k) and text with value(v) using $map and transform the array to object using $arrayToObject

db.collection.aggregate([
  { "$addFields": {
    "texts": {
      "$map": {
        "input": "$texts",
        "in": {
          "k": "$$this.language",
          "v": "$$this.text"
        }
      }
    }
  }},
  { "$addFields": {
    "texts": { "$arrayToObject": "$texts" }
  }}
])

7 Comments

Many thanks for the very rapid answer! +1 Both were exactly simultaneous and work equally well. The reason I'm accepting mickl’s answer instead of yours is, that I prefer having everything in a single pipeline stage. Cheers!
Interesting update: I'm actually still running MongoDB 3.4.6 -- still the aggregation works just fine.
not sure why so... but $arrayToObject has been introduced in mongodb 3.6
That's how they announced it. Mind you, that's not true. ;) It has been around since v3.4.4: docs.mongodb.com/v3.4/reference/operator/aggregation/…
@dnickless oops!!! I had mistaken... thanks for comeuppance me... updated the answer
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.