Reputation: 77
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
Upvotes: 0
Views: 444
Reputation: 77
Thanks Bertrand. Your code helped me understand how aggregate works and how I can make calculations with it. I appreciate your support.
For reference, I used the following code to give me the ratings of each individual based on an individual survey.
//Get ratings for each user
Vote.aggregate(
[
{
$match : { survey : mongoose.Types.ObjectId(req.params.id) }
},
{
$group: { "_id": "$voteFor", "total" : { $sum: "$rating" }
}},
{ $lookup: { "from": "users", "localField": "_id", "foreignField":"_id", "as": "user"} }
],
function(err, result) {
console.log(result);
});
Upvotes: 0
Reputation: 45432
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
Upvotes: 1