Search code examples
mongodbmongooseaggregation-frameworkpymongo

MongoDB nested aggregation


So, I have two collections User and Book, and I want to aggregate them to receive an output (shown at the end of the post) for a specific User ID.

below are the collections:

User

Each document contains User ID, Name of the User and an array containing ID of a document from Book collection and a Boolean property read.

[
  {
    _id: ObjectId('adfa2sd5'),
    name: "Mia",
    books: [
        {
            bid: "154854",
            read: true
        },
        {
            bid: "5475786",
            read: false
        }
    ]

  },

  {
    _id: ObjectId('uai5as5a'),
    name: "Jack",
    books: [
        {
            bid: "5475786",
            read: true
        }
    ]

  }
]

Book

Each document possesses a book ID and name of the book.

[
  {
    _id: ObjectId('154854'),
    name: "The Great Gatsby"
  },

  {
    _id: ObjectId('5475786'),
    name: "Frankenstein"
  },
]

Output:

The output contains the User ID, along an array book_list which contains detail of each book (id, name) from the documents of Book collection based on the books.bid from User document and read field which was along books.bid.

[
  {
    _id: ObjectId('adfa2sd5'),
    book_list: [
        {
            _id: ObjectId('154854'),
            name: "The Great Gatsby",
            read: true
        },
        {
            _id: ObjectId('5475786'),
            name: "Frankenstein",
            read: false
        }
    ]

  }
]

Solution

  • Here's one way you could do it.

    db.users.aggregate([
      {
        "$match": {
          "_id": ObjectId("fedcba9876543210fedcba98")
        }
      },
      {
        "$lookup": {
          "from": "books",
          "localField": "books.bid",
          "foreignField": "_id",
          "as": "bookList"
        }
      },
      {
        "$set": {
          "books": {
            "$map": {
              "input": "$books",
              "as": "book",
              "in": {
                "$mergeObjects": [
                  "$$book",
                  {
                    "name": {
                      "$getField": {
                        "field": "name",
                        "input": {
                          "$first": {
                            "$filter": {
                              "input": "$bookList",
                              "cond": {"$eq": ["$$this._id", "$$book.bid"]}
                            }
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      },
      {"$unset": ["bookList", "name"]}
    ])
    

    Try it on mongoplayground.net.