Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$unwind empty array

I have a collection of users where each document has following structure:

{   "_id": "<id>",   "login": "xxx",   "solved": [     {       "problem": "<problemID>",       "points": 10     },     ...   ] } 

The field solved may be empty or contain arbitrary many subdocuments. My goal is to get a list of users together with the total score (sum of points) where users that haven't solved any problem yet will be assigned total score of 0. Is this possible to do this with a single query (ideally using aggregation framework)?

I was trying to use following query in aggregation framework:

{ "$group": {   "_id": "$_id",   "login": { "$first": "$login" },   "solved": { "$addToSet": { "points": 0 } } } } { "$unwind": "$solved" } { "$group": {   "_id": "$_id",   "login": { "$first": "$login" },   "solved": { "$sum": "$solved.points" } } } 

However I am getting following error:

exception: The top-level _id field is the only field currently supported for exclusion 

Thank you in advance

like image 997
Karel Horak Avatar asked Dec 15 '12 18:12

Karel Horak


People also ask

How do I unwind an array in MongoDB?

The $unwind operator is used to deconstructing an array field in a document and create separate output documents for each item in the array. The only difference between input and output documents is that the value of the array field in output documents is replaced with a single item from the input document array.

How do you unwind more than one array?

As you can unwind more than one arrays in single aggregation pipeline. first unwind->group->count, then repeat it for another array in same pipeline. okay let me try, I will then post the query as well as the output..

What is preserveNullAndEmptyArrays?

preserveNullAndEmptyArrays option Defining the “preserveNullAndEmptyArrays” option in a $unwind operation allows the user to include documents where the array field is missing, null, or an empty array. The “preserveNullAndEmptyArrays” option only takes Boolean arguments like true or false.


2 Answers

With MongoDB 3.2 version and newer, the $unwind operator now has some options where in particular the preserveNullAndEmptyArrays option will solve this.

If this option is set to true and if the path is null, missing, or an empty array, $unwind outputs the document. If false, $unwind does not output a document if the path is null, missing, or an empty array. In your case, set it to true:

db.collection.aggregate([     { "$unwind": {         "path": "$solved",         "preserveNullAndEmptyArrays": true     } },     { "$group": {         "_id": "$_id",         "login": { "$first": "$login" },         "solved": { "$sum": "$solved.points" }     } } ]) 
like image 122
chridam Avatar answered Sep 21 '22 16:09

chridam


Here is the solution - it assumes that the field "solved" is either absent, is equal to null or has an array of problems and scores solved. The case it does not handle is "solved" being an empty array - although that would be a simple additional adjustment you could add.

project = {$project : {         "s" : {             "$ifNull" : [                 "$solved",                 [                     {                         "points" : 0                     }                 ]             ]         },         "login" : 1     } }; unwind={$unwind:"$s"}; group= { "$group" : {         "_id" : "$_id",         "login" : {             "$first" : "$login"         },         "score" : {             "$sum" : "$s.points"         }     } } 

db.students.aggregate( [ project, unwind, group ] );

like image 28
Asya Kamsky Avatar answered Sep 21 '22 16:09

Asya Kamsky