R Brennan
R Brennan

Reputation: 715

Returning a subset of array items in MongoDB

Can anyone offer advice on how I would return a subset of array items? For example, let's suppose I have a collection of documents (similar to the example below) that contains a simple _id key and a key that contains an array of objects.

I would like to find all _id's and the matching objects that match a simple criteria:

// campaigns
{
  "_id" : "Fred's C25K",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "nike"
    },
    {
      "date" : "2015-06-17",
      "source" : "reebok",
    },
    {
      "date" : "2015-06-12",
      "source" : "nike"
    },
    {
      "date" : "2015-06-14",
      "source" : "adidas"
    },
  ]
},
{
  "_id" : "Mike's Marathon",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "nike"
    }
  ]
},
{
  "_id" : "Jacob's Jamboree",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "keen"
    }
  ]
}

I would like my result to contain the _id and any matching objects for, say, a date value of "2015-06-17"

// GOAL => To generate a result set that looks like:
{
  "_id" : "Fred's C25K",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "nike"
    },
    {
      "date" : "2015-06-17",
      "source" : "reebok",
    }
  ]
},
{
  "_id" : "Mike's Marathon",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "nike"
    }
  ]
},
{
  "_id" : "Jacob's Jamboree",
  "campaignData" : [
    {
      "date" : "2015-06-17",
      "source" : "keen"
    }
  ]
}

Upvotes: 6

Views: 924

Answers (1)

chridam
chridam

Reputation: 103365

Use the aggregation framework to achieve the desired result. The following pipeline consists of a $match operator stage as the first step to filter the documents that should pass through the pipeline.

The next stage is the $addFields operator that allows you to output documents that contain all existing fields from the input documents and newly added fields.

Within this step you can use the $filter operator to select a subset of an array to return based on the specified filter:

db.collection.aggregate([
    { "$match": {
        "campaignData.date" : "2015-06-17"
    } },
    { "$addFields": {
        "campaignData": {
            "$filter": {
                "input": "$campaignData",
                "cond": {
                    "$eq": ["$$this.date", "2015-06-17"]
                }
            }
        }
    } }
])

Result:

/* 0 */
{
    "result" : [ 
        {
            "_id" : "Mike's Marathon",
            "campaignData" : [ 
                {
                    "date" : "2015-06-17",
                    "source" : "nike"
                }
            ]
        }, 
        {
            "_id" : "Jacob's Jamboree",
            "campaignData" : [ 
                {
                    "date" : "2015-06-17",
                    "source" : "keen"
                }
            ]
        }, 
        {
            "_id" : "Fred's C25K",
            "campaignData" : [ 
                {
                    "date" : "2015-06-17",
                    "source" : "nike"
                }, 
                {
                    "date" : "2015-06-17",
                    "source" : "reebok"
                }
            ]
        }
    ],
    "ok" : 1
} 

Upvotes: 3

Related Questions