MongoDB MapReduce
I got my first map reduce function working today with MongoDB. Well, it's not a full map-reduce, but just a group function, but it was a good learning experience.
We have a collection to store all the activities of a user's child (user can have many children) when the child does a quiz or reads a book, etc. We use ActivityStrea.ms format to store the activities.
So one of my tasks is to mark all the quizzes each user's child has completed, using the latest attempt as the score of how many corrects over total. I went an extra mile and added some statistics to the requirements list, such as the number of attempts, the overall total questions, and the overall total corrects.
So I have a list of quiz ids, and the username. I would loop through the quiz ids, find the whole quiz, then use group function to find all the related activities group by the children name, and attach the result to the quiz and return to the browser (or the Backbone Model in my case).
The piece of code to perform the grouping is:
$keyf = new MongoCode(' function(activity) { return {child: activity.actor.displayName}; } '); $initial = array( "attempts" => 0 , "corrects" => 0 , "total" => 0 ); $reduce = 'function (activity, prev) { prev.attempts++; if (typeof(prev.latest) === "undefined" || prev.latest.updated < activity.updated) { prev.latest = activity; } for (var i=0; i<activity.object.questions.length; i++) { prev.total++; var question = activity.object.questions[i]; if (question.options[4] === question.options[0]) { prev.corrects++; } } }'; $condition = array( "object.objectType" => "quiz" , "object.id" => $quiz_id // $quiz_id is the element in the loop , "actor.parent" => $user->email , "updated" => array('$exists' => true) ); // Called after all the reduce calls are completed. $finalize = 'function(out) { out.latest_total = 0; out.latest_corrects = 0; for (var i=0; i<out.latest.object.questions.length; i++) { out.latest_total++; var question = out.latest.object.questions[i]; if (question.options[4] === question.options[0]) { out.latest_corrects++; } } }'; $progress = $activities->group( $keyf, $initial, $reduce, array("condition"=>$condition, 'finalize'=>$finalize) );
$progress will have the output
[ { "child": "Child 1" , "attempts": 1 , "corrects": 9 , "total": 10 , "latest": {/* the latest activity object */} , "latest_corrects": 9 , "latest_total": 10 } , { "child": "Child 2" , "attempts": 3 , "corrects": 27 , "total": 30 , "latest": {/* the latest activity object */} , "latest_corrects": 8 , "latest_total": 10 } ]














