Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group by specific element of array with mongo aggregation framework

Is it possible to use the aggregation framework to group by a specific element of an array?

Such that with documents like this:

{
  name: 'Russell',
  favourite_foods: [
    { name: 'Pizza', type: 'Four Cheeses' },
    { name: 'Burger', type: 'Veggie'}
  ],
  height: 6
}

I could get a distinct list of top favourite foods (ie. foods at index 0) along with the height of the tallest person who's top favourite food that is?

Something like this (although it doesn't work as the array index access dot notation doesn't seem to work in the aggregation framework):

db.people.aggregate([
  { $group : { _id: "$favourite_foods.0.name", max_height: { $max : "$height" } } }
])
like image 310
Russell Avatar asked Oct 24 '12 18:10

Russell


People also ask

How do I pull an element from an array in MongoDB?

The $pull operator removes from an existing array all instances of a value or values that match a specified condition. The $pull operator has the form: { $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } } To specify a <field> in an embedded document or in an array, use dot notation.

How do I filter an array in MongoDB aggregation?

Filter MongoDB Array Element Using $Filter Operator This operator uses three variables: input – This represents the array that we want to extract. cond – This represents the set of conditions that must be met. as – This optional field contains a name for the variable that represent each element of the input array.


1 Answers

Seems like you are relying on the favorite food for each person being first in the array. If so, there is an aggregation framework operator you can take advantage of.

Here is the pipeline you can use:

db.people.aggregate(
[
    {
        "$unwind" : "$favourite_foods"
    },
    {
        "$group" : {
            "_id" : {
                "name" : "$name",
                "height" : "$height"
            },
            "faveFood" : {
                "$first" : "$favourite_foods"
            }
        }
    },
    {
        "$group" : {
            "_id" : "$faveFood.name",
            "height" : {
                "$max" : "$_id.height"
            }
        }
    }
])

On this sample dataset:

> db.people.find().pretty()
{
    "_id" : ObjectId("508894efd4197aa2b9490741"),
    "name" : "Russell",
    "favourite_foods" : [
        {
            "name" : "Pizza",
            "type" : "Four Cheeses"
        },
        {
            "name" : "Burger",
            "type" : "Veggie"
        }
    ],
    "height" : 6
}
{
    "_id" : ObjectId("5088950bd4197aa2b9490742"),
    "name" : "Lucy",
    "favourite_foods" : [
        {
            "name" : "Pasta",
            "type" : "Four Cheeses"
        },
        {
            "name" : "Burger",
            "type" : "Veggie"
        }
    ],
    "height" : 5.5
}
{
    "_id" : ObjectId("5088951dd4197aa2b9490743"),
    "name" : "Landy",
    "favourite_foods" : [
        {
            "name" : "Pizza",
            "type" : "Four Cheeses"
        },
        {
            "name" : "Pizza",
            "type" : "Veggie"
        }
    ],
    "height" : 5
}
{
    "_id" : ObjectId("50889541d4197aa2b9490744"),
    "name" : "Augie",
    "favourite_foods" : [
        {
            "name" : "Sushi",
            "type" : "Four Cheeses"
        },
        {
            "name" : "Pizza",
            "type" : "Veggie"
        }
    ],
    "height" : 6.2
}

You get these results:

{
    "result" : [
        {
            "_id" : "Pasta",
            "height" : 5.5
        },
        {
            "_id" : "Pizza",
            "height" : 6
        },
        {
            "_id" : "Sushi",
            "height" : 6.2
        }
    ],
    "ok" : 1
}
like image 145
Asya Kamsky Avatar answered Oct 24 '22 06:10

Asya Kamsky