I am trying to group by user and email and only output the subtotal > 1. I tried this but it is failed to compile.
db.member.aggregate(
{"$group" : {
_id : {user:"$user", email: "$email"},
count : { $sum : { if: { $gte: [ "$sum", 1 ] }, then: 1, else: 0 }
} } } )
You don't have to try and fit everything into a single $group stage. It's an aggregation "pipeline" and should be used as such. Just at a $match at the end:
db.member.aggregate([
{ "$group": {
"_id": { "user": "$user", "email": "$email" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gte": 1 } } }
])
It's basically required anyway, since "first" you accumulate, and then you filter. Much like GROUP BY and HAVING in SQL.
Also see SQL To Aggregation Mapping Chart in the core documentation for common examples.
Related
I am trying to find the products whose price is above the average price.
I know how to get the average:
db.products.aggregate([{
"$group": {
"_id": null,
"average": { "$avg": "$price" }
}
},
{ $project : { _id : 0 } } ])
But how can I use it in a $gt clause?
For instance, I tried to save the result in a variable:
var averageValue =
db.products.aggregate([{
"$group": {
"_id": null,
"average": { "$avg": "$price" }
}
},
{ $project : { _id : 0 } } ])
And then use it in the $gt clause:
db.products.find({ "price": { "$gt": averageValue} })
However, it does not seem to print me anything.
I am also wondering if this is possible to be done in a single query.
If you use MongoDB version 5.0, you can use $setWindowFields to perform the average for all documents in the collection and add the field with result to each document.
Performs operations on a specified span of documents in a collection, known as a window, and returns the results based on the chosen window operator.
db.products.aggregate([
{
"$setWindowFields": {
"output": {
"average": {
"$avg": "$price"
}
}
}
},
{
$match: {
$expr: {
$gt: [
"$price",
"$average"
]
}
}
}
])
Sample Mongo Playground
I am trying to get the sum of field 'score.number' based on the type of a column value work.type in MongoDB. It should fetch sum as 25 for 'hw' ,and 'cw' as 5 as a single output for the student 'A'. Is there a way to achieve it using mongodb queries ? I tried the $group as well but it doesn't seem to fetch the worktype and the sum for each worktype against it for a single student record 'A'.
Expected Output:
after $match you should use $group like this
db.collection.aggregate([
{
$match: {
student: {
$in: [
"A"
]
},
"work.type": {
$in: [
"hw",
"cw"
]
}
}
},
{
"$group": {
"_id": {
"worktype": "$work.type",
"student": "$student"
},
"workScore": {
"$sum": "$score.number"
}
}
}
])
https://mongoplayground.net/p/qzghM5KoAbp
Able to get the sum with these two
$match{
'student': {'$in': ['A']},
"work.type": {'$in': ['hw', 'cw']},
}
followed by
$group
{
_id: '$work.type',
totalAmount: { $sum: "$score.number" },
}
$match {'student': {
$in: [
"A"
]
},
"work.type": {
$in: [
"hw",
"cw"
]
}}
followed by
$group {
"_id": {
"worktype": "$work.type",
"student": "$student"
},
"workScore": {
"$sum": "$score.number"
}
}
followed by
$group {"_id": {
"student": "$_id.student"
},
'list': {'$push': {'worktype':"$_id.worktype", 'workScore': "$workScore" }},
}
Solved output:
Solves the issue.
So i have this json file:
{"_id":190,"name":"Adrien Renda","scores":[{"score":64.16109192679477,"type":"exam"},{"score":66.93730600935531,"type":"quiz"},{"score":96.0560340227047,"type":"homework"}]}
{"_id":191,"name":"Efrain Claw","scores":[{"score":94.67153825229884,"type":"exam"},{"score":82.30087932110595,"type":"quiz"},{"score":75.86075840047938,"type":"homework"}]}
{"_id":192,"name":"Len Treiber","scores":[{"score":39.19832917406515,"type":"exam"},{"score":98.71679252899352,"type":"quiz"},{"score":44.8228929481132,"type":"homework"}]}
{"_id":193,"name":"Mariela Sherer","scores":[{"score":47.67196715489599,"type":"exam"},{"score":41.55743490493954,"type":"quiz"},{"score":70.4612811769744,"type":"homework"}]}
{"_id":194,"name":"Echo Pippins","scores":[{"score":18.09013691507853,"type":"exam"},{"score":35.00306967250408,"type":"quiz"},{"score":80.17965154316731,"type":"homework"}]}
{"_id":195,"name":"Linnie Weigel","scores":[{"score":52.44578368517977,"type":"exam"},{"score":90.7775054046383,"type":"quiz"},{"score":11.75008382913026,"type":"homework"}]}
{"_id":196,"name":"Santiago Dollins","scores":[{"score":52.04052571137036,"type":"exam"},{"score":33.63300076481705,"type":"quiz"},{"score":78.79257377604428,"type":"homework"}]}
{"_id":197,"name":"Tonisha Games","scores":[{"score":38.51269589995049,"type":"exam"},{"score":31.16287577231703,"type":"quiz"},{"score":79.15856355963004,"type":"homework"}]}
{"_id":198,"name":"Timothy Harrod","scores":[{"score":11.9075674046519,"type":"exam"},{"score":20.51879961777022,"type":"quiz"},{"score":64.85650354990375,"type":"homework"}]}
{"_id":199,"name":"Rae Kohout","scores":[{"score":82.11742562118049,"type":"exam"},{"score":49.61295450928224,"type":"quiz"},{"score":28.86823689842918,"type":"homework"}]}
in a mongodb collection. And i'm trying to read the maximum and minimum score of the last 5 students and display them. I'm using mongolite in r studio and i've tried this:
res2 = con$aggregate(
'[{"$group":{"_id": "$_id", "MaxScore": {"$max": "$scores.score"}, "MinScore":{"$min":"$scores.score"}}},
{ "$sort" : { "_id" : -1} },
{"$limit": 5}
]'
)
The sorting and limit work just fine but the scores come out wrong. I'm guessing because they're embedded documents but i have no idea how to fix it.
This is the end result of the above command
You don't need to perform $group query to calculate $max / $min scores, you can calculate them during $project stage
db.collection.aggregate([
{
"$project": {
"_id": 1,
"MaxScore": {
"$max": "$scores.score"
},
"MinScore": {
"$min": "$scores.score"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$limit": 5
}
])
MongoPlayground
If you want $group code working, just add before $group stage $unwind operator like below:
db.collection.aggregate([
{
$unwind: "$scores"
},
{
$group: {
_id: "$_id",
MaxScore: {
$max: "$scores.score"
},
MinScore: {
$min: "$scores.score"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$limit": 5
}
])
MongoPlayground
If I create a collection like so:
db.People.insert({"Name": "John"})
and run a simple mongo aggregate, like so:
db.People.aggregate([{$match: {Name: "John"}}, {$group: {_id: "null", count: {$sum: 1}}}])
This counts all the Johns in the collection and returns this
{ "_id" : "null", "count" : 1 }
Which is nice. But if I search for the name "Clarice" that does not exist at all, it returns null.
I would like it to return
{ "_id" : "null", "count" : 0 }
I have not found a way to achieve this. I would have to include some kind of null-check between the $match- and $group-stage.
Have have to use $facet aggregation along with the operator $ifNull. e.g:
db.People.aggregate([
{ "$facet": {
"array": [
{ "$match": { Name:"John" }},
{ "$group": {
"_id": null,
"count": { "$sum": 1 }
}},
{ "$project": { "_id": 0, "count": 1 }}
]
}},
{ "$project": {
"count": {
"$ifNull": [{ "$arrayElemAt": ["$array.count", 0] }, 0 ]
}
}}
])
Output:
{ "count" : 1 }
For other name, it should be as follow:
{ "count" : 0 }
Similar ans at $addFields when no $match found
Simply use count
db. People.count({Name:"John"})
This will return the exact number.
Otherwise You need to check the result wether it is a empty array. Below are the code for node using loopback,
db.People.aggregate([
{$match: {Name: "John"}},
{$group: {_id: "null", count: {$sum: 1}}}
],(err,res)=>{
if(err) return cb(err)
if(res.length) return cb(err,res)
else return cb(err,{_id:null,count:0})
})
You can use $ifNull in your $match stage.
If you can provide an collecion of examples it's more easy to elaborare an answer on it.
Edit: if you group by Name, result for "John" is one, for "Clarice" is an empty array that is correct, here the aggregation query:
db.People.aggregate([
{
$match: { Name: "John" }
},
{
$group: { _id: "$Name", count: { $sum: 1 } }
}
])
I'm doing the course of MongoDB and I'm on the first exercise of week 5. The first exercise consists on getting the author who has more comments.
The first thing I did was check how looks the data and after that I started writing the query and that's what I got:
db.posts.aggregatae([
{ $unwind: "$comments" },
{ $group:
{
_id: "$author",
num_posts:{ $sum:1 }
}
},
{ $sort:
{ "num_posts": -1 }
}
]);
The query works and counts the num of comments correctly but when I try to sort the results it didn't work. I tried to change the $group stage to this:
{ $group:
{ _id: "$author" },
num_posts:{ $sum:1 }
}
But I get the error:
Error: command failed: {
"errmsg" : "exception": A pipeline state specification object must contain exactly
one field.", "code" : 16435, "ok" : 0
The problem with your query is you are grouping by a non-existing key, you need to group by the comments' author key to get the author (from the embedded comments subdocuments array) with the most number of comments as follows:
db.posts.aggregate([
{ "$unwind": "$comments"},
{
"$group": {
"_id": "$comments.author",
"num_posts": { "$sum": 1 }
}
},
{
"$sort": { "num_posts": -1 }
},
{ "$limit": 1 }
]);