Mongo Age Group Aggregation

喜夏-厌秋 提交于 2019-12-11 09:47:22

问题


Please consider the collection bellow

 $people->insert(array("user_id" => "1", "day" => "Monday", 'age' => 18));
 $people->insert(array("user_id" => "3", "day" => "Monday", 'age' => 24));
 $people->insert(array("user_id" => "1", "day" => "Monday", 'age' => 18));
 $people->insert(array("user_id" => "1", "day" => "Monday", 'age' => 18));
 $people->insert(array("user_id" => "2", "day" => "Monday", 'age' => 25));
 $people->insert(array("user_id" => "4", "day" => "Monday", 'age' => 33));
 $people->insert(array("user_id" => "1", "day" => "Tuesday", 'age' => 18));
 $people->insert(array("user_id" => "2", "day" => "Tuesday", 'age' => 25));
 $people->insert(array("user_id" => "1", "day" => "Wednesday", 'age' => 18));
 $people->insert(array("user_id" => "2", "day" => "Thursday", 'age' => 25));
 $people->insert(array("user_id" => "1", "day" => "Friday", 'age' => 18));

Can anyone help me to get count of distinct users within a age group? For example for the schema above i would like to get

      Age 0-17 = 0, Age 18-25 = 3, Age 26-32 = 0 Age > 32 = 1

I have tried to use the $cond operator but didn't manage to get it it work. Every time i try to run or change it i get one of the 2 errors:

  1. The "$cond" operator requires 3 operands or
  2. A pipeline stage specification object must contain exactly one field.

My query is bellow any help is much appreciated. Thanks in advance,

    $query =
        array(
           $project' => array(
                ageGroup' => array(
                   array('$cond'=>  array('$user_data.age' => array('$lt' => 18),
                                           "age_0_17",
                   array('$cond'=>  array('$user_data.age' => array('$lte' => 25),
                                           "age_18_25",
                   array('$cond'=>  array('$user_data.age' => array('$lte' => 32),
                                           "age_26_32",
                                           "age_Above_32")))))
                    )
                ),
            ),

            array(
                '$group' => array(
                    '_id'  => '$ageGroup',
                    'count' => array('$sum' => 1),
                )
            ));

The Answer by @Neil Lunn is 90% right, it did not give me the desired output but leaded took me there.

With Neil's query the output i get is:

age_Above_32 = 1 and age_18_25 = 10 

The output for distinct user_id count should be

age_Above_32 = 1 and age_18_25 = 3 

To obtain that i just had to tweak Neil's query a little bit. The final query is below.

$query2 = array(
        array(
            '$group' => array(
                '_id' => array(
                    'ageGroup' => array(
                        '$cond' =>  array(
                            array('$lt' => array( '$age', 18 )),
                            'age_0_17',
                            array(
                                '$cond' => array(
                                    array( '$lte' => array( '$age', 25 )),
                                    'age_18_25',
                                    array(
                                        '$cond' => array(
                                            array( '$lte' => array ( '$age', 32 )),
                                            'age_26_32',
                                            'age_Above_32'
                                        )
                                    )
                                )
                            )
                        )
                    ),
                    'user_id' =>'$user_id'
                )
            )

        ),
        array(
            '$group' => array(
                '_id'  => '$_id.ageGroup',
                'count' => array('$sum' => 1)
            ))
    );

回答1:


You were in the right place, but as $cond requires three arguments (being the evaluation , true result and false result) you need to "nest" these operations, which each subsequent $cond as the false condition. So your syntax here is a little off.

You can also do this just in the $group to avoid passing through the whole collection with a separate $project. Based on the document structure you give as an example you would form like this:

$pipeline = array(
  array(
    '$group' => array(
      '_id' => array(
        '$cond' =>  array(
          array('$lt' => array( '$age', 18 )),
          'age_0_17',
          array(
            '$cond' => array(
              array( '$lte' => array( '$age', 25 )),
              'age_18_25',
              array(
                '$cond' => array(
                  array( '$lte' => array ( '$age', 32 )),
                  'age_26_32',
                  'age_Above_32'
                )
              )
            )
          )
        )
      ),
      'count' => array( '$sum' => 1 )
    )
  )
);

Also noting that logical comparison operators such as $lt work differently in these stages to their query counterparts. They themselves take an array of arguments being the values to test and compare. They return true/false based on that comparison, which is the requirement for the first argument to $cond.

Always handy to have a json_encode somewhere where you are debugging the form of pipeline queries, as JSON will be the general scope of examples:

echo json_encode( $pipeline, JSON_PRETTY_PRINT ) . "\n";

Which yields the common JSON structure:

[
    { "$group": {
        "_id": { 
            "$cond":[
                { "$lt":["$age",18] },
                "age_0_17",
                { "$cond":[
                    { "$lte":["$age",25] },
                    "age_18_25",
                    { "$cond":[
                        { "$lte":["$age",32] },
                        "age_26_32",
                        "age_Above_32"
                    ]}
                ]}
            ]
        },
        "count":{ "$sum": 1 }
    }}
]


来源:https://stackoverflow.com/questions/25150635/mongo-age-group-aggregation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!