MongoDB aggregate Query for sum - mongodb

I have a collection as follows
{ "_id" : 0, "name" : "aimee Zank", "scores" :
[
{ "type" : "exam", "score" : 1.463179736705023 },
{ "type" : "quiz", "score" : 11.78273309957772 },
{ "type" : "homework", "score" : 6.676176060654615}
] }
{"_id" : 1, "name" : "Aurelia Menendez", "scores" :
[
{ "type" : "exam", "score" : 60.06045071030959 },
{ "type" : "quiz", "score" : 52.79790691903873 },
{ "type" : "homework", "score" : 71.761334391544 }
] }
{"_id" : 2, "name" : "Corliss Zuk", "scores" :
[
{ "type" : "exam", "score" : 67.03077096065002 },
{ "type" : "quiz", "score" : 6.301851677835235 },
{ "type" : "homework", "score" : 20.18160621941858}
] }
Now i want the sum of all the scores of each type for respective students
for example for student aimee zank i want the sum of scores for exam+quiz+homework.
I have tried this
db.collection.aggregate(
[
{
$group:
{
_id: "$name",
total: { $sum: "$scores.score" },
}
}
]
)
and this
db.scores.aggregate(
[
{ $project: { name: 1, total: { $add: [ "$scores.score" ] } } }
]
)
But i could not find a solution
Can someone please help me with the query?

After finding no help on stackoverflow and only discouraging people in the group, i have found a solution on my own and it is just one part of the solution of i was searching for:
db.scores.aggregate(
[
{ $unwind : "$scores"},
{ $group:
{
_id: "$name",
total: { $sum: "$scores.score" }
}
}
]
)

Related

MongoDB - Having difficulty further sorting the query results

