Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add a computed field using an arbitrary function with Mongo aggregate framework?

I'm using MongoDB's Aggregate Framework. I have an existing field in each of my documents:

time: Date

And I wish create a new field timeBlock based on a simple funcion:

var dateToTimeBlock = function(dateString, timeBlock){
    return new Date(dateString).valueOf() / timeBlock 
}

I understand that $group can add fields, but the functions used to calculate those fields seems to be built into mongo, eg, $avg, $add, etc. Is it possible to generate a computed value based on an arbitrary field?

like image 982
mikemaccana Avatar asked Jun 17 '14 12:06

mikemaccana


People also ask

How do you add a field in aggregate?

You can include one or more $addFields stages in an aggregation operation. To add field or fields to embedded documents (including documents in arrays) use the dot notation. See example. To add an element to an existing array field with $addFields , use with $concatArrays .

Which aggregation method is preferred for use by MongoDB?

The pipeline provides efficient data aggregation using native operations within MongoDB, and is the preferred method for data aggregation in MongoDB. The aggregation pipeline can operate on a sharded collection. The aggregation pipeline can use indexes to improve its performance during some of its stages.


1 Answers

You can compute fields using aggregation framework. If you want to use native JavaScript functions (like valueOf on Date object) you will have to use Map-Reduce.

Although, aggregation framework is not as flexible as Map-Reduce, in most cases it will be significantly faster. If performance is critical I would precalculate those values and use a simple query instead.

If you want to use aggregation, you can simplify it by converting the Date into an integer or add a new field in the document that's an integer. Then you can than do your calculations easily with $divide.

db.coll.aggregate([
    { $project : { dateToTime: { $divide : [ "$timeInt", timeBlock ] }}}
]);

Or if timeBlock is a field in the same document you can do it like this:

db.coll.aggregate([
    { $project : { dateToTime: { $divide : [ "$timeInt", "$timeBlock" ] }}}
]);
like image 123
Christian P Avatar answered Nov 15 '22 06:11

Christian P