Search code examples
mongodbaggregation-framework

Find all the values for a nested attribute based on the value of another attribute document in MongoDB shell


I have a bunch of documents in a mongoDB collection structured like this:

[
  {
    "_id": "11111",
    "headerInfo": [
      {
        "headerAttribute": "string"
      }
    ],
    "attributeCollection": [
      {
        "name": "attributeOne",
        "value": "foo",
        "metadata": false,
        "otherAttribute": "string"
      },
      {
        "name": "attributeTwo",
        "value": "foo2",
        "metadata": false,
        "otherAttribute": "string"
      }
    ],
    "status": "active"
  },
  {
    "_id": "11112",
    "headerInfo": [
      {
        "headerAttribute": "string"
      }
    ],
    "attributeCollection": [
      {
        "name": "attributeOne",
        "value": "bar",
        "metadata": false,
        "otherAttribute": "string"
      },
      {
        "name": "attributeTwo",
        "value": "foo2",
        "metadata": true,
        "otherAttribute": "string"
      }
    ],
    "status": "active"
  }
]
 

I want to get a list of all of the unique values that are used in {"attributeCollection.name" : "attributeOne"} - so a new document that is something along the lines of ["foo", "bar"]

I have messed around with https://www.mongodb.com/docs/manual/reference/command/distinct/#mongodb-dbcommand-dbcmd.distinct and it just returns collections of documents. I have a feeling some combination of $elemmatch, $arrayElemAt and/or aggregation and/or projection is necessary.


Solution

  • If you are just looking for a flat array of unique attributeCollection.value where the attributeCollection.name is equal to attributeOne then you can use aggregate:

    db.collection.aggregate([
      {
        $match: {
          "attributeCollection.name": "attributeOne"
        }
      },
      {
        $project: {
          v: {
            $filter: {
              input: "$attributeCollection",
              as: "ac",
              cond: {
                $eq: [
                  "$$ac.name",
                  "attributeOne"
                ]
              }
            }
          }
        }
      },
      {
        $unwind: "$v"
      },
      {
        $group: {
          _id: null,
          values: {
            $addToSet: "$v.value"
          }
        }
      }
    ])
    

    Explanation:

    1. $match : find the documents where "attributeCollection.name": "attributeOne"
    2. $project: only output the $attributeCollection array objects where "attributeCollection.name": "attributeOne" by leveraging $filter.
    3. $unwind: the resulting array which will be called v.
    4. $group: by their $v.value and while doing that push the $v.value to an array but use $addToSet to push so that only unique values are added to the array.

    See HERE for a working example.