This is an example of the collection I am working with
> db.grades.find().limit(5).forEach(printjson)
{
"_id" : ObjectId("50b59cd75bed76f46522c353"),
"student_id" : 0,
"class_id" : 30,
"scores" : [
{
"type" : "exam",
"score" : 14.34345947841966
},
{
"type" : "quiz",
"score" : 47.65945482174327
},
{
"type" : "homework",
"score" : 83.42772189120254
},
{
"type" : "homework",
"score" : 49.86812935368258
},
{
"type" : "homework",
"score" : 39.85525554437086
}
]
}
{
"_id" : ObjectId("50b59cd75bed76f46522c356"),
"student_id" : 0,
"class_id" : 27,
"scores" : [
{
"type" : "exam",
"score" : 60.19473636151568
},
{
"type" : "quiz",
"score" : 64.15966210014162
},
{
"type" : "homework",
"score" : 82.80835343023551
}
]
}
{
"_id" : ObjectId("50b59cd75bed76f46522c350"),
"student_id" : 0,
"class_id" : 5,
"scores" : [
{
"type" : "exam",
"score" : 88.22950674232497
},
{
"type" : "quiz",
"score" : 79.28962650427184
},
{
"type" : "homework",
"score" : 18.66254946562674
},
{
"type" : "homework",
"score" : 40.28154176513361
},
{
"type" : "homework",
"score" : 1.23735944117882
},
{
"type" : "homework",
"score" : 88.96101200683958
}
]
}
{
"_id" : ObjectId("50b59cd75bed76f46522c357"),
"student_id" : 0,
"class_id" : 11,
"scores" : [
{
"type" : "exam",
"score" : 58.83297411100884
},
{
"type" : "quiz",
"score" : 49.66835710930263
},
{
"type" : "homework",
"score" : 18.05861540807023
},
{
"type" : "homework",
"score" : 80.04086698967356
}
]
}
{
"_id" : ObjectId("50b59cd75bed76f46522c358"),
"student_id" : 0,
"class_id" : 10,
"scores" : [
{
"type" : "exam",
"score" : 30.93065784731665
},
{
"type" : "quiz",
"score" : 55.98003281528393
},
{
"type" : "homework",
"score" : 55.6752702814148
},
{
"type" : "homework",
"score" : 63.15391302252755
}
]
}
What I'm trying to achieve, is to get the highest score of the exam, where the student id is 5. I've been stuck on this for quite a while, and the furthest I've managed to come is to retrieve all of the student's exam scores, however I haven't managed to make it so only the highest scoring one displays. This is another aspect I'm stuck on.
This is the code for this output:
{
"student_id" : 5,
"class_id" : 18,
"scores" : [
{
"type" : "exam",
"score" : 73.04238861317688
}
]
}
{
"student_id" : 5,
"class_id" : 8,
"scores" : [
{
"type" : "exam",
"score" : 22.38732080941065
}
]
}
{
"student_id" : 5,
"class_id" : 0,
"scores" : [
{
"type" : "exam",
"score" : 43.64758440439862
}
]
}
{
"student_id" : 5,
"class_id" : 16,
"scores" : [
{
"type" : "exam",
"score" : 33.39752665396672
}
]
}
{
"student_id" : 5,
"class_id" : 30,
"scores" : [
{
"type" : "exam",
"score" : 73.48459944869943
}
]
}
{
"student_id" : 5,
"class_id" : 19,
"scores" : [
{
"type" : "exam",
"score" : 15.36563152024366
}
]
}
{
"student_id" : 5,
"class_id" : 23,
"scores" : [
{
"type" : "exam",
"score" : 21.58296008740177
}
]
}
The code that gets me this is as follows:
var pipeline = [
{ $match: {student_id: 5} },
{ $unwind: "$scores" },
{ $group: {
_id: "$_id",
"student_id": { "$first": "$student_id" },
"class_id": { "$first": "$class_id" },
scores: { $push: "$scores" } } },
{ $project: { _id: 0,
student_id: 1,
class_id: 1,
scores: { $slice: [ "$scores", 1] } } } ];
var results = db.grades.aggregate ( pipeline );
results.forEach(printjson)
(Sorry for the poor structuring, I did my best but I'm not used too it on stackoverflow)
I've been coding with MongoDB for about 2 days now, and I'm knowledgeable in it. Even less so with aggregates, but looking at posts and other code this seemed like the way to do it. From my point of view, because I'm already slicing through it, and attempting to sort the score will only result in getting the highest score out of exams, quiz and homeworks, so it's not a guarantee to give me the exam. Unless there's a different way to sort through these individually
Ideally, I'd want the end result to return only one document, where the exam score is the highest:
{
"student_id" : 5,
"class_id" : 30,
"scores" : [
{
"type" : "exam",
"score" : 73.48459944869943
}
]
}
$match student_id condition
$unwind deconstruct scores array
$match type: exam condition
$sort documents by score in descending order
$group by student_id and get first root document
$replaceRoot to replace doc to root
var pipeline = [
{ $match: { student_id: 5 } },
{ $unwind: "$scores" },
{ $match: { "scores.type": "exam" } },
{ $sort: { "scores.score": -1 } },
{
$group: {
_id: "$student_id",
doc: { $first: "$$ROOT" }
}
},
{ $replaceRoot: { newRoot: "$doc" } }
]
Playground

Problems aggregating MongoDB

I am having problems aggregating my Product Document in MongoDB.
My Product Document is:
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T18:25:48.026+01:00"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
},
And my Category Document is:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T18:25:47.196+01:00")
},
My aim is to perform aggregation on the Product Document in order to count the number of items within each Category. So I have the Category "Art", I need to count the products are in the "Art" Category:
My current aggregate:
db.product.aggregate(
{ $unwind : "$categories" },
{
$group : {
"_id" : { "name" : "$name" },
"doc" : { $push : { "category" : "$categories" } },
}
},
{ $unwind : "$doc" },
{
$project : {
"_id" : 0,
"name" : "$name",
"category" : "$doc.category"
}
},
{
$group : {
"_id" : "$category",
"name": { "$first": "$name" },
"items_in_cat" : { $sum : 1 }
}
},
{ "$sort" : { "items_in_cat" : -1 } },
)
Which does actually work but not as I need:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : null, // Why is the name of the category no here?
"items_in_cat" : 4
},
As we can see the name is null. How can I aggregate the output to be:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : "Art",
"items_in_cat" : 4
},
We need to use $lookup to fetch the name from Category collection.
The following query can get us the expected output:
db.product.aggregate([
{
$unwind:"$categories"
},
{
$group:{
"_id":"$categories",
"items_in_cat":{
$sum:1
}
}
},
{
$lookup:{
"from":"category",
"let":{
"id":"$_id"
},
"pipeline":[
{
$match:{
$expr:{
$eq:["$_id","$$id"]
}
}
},
{
$project:{
"_id":0,
"name":1
}
}
],
"as":"categoryLookup"
}
},
{
$unwind:{
"path":"$categoryLookup",
"preserveNullAndEmptyArrays":true
}
},
{
$project:{
"_id":1,
"name":{
$ifNull:["$categoryLookup.name","NA"]
},
"items_in_cat":1
}
}
]).pretty()
Data set:
Collection: product
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T17:25:48.026Z"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
}
Collection: category
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Craft",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
Output:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"items_in_cat" : 1,
"name" : "Craft"
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"items_in_cat" : 1,
"name" : "Art"
}

