skip to Main Content

Given this collection of courses, there is a list of subjects that are composed of objects. Is it possible to create an aggregation to create a new field newSubjects that maps all subjects by their ids? For example, given this collection how to transform the subjects entries from :

[
  {
    _id: ObjectId("623e2f0cb242ee9367eb3c9f"),
    subjects: [ { id: '1', name: 'math' }, { id: '2', name: 'physics' } ]
  },
  {
    _id: ObjectId("623e2f17b242ee9367eb3ca0"),
    subjects: [ { id: '1', name: 'math' }, { id: '3', name: 'biology' } ]
  }
]

into:

{
  newSubjects: { '1': { id: '1', name: 'math' }, '2': { id: '2', name: 'physics' } }
}
{
  newSubjects: { '1': { id: '1', name: 'math' }, '3': { id: '3', name: 'biology' } }
}

Using JavaScript I could do with this expression:

db.courses.find().forEach(x => print({"newSubjects": Object.fromEntries(new Map(x.subjects.map(s => [s.id, s])))}))

I’m trying to figure out an aggregation to accomplish this but haven’t succeeded with $addField and $map yet.

2

Answers


  1. You can try this query:

    Only need one aggregate stage to compound the result you want. In this stage you can use a $map to iterate for every value and return a new one. This new one will have k and v fields.

    • k field will be the object key (1, 2, 3…)
    • v field will be the value field (the object with {id:…, name:…})

    And wrapped by $arrayToObject to translate k and v to the desired object.

    db.collection.aggregate([
      {
        "$project": {
          "_id": 0,
          "newSubjects": {
            "$arrayToObject": {
              "$map": {
                "input": "$$ROOT.subjects",
                "in": {
                  "k": "$$this.id",
                  "v": {
                    "id": "$$this.id",
                    "name": "$$this.name"
                  }
                }
              }
            }
          }
        }
      }
    ])
    

    Example here

    Login or Signup to reply.
  2. Another option:

     db.collection.aggregate([
    {
    "$project": {
      "newSubjects": {
        "$arrayToObject": {
          "$map": {
            "input": "$subjects",
            "as": "s",
            "in": {
              k: "$$s.id",
              v: "$$s"
            }
           }
          }
         }
        }
       }
     ])
    

    Explained:

    Map the array with the necessary k,v suitable for arrayToObject to be projected as newSubjects

    playground

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