How to find max and min value from embedded documents in Mongodb - mongodb

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

Related

MongoDB Select Values above Average

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

Fetching sum of rows for a type of column value in mongodb as a single output

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.

summation of two columns in Aggregate Method

I am using mongodb Aggregate query. My db is like this:
{
"_id" : ObjectId("5a81636f017e441d609283cc"),
"userid": "123",
page : 'A',
newpage: 'A',
},
{
"_id" : ObjectId("5a81636f017e441d609283cd"),
"userid": "123",
page : 'B',
newpage: 'A',
},
{
"_id" : ObjectId("5a81636f017e441d609283ce"),
"userid": "123",
page : 'D',
newpage: 'D',
}
I want to get the Sum of all page and new page value. I am able to get one column value which can give the very precise result.
But I am stuck with the two columns. What I did for getting the sum/repetition of values for one column is:
db.Collection.aggregate([
{$match:{ "userid":"123"}},
{$unwind:"$newpage"},
{$group:{"_id":"$newpage", "count":{"$sum":1}}},
{$project: {_id:0, pagename :"$_id", count:{ $multiply: [ "$count", 1 ] }}},
{$sort: {count: -1}},
//{$limit: 10}
], function(error, data){
if (error) {
console.log(error);
} else {
console.log(data);
}
});
Desired Result will be like:
{
"pagename": "A",
"count": 3
},
{
"pagename": "D",
"count": 2
},
{
"pagename": "B",
"count": 1
}
Is anyone has any approach to getting these things for Two Column? Any Help is appreciated
Use $facet pipeline stage to process multiple aggregation pipelines within a single stage on the same set of input documents. In your case you need to aggregate the counts separately then join the two results and calculate the final aggregates.
This can be demonstrated by running the following pipeline:
db.collection.aggregate([
{ "$match": { "userid": "123" } },
{
"$facet": {
"groupByPage": [
{ "$unwind": "$page" },
{
"$group": {
"_id": "$page",
"count": { "$sum": 1 }
}
}
],
"groupByNewPage": [
{ "$unwind": "$newpage" },
{
"$group": {
"_id": "$newpage",
"count": { "$sum": 1 }
}
}
]
}
},
{
"$project": {
"pages": {
"$concatArrays": ["$groupByPage", "$groupByNewPage"]
}
}
},
{ "$unwind": "$pages" },
{
"$group": {
"_id": "$pages._id",
"count": { "$sum": "$pages.count" }
}
},
{ "$sort": { "count": -1 } }
], function(error, data){
if (error) {
console.log(error);
} else {
console.log(data);
}
)
There you go:
db.Collection.aggregate([
{$match:{ "userid":"123"}}, // filter out what's not of interest
{$facet: { // process two stages in parallel --> this will give us a single result document with the following two fields
"newpage": [ // "newpage" holding the ids and sums per "newpage" field
{$group:{"_id":"$newpage", "count":{"$sum":1}}}
],
"page": [ // and "page" holding the ids and sums per "page" field
{$group:{"_id":"$page", "count":{"$sum":1}}}
]
}},
{$project: {x:{$concatArrays:["$newpage", "$page"]}}}, // merge the two arrays into one
{$unwind: "$x"}, // flatten the single result document into multiple ones so we do not need to $reduce but can nicely $group
{$group: {_id: "$x._id", "count": {$sum: "$x.count"}}} // perform the final grouping/counting,
{$sort: {count: -1}} // well, the sort according to your question
]);

Using Sum with Last mongodb

UseCase: I have the following data:
{"accountNumber":"1-1", "details":["version":{ "number": "1","accountGroup":"1", "editable":"false" , "amount":100 }]}
{"accountNumber":"1-2", "details":[version":{ "number": "2", "accountGroup":"1", "editable":"false" , "amount":200}]}
{"accountNumber":"2-1", "details":[version":{ "number": "1", "accountGroup":"2", "editable":"false", "amount":200 }]}
Where: my document is account. Each record has a accountGroup (1, 2). A group can have multiple versions. AccountNumber is being initialized by the combination of AccountGroup & version
I want to get the latest version of the account (accountNumber 1-2 & 2-1) along with the sum of their amount.
Expected output:
{accountNumber:2-1}, {accountNumber: 1-2}, total: 400 (sum of amount of the latest versions of the account group)
I am using the following query:
db.getCollection('account').aggregate([
{ "$sort": { "accountNumber": 1 } },
{ "$unwind": "$details"},
{ "$group": {
"_id": "$details.version.accountGroup",
"Latestversion": { "$last": "$$ROOT" },
"total": {
$sum: "$details.version.amount"
}
}
}])
It gets the sum of the all the versions which belongs to a group.
Current output:
{"accountNumber": "1-2", total: 300}, {"accountNumber":"2-1", total: 200}
I am new to Mongodb, any help is appreciated. Looking forward for a response.
You will need two $group stages.
First $group to find the latest document for each account group and second $group to sum amount from latest document.
Something like
aggregate([
{ "$sort": { "accountNumber": 1 } },
{ "$unwind": "$details"},
{ "$group": {
"_id": "$details.version.accountGroup",
"latest": { "$last": "$$ROOT" }
}
},
{ "$group": {
"_id": null,
"accountNumbers": { $push:"$latest.accountNumber" },
"total": { $sum: "$latest.details.version.amount" }
}
}
])
You can update your structure to below and remove $unwind.
{"accountNumber":"1-1", detail:{"number": "1","accountGroup":"1", "editable":"false" , "amount":100 }}

How to return count:0 in an aggregation if there is no match

I am having an issues that I thought it would happen often, but I wasn't able to find enough information during my research.
My problem is that I expect the return of a query to have a given JSON format, but when the match filters out all documents, I get no json.
A simplified example: I would like to have the count if documents that match a given criteria, so I have the following query
db.collection.aggregate( [{
$match: {
type: /^1[.]2[.]3[.].*$/
}
}, {
$group: {
_id: {$ifNull : ["$type", 0]},
count: { $sum: 1 }
}
}]);
If I have at least one document that matches, then the query works:
{ "_id" : "1.2.3", "count" : 44 }
If I have no documents, I would like to receive a json like this:
{ "_id" : "1.5.3", "count" : 0 }
Is this possible?
ps: this is a simplified case, it would not be so easy to handle that on the application side, so I would rather try to adjust my query
If you can know beforehand the value of the key that you are searching for(i.e. 1.2.3, 1.5.3 in your case), here is a workaround using $facet. It first tries to get the documents by $match and store them into an array named results. Depending on the $size of the results array, we either replace it with the $group result (when we have matched records); or replace it with a default count: 0 record with the key you specified.
db.collection.aggregate([
{
"$facet": {
"results": [
{
$match: {
"type": <key you want to search>
}
},
{
$group: {
_id: {
$ifNull: [
"$type",
0
]
},
count: {
$sum: 1
}
}
}
]
}
},
{
"$replaceRoot": {
"newRoot": {
"$cond": {
"if": {
$gt: [
{
"$size": "$results"
},
0
]
},
"then": "$$ROOT",
"else": {
"results": [
{
"_id": <key you want to search>,
"count": 0
}
]
}
}
}
}
},
{
"$unwind": "$results"
},
{
"$replaceRoot": {
"newRoot": "$results"
}
}
])
Mongo Playground