Find sum of fields inside array in MongoDB

I have a data as follows:
> db.PQRCorp.find().pretty()
{
"_id" : 0,
"name" : "Ancy",
"results" : [
{
"evaluation" : "term1",
"score" : 1.463179736705023
},
{
"evaluation" : "term2",
"score" : 11.78273309957772
},
{
"evaluation" : "term3",
"score" : 6.676176060654615
}
]
}
{
"_id" : 1,
"name" : "Mark",
"results" : [
{
"evaluation" : "term1",
"score" : 5.89772766299929
},
{
"evaluation" : "term2",
"score" : 12.7726680028769
},
{
"evaluation" : "term3",
"score" : 2.78092882672992
}
]
}
{
"_id" : 2,
"name" : "Jeff",
"results" : [
{
"evaluation" : "term1",
"score" : 36.78917882992872
},
{
"evaluation" : "term2",
"score" : 2.883687879200287
},
{
"evaluation" : "term3",
"score" : 9.882668212003763
}
]
}
What I want to achieve is ::Find employees who failed in aggregate (term1 + term2 + term3)
What I am doing and eventually getting is:
db.PQRCorp.aggregate([
{$unwind:"$results"},
{ $group: {_id: "$id",
'totalTermScore':{ $sum:"$results.score" }
}
}])
OUTPUT:{ "_id" : null, "totalTermScore" : 90.92894831067625 }
Simply I am getting a output of a flat sum of all scores. What I want is, to sum terms 1 , 2 and 3 separately for separate employees.
Please can someone help me. I am new to MongoDB (quite evident though).
You do not need to use $unwind and $group here... A simple $project query can $sum your entire score...
db.PQRCorp.aggregate([
{ "$project": {
"name": 1,
"totalTermScore": {
"$sum": "$results.score"
}
}}
])

How to find minimum value in aggregate $group

I have below collection structure and I want to find minimum score for each student.
>db.students.findOne()
{
"_id" : 0,
"name" : "aimee Zank",
"scores" : [
{
"type" : "exam",
"score" : 1.463179736705023
},
{
"type" : "quiz",
"score" : 11.78273309957772
},
{
"type" : "homework",
"score" : 6.676176060654615
},
{
"type" : "homework",
"score" : 35.8740349954354
}
]
}
I use below aggregate command
db.students.aggregate([
{
$group: {_id: "$_id" , min: {$min: '$scores.score'}}
}
])
below is the output:
{ "_id" : 199, "min" : [ 82.11742562118049, 49.61295450928224, 28.86823689842918, 5.861613903793295 ] }
{ "_id" : 198, "min" : [ 11.9075674046519, 20.51879961777022, 55.85952928204192, 64.85650354990375 ] }
{ "_id" : 95, "min" : [ 8.58858127638702, 88.40377630359677, 25.71387474240768, 23.73786528217532 ] }
{ "_id" : 11, "min" : [ 78.42617835651868, 82.58372817930675, 87.49924733328717, 15.81264595052612 ] }
{ "_id" : 94, "min" : [ 6.867644836612586, 63.4908039680606, 85.41865347441522, 26.82623527074511 ] }
it returns all scores for each student instead of the minimum one. What wrong with my query command? I am using mongo 3.4.
After some searching, I found that the solution is to add $unwind on scores.score. The complete command is:
stus = db.students.aggregate([
{
"$unwind": "$scores"
},
{
$group: {_id: "$_id" , minScore: {$min: '$scores.score'}}
}
])

Finding element in embedded array in MongoDB

I have a collection students look like this:
{
"_id" : 10,
"name" : "Christiano Ronaldo",
"scores" : [
{
"type" : "exam",
"score" : 40.58945534169687
},
{
"type" : "quiz",
"score" : 4.30461571152303
},
{
"type" : "homework",
"score" : 62.36309025722009
},
{
"type" : "homework",
"score" : 32.1707802903173
}
]
}
How do I find out the lowest homework? Using javadriver
Note : I can not change the Data model.
Try this below -
x = db.students.aggregate([{
"$unwind": "$scores"
}, {
"$match": {
"scores.type": "homework"
}
}, {
"$group": {
"_id": "$_id",
"minscore": {
"$min": "$scores.score"
}
}
}])
Your result document "x" contains an array field - lowest scores in the document