Search code examples
node.jsmongodbmatchaggregate

Mongodb aggregation to pass both a matched array and an unmatched array


I've got a MongoDB / Nodes aggregation that looks a little like this (there are other values in there, but this is the basic idea).

            [
            {
            '$unwind': {
                'path': '$Vehicles'
                }
            },
            {
            '$match': {
                'Vehicles.Manufacturer': 'FORD'
                }
            },
            {
            '$facet': {
                'makes': [
                        {
                    '$group': {
                    '_id': '$Vehicles.Manufacturer', 
                    'count': {
                        '$sum': 1
                                }
                            }
                        }
                    ]
                }
            },
            {
            '$project': {
                'makes': {
                '$sortArray': {
                    'input': '$makes', 
                    'sortBy': 1
                        }
                    }
                }
            }
        ]

This works fine. But I would also like to pass an unmatched list through. IE an an array of vehicles whose Manufacturer = FORD and an other list of all Manufacturer.

Can't get it to work. Any ideas please?

Thanks in advance.

Edit:-

The current output looks like this:

      [{
    "makes": [
      {
        "_id": "FORD",
        "count": 285
      }
    ]
  }]

and ideally it would look something like this:

      [{
    "makes": [
      {
        "_id": "FORD",
        "count": 285
      }
    ],
    "unfiltered_makes": [
      {
        "_id": "ABARTH",
        "count": 1
      },
      {
        "_id": "AUDI",
        "count": 7
      },
      {
        "_id": "BMW",
        "count": 2
      },
      {
        "_id": "CITROEN",
        "count": 4
      },
      {
        "_id": "DS",
        "count": 1
      },
      {
        "_id": "FIAT",
        "count": 1
      }.... etc
    ]
  }]

The data looks a bit like this:

"Vehicles": [
{
  "Id": 1404908,
  "Manufacturer": "MG",
  "Model": "3",
  "Price": 11995 .... etc
},{
  "Id": 1404909,
  "Manufacturer": "FORD",
  "ManufacturerId": 34,
  "Model": "Focus",
  "Price": 12000 .... etc
} ... etc
]

Solution

  • In this case you can do something like:

    db.collection.aggregate([
      {$unwind: "$Vehicles"},
      {$group: {
          _id: "$Vehicles.Manufacturer",
          count: {$sum: 1}}
      },
      {$facet: {
          makes: [{$match: {_id: "FORD"}}],
          unfiltered_makes: [{$group: {_id: 0, data: {$push: "$$ROOT"}}}]
        }
      },
      {$project: {makes: 1, unfiltered_makes: "$unfiltered_makes.data"}}
    ])
    

    See how it works on the playground example

    Another option is:

    db.collection.aggregate([
      {$unwind: "$Vehicles"},
      {$group: {
          _id: "$Vehicles.Manufacturer",
          count: {$sum: 1}}
      },
      {$group: {
          _id: 0,
          unfiltered_makes: {$push: "$$ROOT"},
          makes: {$push: {$cond: [{$eq: ["$_id", "FORD"]}, "$$ROOT", "$$REMOVE"]}}
        }
      }
    ])
    

    See how it works on the playground example