Max Nachamkin Max Nachamkin - 1 month ago 6
Node.js Question

Node.js Mongoose Calculations (arrays, populations?)

I'm trying to figure out a way to calculate data from a multitude of different collections in my MongoDB database.

Essentially, I have an app that sends out a Survey to each user on a team that allows them to vote ever other user on the team on a scale of 1-10.

I collect this data in a Votes collection that looks like the following:

// Vote Schema

var voteSchema = mongoose.Schema({
_creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
voteFor: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
survey: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Survey'
},
rating : Number,
status : String,
});


Here's some sample data that's in my local dev database:

"_id" : ObjectId("5812caceb4829937f4599b57"), "rating" : 10, "voteFor" : ObjectId("580fe9ba5b72196bc4a8d946"), "_creator" : ObjectId("580f8ddc5de2365b6852bc03"), "survey" : ObjectId("5812caaeb4829937f4599b54"), "__v" : 0 }
{ "_id" : ObjectId("5812caceb4829937f4599b58"), "rating" : 6, "voteFor" : ObjectId("58114aae3081aa62982b814d"), "_creator" : ObjectId("580f8ddc5de2365b6852bc03"), "survey" : ObjectId("5812caaeb4829937f4599b54"), "__v" : 0 }
{ "_id" : ObjectId("5812caddb4829937f4599b5a"), "rating" : 4, "voteFor" : ObjectId("580f8ddc5de2365b6852bc03"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5812caaeb4829937f4599b54"), "__v" : 0 }
{ "_id" : ObjectId("5812caddb4829937f4599b5b"), "rating" : 6, "voteFor" : ObjectId("580fc963911b2864e0ccfcc2"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5812caaeb4829937f4599b54"), "__v" : 0 }
{ "_id" : ObjectId("5812caddb4829937f4599b5c"), "rating" : 1, "voteFor" : ObjectId("580fc99e911b2864e0ccfcc3"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5812caaeb4829937f4599b54"), "__v" : 0 }
{ "_id" : ObjectId("5813ec0d12dd1731a8892bec"), "rating" : 4, "voteFor" : ObjectId("580f8ddc5de2365b6852bc03"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5813ebe112dd1731a8892beb"), "__v" : 0 }
{ "_id" : ObjectId("5813ec0d12dd1731a8892bed"), "rating" : 7, "voteFor" : ObjectId("580fc963911b2864e0ccfcc2"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5813ebe112dd1731a8892beb"), "__v" : 0 }
{ "_id" : ObjectId("5813ec0d12dd1731a8892bee"), "rating" : 5, "voteFor" : ObjectId("580fc99e911b2864e0ccfcc3"), "_creator" : ObjectId("58114aae3081aa62982b814d"), "survey" : ObjectId("5813ebe112dd1731a8892beb"), "__v" : 0 }


As you can see, I have _creator, voteFor, and survey linked as ObjectIds to their respective Models.

What I'm attempting to do is output the following on a front-end page for each Survey that would look like the following:

Max
===
Total rating: 32 - this is the total of all the ratings where people voted for them (stored in ratings and voteFor, so I'd add up all the ratings that had voteFor as their userID in that particular survey and output it)

Average rating: 6
% of Survey total rating: 15%

John
===
Total rating: 60
Average rating: 9
% of Survey total rating: 34%


Basically I want to create/output calculations for each User in a specific Survey based on some math calculations that I do with their rating data.

I've tried running find queries for the model and running for loops on the rating data to get things like "Total Rating for the Survey" but I'm stuck on how to get data for each individual User in the survey and how to output it.

I'm comfortable making the math calculations, but I'm unsure of how to structure a query or model function to allow me to do the calculations and send it to a view (using handlebars) with the right data.

Do I use .populate? Create a new model to put the calculations in and then output it to my view separately? Something else I'm not aware of?

I can't seem to get anything to work with the docs/searching I've done and would love some help!

Cheers,

Max

Answer

To count all record for each distinct $_creator, use a $group aggregation with $sum : 1 :

var voteItems = mongoose.model('Votes', voteSchema);

voteItems.aggregate([{
    $group: {
        "_id": "$_creator",
        "count": { $sum: 1 }
    }
}], function(err, result) {
    console.log("number of ratings list : \n", result);
});

It gives you :

{ "_id" : ObjectId("58114aae3081aa62982b814d"), "count" : 6 }
{ "_id" : ObjectId("580f8ddc5de2365b6852bc03"), "count" : 2 }

For the average of all ratings for each distinct $_creator, also use a $group and use $avg :

voteItems.aggregate([{
    $group: {
        "_id": "$_creator",
        total: { $avg: "$rating" }
    }
}], function(err, result) {
    console.log("average ratings : ", result[0].total);
});

It gives you :

{ "_id" : ObjectId("58114aae3081aa62982b814d"), "total" : 4.5 }
{ "_id" : ObjectId("580f8ddc5de2365b6852bc03"), "total" : 8 }

You can achieve much more with aggregation check : mongodb aggegration and group aggregation

Comments