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
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.