Aggregate function isn't showing results with a count greater than 1 - mongodb

I'm running this aggregate function, which is supposed to only show results when they have a count greater than 1. When I remove 'count': { '$gt': 1 } the aggregate works, however it obviously shows all results. How should I use this count correctly?
db.getCollection('songs').aggregate([
{
'$match': { 'is_song': 1, 'is_soundtrack': 0, 'count': { '$gt': 1 } }
},
{
'$group': { '_id': { 'name': '$name', 'artist_id': '$artist_id' }, 'count': { '$sum': 1 } }
},
{
'$sort': { 'count': -1 }
}
])
Sample data:
{
"_id" : ObjectId("5f93a43b4e8883298849ad18"),
"name" : "Come Fly With Me",
"song_id" : 5,
"artist_id" : 5,
"is_song" : 1,
"is_soundtrack" : 0,
"updatedAt" : ISODate("2016-10-04T13:34:53.328Z")
}

You should not add 'count': { '$gt': 1 } in the first $match stage.
As the count field is only populated after the $group stage.
So, you need add another $match stage after $group stage for filtering document with the count value is greater than 1.
db.collection.aggregate([
{
"$match": {
"is_song": 1,
"is_soundtrack": 0
}
},
{
"$group": {
"_id": {
"name": "$name",
"artist_id": "$artist_id"
},
"count": {
"$sum": 1
}
}
},
{
$match: {
"count": {
"$gt": 1
}
}
},
{
"$sort": {
"count": -1
}
}
])
Sample Mongo Playground

Related

How to find max and min value from embedded documents in 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

Adding up values from array elements in MongoDB

I have done some aggregation to arrive at the below document structure for my given data:
{
"_id" : "test",
"NoOfQuestions" : 3.0,
"info" : [
{
"AnswerrCount" : 3
},
{
"AnswerrCount" : 3
},
{
"AnswerrCount" : 2
}
]
}
However, I am trying to add up all the values in the AnswerrCount column. So from the above example, I want another column that says TotalAnswers:8, (3+3+2) and then eventually have a from using the NoOfQuestions, FinalTotal:11, (8+3)
You can use $sum aggregation to add array values
db.collection.aggregate([
{ "$addFields": {
"TotalAnswers": {
"$sum": "$info.AnswerrCount"
},
"FinalTotal": {
"$add": [{ "$sum": "$info.AnswerrCount" }, "$NoOfQuestions"]
}
}}
])
db.collection.aggregate([{
$unwind: "$info"
}, {
$group: {
_id: null,
TotalAnswers: {
$sum: '$info.AnswerrCount'
},
doc: {
$first: '$$CURRENT'
}
}
}, {
$project: {
TotalAnswers: 1,
FinalTotal: {
'$add': ['$TotalAnswers', '$doc.NoOfQuestions']
},
_id: 0
}
}])

How do I flatten the results of an aggregation?

I have the following query...
db.getCollection('apprenticeships')
.aggregate([
{
$match: {
'Vacancy._id': { $in: [1, 2, 3] },
}
},
{
$group: {
'_id': {
'VacancyId': '$Vacancy._id',
'Status': '$Status'
},
'Count': { $sum: 1 }
}
},
{
$sort: {
'_id.VacancyId': 1,
'_id.Status': 1
}
}
])
Which gives results where each element has following structure
{
"_id" : {
"VacancyId" : 1,
"Status" : 90
},
"Count" : 40.0
}
How can I remap that structure so that the elements in the output look like this instead?
{
"VacancyId": 1,
"Status": 90,
"Count": 40
}
You can add $project stage to aggregation pipeline to add new fields VacancyId and status and then hide the _id
db.getCollection('apprenticeships')
.aggregate([{
$match: {
'Vacancy._id': {
$in: [1, 2, 3]
},
}
},
{
$group: {
'_id': {
'VacancyId': '$Vacancy._id',
'Status': '$Status'
},
'Count': {
$sum: 1
}
}
},
{
$sort: {
'_id.VacancyId': 1,
'_id.Status': 1
}
},
{
{
$project:{ 'VacancyId': '$_id.VacancyId', 'Status': '$_id.Status', 'Count': '$Count', '_id': 0 }
}
}
])

Return 5 elements for for each type with aggregation

