Search code examples
mongodbmongoosemongodb-queryaggregation-framework

MongoDB filter and aggregate by multiple array elements example


Lets say my collection looks like:

{
        "_id" : ObjectId("6451529ad254d565bd12156b"),
        "contentType" : "Json",
        "fieldName" : "xxx",
        "method" : "GET",
        "value" : "Car",
    }
    {
        "_id" : ObjectId("6451529ad254d565bd12156d"),
        "contentType" : "Json",
        "fieldName" : "xxx",
        "method" : "GET",
        "value" : "Plane",
    }
    {
        "_id" : ObjectId("6451529ad254d565bd121570"),
        "contentType" : "Json",
        "fieldName" : "yyy",
        "method" : "GET",
        "value" : "Smile",
    }
    {
        "_id" : ObjectId("6451529ad254d565bd12156b"),
        "contentType" : "Empty",
        "fieldName" : "zzz",
        "method" : "POST",
        "value" : "Train",
    }
    {
        "_id" : ObjectId("6451529ad254d5651112156b"),
        "contentType" : "Empty",
        "fieldName" : "zzz",
        "method" : "POST",
        "value" : "Player",
    }
    {
        "_id" : ObjectId("6451529ad254d565bd12156d"),
        "contentType" : "Empty",
        "fieldName" : "zzz",
        "method" : "POST",
        "value" : "Van",
    }
    {
        "_id" : ObjectId("6451529ad254d565bd121570"),
        "contentType" : "Json",
        "fieldName" : "name4",
        "method" : "GET",
        "value" : "Van",
    }

I'd like to request mongo with array and filter by inner elements of each array item, finally group by fieldName.

For example if I request:

       [ {
             method: GET,
             contentType : Json,
             fieldName: xxx,
           } ,
           {
             method: POST,
             contentType : Empty,
             fieldName: zzz,
            }
        ]

I want to have all objects with unique combination of
{ method: GET, contentType : Json , fieldName: xxx} and aggregatin all values by fieldName: xxx

the response of this request should look like :

   [ {
         method: GET,
         contentType : Json,
         fieldName: xxx
         values: ["Car", "Plane"] // here the aggregation by fieldName:xxx
       } ,
       {
         method: POST,
         contentType : Empty,
         fieldName: zzz,
         values: ["Player", "Train" ,"Van"] // here the aggregation by fieldName:zzz
        }
    ] 

tried to do like this but it didn't work

db.collection.aggregate([
    {$match: {"$and": [{method: "GET"}, {contentType: 'Json'}, {fieldName: "xxx"}]}},
    {$group: {values: "$fieldName"}}}
]) 

but I need it as array in input and return the same structure as request + aggregated values by fieldName preferably by Scala or Java


Solution

  • One option is to use an aggregation pipeline with a $filter step::

    db.collection.aggregate([
      {$match: {
          $expr: {$gt: [
              {$size: {$filter: {
                    input: input,
                    cond: {$and: [
                        {$eq: ["$$this.method", "$method"]},
                        {$eq: ["$$this.contentType", "$contentType"]},
                        {$eq: ["$$this.fieldName", "$fieldName"]}
                    ]}
              }}},
              0
          ]}
      }},
      {$group: {
          _id: {
            fieldName: "$fieldName",
            contentType: "$contentType",
            method: "$method"
          },
          values: {$push: "$value"}
      }},
      {$replaceRoot: {newRoot: {$mergeObjects: ["$_id", {values: "$values"}]}}}
    ])
    

    See how it works on the playground example