Group back unwinded array after match stage and keep the parent data/structure

Hello, I’m having some issues getting my aggregation to work. I have a $lookup where i join two collections, in the joined one I there is nested arrays with objects that I want to match in. The matching part seems to work by I’m not getting the structure back that I’m expecting in the $group stage after $unwind .

Here is a mongo playground with sample data Mongo playground

The original joined document has a structure like this where I among others match on some of the approvalStatus fields and I get the correct ones. But i also want to keep the fields in the “parent” i.e version, allHaveSigned, isLatestVersion etc.

    "agreements": [
        {
          "_id": {
            "$oid": "612c8d394459ebd2483a0920"
          },
          "agreementVersion": [
            {
              "version": 1,
              "allHaveSigned": false,
              "_id": {
                "$oid": "612c8d394459ebd2483a0921"
              },
              "approvalStatus": [
                {
                  "_id": {
                    "$oid": "612c8d394459ebd2483a0922"
                  },
                  "publisherSignsFor": {
                    "$oid": "60801bef03a9cd5f4a097e76"
                  },
                  "signingPerson": {
                    "$oid": "60d06fc53a7fc65be3787df8"
                  },
                  "isSignedByPublisher": true
                },
                {
                  "_id": {
                    "$oid": "612c8d394459ebd2483a0923"
                  },
                  "publisherSignsFor": null,
                  "signingPerson": {
                    "$oid": "60d06fc53a7fc65be3787df8"
                  },
                  "isSignedByPublisher": false
                },
                {
                  "_id": {
                    "$oid": "612c8d394459ebd2483a0921"
                  },
                  "publisherSignsFor": {
                    "$oid": "60801bef03a9cd5f4a097e76"
                  },
                  "signingPerson": {
                    "$oid": "60d06fc53a7fc65be3787df9"
                  },
                  "isSignedByPublisher": true
                }
              ],
              "isLatestVersion": true,
              "createdAt": {
                "$date": "2021-08-30T07:48:09.979Z"
              }
            }
          ],

This is what my pipeline looks like

db.songs.aggregate([
  {
    "$lookup": {
      "from": "agreements",
      "localField": "agreements.songComposition",
      "foreignField": "_id",
      "as": "agreements.songComposition"
    }
  },
  {
    "$unwind": {
      "path": "$agreements.songComposition",
      "preserveNullAndEmptyArrays": false
    }
  },
  {
    "$unwind": {
      "path": "$agreements.songComposition.agreementVersion",
      "preserveNullAndEmptyArrays": false
    }
  },
  {
    "$unwind": {
      "path": "$agreements.songComposition.agreementVersion.approvalStatus",
      "preserveNullAndEmptyArrays": false
    }
  },
  {
    "$match": {
      "$and": [
        {
          "agreements.songComposition.agreementVersion.isLatestVersion": true
        },
        {
          "agreements.songComposition.agreementVersion.approvalStatus.signingPerson": ObjectId("60d06fc53a7fc65be3787df8")
        }
      ],
      "$or": [
        {
          "agreements.songComposition.agreementVersion.approvalStatus.isApprovedByUser": false
        },
        {
          "agreements.songComposition.agreementVersion.approvalStatus.isApprovedByUser": {
            "$exists": false
          }
        }
      ]
    }
  },
  {
    "$facet": {
      "songs": [
        {
          "$group": {
            "_id": "$_id",
            "title": {
              "$first": "$title"
            },
            "artistName": {
              "$first": "$artistName"
            },
            "agreements": {
              "$push": "$agreements.songComposition"
            },
            "count": {
              "$sum": 1
            }
          }
        }
      ],
      "countAgreements": [
        {
          "$group": {
            "_id": null,
            "count": {
              "$sum": 1
            }
          }
        }
      ]
    }
  },
  {
    "$project": {
      "songs": 1,
      "sumAgreements": {
        "$arrayElemAt": [
          "$countAgreements.count",
          0
        ]
      }
    }
  }
])

The result looks like this and I get two elements in the agreements array where it should only be one and I think it is because I cant join on the approvalStatus fields correctly or missing something else.

[
  {
    "songs": [
      {
        "_id": ObjectId("60debdaae7593406b682e45d"),
        "agreements": [
          {
            "__v": 0,
            "_id": ObjectId("612c8d394459ebd2483a0920"),
            "addedAt": ISODate("2021-08-30T07:48:09.979Z"),
            "agreementVersion": {
              "_id": ObjectId("612c8d394459ebd2483a0921"),
              "allHaveSigned": false,
              "approvalStatus": {
                "_id": ObjectId("612c8d394459ebd2483a0922"),
                "isSignedByPublisher": true,
                "publisherSignsFor": ObjectId("60801bef03a9cd5f4a097e76"),
                "signingPerson": ObjectId("60d06fc53a7fc65be3787df8")
              },
              "createdAt": ISODate("2021-08-30T07:48:09.979Z"),
              "isLatestVersion": true,
              "version": 1
            }
          },
          {
            "__v": 0,
            "_id": ObjectId("612c8d394459ebd2483a0920"),
            "addedAt": ISODate("2021-08-30T07:48:09.979Z"),
            "agreementVersion": {
              "_id": ObjectId("612c8d394459ebd2483a0921"),
              "allHaveSigned": false,
              "approvalStatus": {
                "_id": ObjectId("612c8d394459ebd2483a0923"),
                "isSignedByPublisher": false,
                "publisherSignsFor": null,
                "signingPerson": ObjectId("60d06fc53a7fc65be3787df8")
              },
              "createdAt": ISODate("2021-08-30T07:48:09.979Z"),
              "isLatestVersion": true,
              "version": 1
            }
          }
        ],
        "artistName": "My test artisters",
        "count": 2,
        "title": "Branches"
      }
    ],
    "sumAgreements": 2
  }
]

The result I would like to have looks like this

[
  {
    "songs": [
      {
        "_id": ObjectId("60debdaae7593406b682e45d"),
        "agreements": [
          {
            "__v": 0,
            "_id": ObjectId("612c8d394459ebd2483a0920"),
            "addedAt": ISODate("2021-08-30T07:48:09.979Z"),
            "agreementVersion": [
              {
              "_id": ObjectId("612c8d394459ebd2483a0921"),
              "allHaveSigned": false,
              "approvalStatus": [
                {
                "_id": ObjectId("612c8d394459ebd2483a0922"),
                "isSignedByPublisher": true,
                "publisherSignsFor": ObjectId("60801bef03a9cd5f4a097e76"),
                "signingPerson": ObjectId("60d06fc53a7fc65be3787df8")
              },
              {"_id": ObjectId("612c8d394459ebd2483a0923"),
                "isSignedByPublisher": false,
                "publisherSignsFor": null,
                "signingPerson": ObjectId("60d06fc53a7fc65be3787df8")
              },
               ],
              "createdAt": ISODate("2021-08-30T07:48:09.979Z"),
              "isLatestVersion": true,
              "version": 1
            }
            ]
          },
          
        ],
        "artistName": "My test artisters",
        "count": 2,
        "title": "Branches"
      }
    ],
    "sumAgreements": 2
  }
]

Thankful for any help!

Hello! @gamisLamis. Could you let me know which mongo version you are using?

Hi @HomAskIe
I’m writing the aggregations in the mongodb compass and have this under Edition MongoDB 4.2.8 Community

and terminal returns this mongod --version
db version v4.2.8

Hey @gamisLamis . Quick question: Does the songs.agreements has to be array? It seems like it often has only one item?!

@HomAskIe Hmm good question, maybe not in this particular case as there should only be one agreement (songComposition) in this query/aggregation.

How in real business case? A song can have how many agreements with different status?

As of now we only have three types of agreements. songComposition and two others, but the other two should not be present in this query.

So other agreements will be the same schema or different?

The other ones does not have agreementVersion field or none of the sub field e.g approvalStatus. They are much more “flat” with no nested arrays. But exists in the agreement collection.

Hello! @gamisLamis. How’s going?
I’m sorry for the delay. But I got something that can help you. Try this out: Mongo playground

Let me know if that helps! Any questions just let me know

Thanks :slight_smile:

2 Likes

The trick here is that the filter out by signingPerson and other fields is done by using $filter. So we don’t have to $unwind the approvalStatus thus no need to put them back together.

1 Like

Thanks! Really apprictate it. Works like a charm!

1 Like

Nice! Thanks @HomAskIe ! I did not know about this MongoDB Playground .net link. Great tool! Thank you for the question and I love the profile_name @gamisLamis . My favorite so far. :dizzy: Going to get leveled up on the PLAYGROUND!

Also, @gamisLamis, I forgot to say Howdy! :cowboy_hat_face:…and welcome to the community! I love it here. Have fun and I hope you find all the answers that you are looking for.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.