How do I create an aggregate operation that shows me 5 for each type?
For example, what I need is to show 5 of type= 1 , 5 of type=2 and 5 of type=3.
I have tried:
db.items.aggregate([
{$match : { "type" : { $gte:1,$lte:3 }}},
{$project: { "type": 1, "subtipo": 1, "dateupdate": 1, "latide": 1, "long": 1, "view": 1,month: { $month: "$dateupdate" } }},
{$sort:{view: -1, dateupdate: -1}},
{$limit:5}
]);
After the $match pipeline, you need to do an initial group which creates an array of the original documents. After that you can $slice the array with the documents to return the 5 elements.
The intuition can be followed in this example:
db.items.aggregate([
{ '$match' : { 'type': { '$gte': 1, '$lte': 3 } } },
{
'$group': {
'_id': '$type',
'docs': { '$push': '$$ROOT' },
}
},
{
'$project': {
'five_docs': {
'$slice': ['$docs', 5]
}
}
}
])
The above will return the 5 documents unsorted in an array. If you need to return the TOP 5 documents in sorted order then you can introduce a $sort pipeline before grouping the docs that re-orders the documents getting into the $group pipeline by the type and dateupdate fields:
db.items.aggregate([
{ '$match' : { 'type': { '$gte': 1, '$lte': 3 } } },
{ '$sort': { 'type': 1, 'dateupdate': -1 } }, // <-- re-order here
{
'$group': {
'_id': '$type',
'docs': { '$push': '$$ROOT' },
}
},
{
'$project': {
'top_five': {
'$slice': ['$docs', 5]
}
}
}
])

MongoDB Get average of group considering rank of document

I have documents getting in order like:
{
"_id": "abcde1",
"value" : 300
},
{
"_id": "abcde2",
"value" : 200
},
{
"_id": "abcde3",
"value" : 400
},
{
"_id": "abcde4",
"value" : 500
},
{
"_id": "abcde5",
"value" : 600
}
i.e,
I want average of "_id" of first 2, first 4 and all 5 documents matching like in single query:
{
"value_2" : 250, // Average of first 2 documents
"value_4" : 350, // Average of first four documents
"value_5" : 400 // Average of all 5 documents
}
Is it possible to Group documents based on rank of document.
I can do 3 results in 3 separate queries. Is it possible in single query?
You could try running the following pipeline:
db.collection.aggregate([
// previous pipeline here
{
"$group": {
"_id": null,
"values": { "$push": "$value" }
}
},
{ "$unwind": { "path": "$values", "includeArrayIndex": "rank" } },
{
"$group": {
"_id": null,
"value_2_sum": {
"$sum": {
"$cond": [
{ "$lt": ["$rank", 2] },
"$values",
0
]
}
},
"value_2_count": {
"$sum": {
"$cond": [
{ "$lt": ["$rank", 2] },
1,
0
]
}
},
"value_4_sum": {
"$sum": {
"$cond": [
{ "$lt": ["$rank", 4] },
"$values",
0
]
}
},
"value_4_count": {
"$sum": {
"$cond": [
{ "$lt": ["$rank", 4] },
1,
0
]
}
},
"value_5": { "$avg": "$values" }
}
},
{
"$project": {
"value_2" : { "$divide": ["$value_2_sum", "$value_2_count"] }, // Average of first 2 documents
"value_4" : { "$divide": ["$value_4_sum", "$value_4_count"] }, // Average of first four documents
"value_5" : 1
}
}
])
You could use a $facet aggregation stage:
// { _id: "abcde1", value: 300 }
// { _id: "abcde2", value: 200 }
// { _id: "abcde3", value: 400 }
// { _id: "abcde4", value: 500 }
// { _id: "abcde5", value: 600 }
db.collection.aggregate([
{ $facet: {
value_2: [ { $limit: 2 }, { $group: { _id: null, value_2: { $avg: "$value" } } } ],
value_4: [ { $limit: 4 }, { $group: { _id: null, value_4: { $avg: "$value" } } } ],
value_5: [ { $limit: 5 }, { $group: { _id: null, value_5: { $avg: "$value" } } } ]
}},
// {
// value_2: [ { _id: null, value_2: 250 } ],
// value_4: [ { _id: null, value_4: 350 } ],
// value_5: [ { _id: null, value_5: 400 } ]
// }
{ $set: {
value_2: { $first: "$value_2.value_2" },
value_4: { $first: "$value_4.value_4" },
value_5: { $first: "$value_5.value_5" }
}}
])
// { "value_2" : 250, "value_4" : 350, "value_5" : 400 }
The $facet stage allows us to run multiple aggregation pipelines within a single stage on the same set of input documents. Each sub-pipeline has its own field in the output document where its results are stored as an array of documents.
Each field is thus produced by its own aggregation pipeline whose first stage is a simple $limit, followed by a $group stage that'll produce the $avg (average) of all considered documents.
The second part of the pipeline (the $set stage) is just there to clean-up the $facet output to the format you wished for.