How to aggregate common values in Mongo? - mongodb

I have following document :
{ asset : {
assetName : abc,
attributes : {
Artist : XYZ,
Duration : 10
}
}
}
{ asset : {
assetName : pqr,
attributes : {
Artist : EFG,
Duration : 5
}
}
}
{ asset : {
assetName : HIJ,
attributes : {
Artist : XYZ,
Duration : 10
}
}
}
I want to collect retrieve data result as follows :
{ result : {
Artist: {
values : [XYZ, EFG],
freq : [2, 1]
}
Duration : {
values : [10, 5],
freq : [2, 1]
}
}
Result should be sorted based upon the frequency.
Will be querying using mongoose package of node.

You can use aggregation
db.collectionName.aggregate(
[
{ "$group": {
"_id":null,
"Artist": { "$push":"$asset.attributes.Artist" },
"Duration": { "$push":"$asset.attributes.Duration" }
}
},
{ "$project": { "_id":0,"Artist":1,"Duration": 1 }}
]
)
Edit
As per new edited question you can do following aggregation to get the result
db.collectionName.aggregate({
"$group": {
"_id": null,
"data": {
"$push": {
"artist": "$asset.attributes.Artist",
"duration": "$asset.attributes.Duration"
}
}
}
}, {
"$unwind": "$data"
}, {
"$group": {
"_id": {
"a": "$data.artist",
"b": "$data.duration"
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": null,
"ArtistValues": {
"$push": "$_id.a"
},
"ArtistFreq": {
"$push": "$count"
},
"DurationValue": {
"$push": "$_id.b"
},
"durationfreq": {
"$push": "$count"
}
}
}, {
"$project": {
"Artist": {
"values": "$ArtistValues",
"freq": "$ArtistFreq"
},
"Duration": {
"values": "$DurationValue",
"freq": "$durationfreq"
},
"_id": 0
}
}).pretty()

Related

Simple MongoDB Aggregation

I'm a bit confused on how to group using aggregation but still be able to extract specific values from arrays:
db.collection.aggregate([
{ "$unwind": f"${stat_type}" },
{
"$group": {
"_id": "$userId",
"value" : { "$max" : f"${stat_type}.stat_value" },
"character" : f"${stat_type}.character_name", <-- how do I extract this value that matches where the $max from above is grabbed.
}
},
{ "$sort": { "value": -1 }},
{ '$limit' : 30 }
])
Sample Entries:
{
'name' : "Tony",
'userId' : 12345,
'damage_dealt' : [
"character_name" : "James",
"stat_value" : 100243
]
}
{
'name' : "Jimmy",
'userId' : 12346,
'damage_dealt' : [
"character_name" : "James",
"stat_value" : 1020243
]
}
{
'name' : "Tony",
'userId' : 12345,
'damage_dealt' : [
"character_name" : "Lebron",
"stat_value" : 99900243
]
}
A sample output for what I'm looking for is below:
[
{
'_id':12345,
'user' : 'Tony'
'character_name' : 'Lebron',
'stat_value' : 99900243
},
{
'_id':12346,
'user' : 'Jimmy'
'character_name' : 'James',
'stat_value' : 1020243
}
]
You can use the $top accumulator to achieve the desired result. Like this:
db.collection.aggregate([
{
"$unwind": "$damage_dealt"
},
{
"$group": {
"_id": "$userId",
"value": {
$top: {
output: {
character_name: "$damage_dealt.character_name",
stat_value: "$damage_dealt.stat_value"
},
sortBy: {
"damage_dealt.stat_value": -1
}
}
},
}
},
{
"$project": {
character_name: "$value.character_name",
stat_value: "$value.stat_value"
}
},
{
"$sort": {
"stat_value": -1
}
},
{
"$limit": 30
}
])
Playground link.
Or collects all the group elements in an array, and the max stat_value, then pick the object from the array containing the max stat_value.
db.collection.aggregate([
{
"$unwind": "$damage_dealt"
},
{
"$group": {
"_id": "$userId",
"max_stat": {
"$max": "$damage_dealt.stat_value"
},
"damages": {
"$push": {
name: "$name",
damage_value: "$damage_dealt"
}
}
}
},
{
"$project": {
"damages": {
"$arrayElemAt": [
{
"$filter": {
"input": "$damages",
"as": "damage",
"cond": {
"$eq": [
"$$damage.damage_value.stat_value",
"$max_stat"
]
}
}
},
0
]
}
}
},
{
"$project": {
"character_name": "$damages.damage_value.character_name",
"stat_value": "$damages.damage_value.stat_value",
"name": "$damages.name"
}
},
{
"$sort": {
"stat_value": -1
}
},
{
"$limit": 30
}
])
Playground link.
Here's another way you could do it.
db.collection.aggregate([
{
"$group": {
"_id": "$userId",
"user": {"$first": "$name"},
"damage_dealts": {"$push": "$damage_dealt"},
"maxStat": {"$max": {"$first": "$damage_dealt.stat_value"}}
}
},
{
"$set": {
"outChar": {
"$first": {
"$arrayElemAt": [
"$damage_dealts",
{"$indexOfArray": ["$damage_dealts.stat_value", "$maxStat"]}
]
}
}
}
},
{
"$project": {
"user": 1,
"character_name": "$outChar.character_name",
"stat_value": "$outChar.stat_value"
}
},
{"$sort": {"stat_value": -1}},
{"$limit": 30}
])
Try it on mongoplayground.net.

MongoDB group with project

I am trying to select maximum value of a field along with the time it occured.
db.temperaterdata.aggregate([
{
$match: {
$and: [
{ "organization_id": 37 },
{
"resulttime":{
$gte:'2020-05-01 00:00',
$lte:'2020-05-29 23:59'
}
}
]
}
},
{
"$group": {
"_id": "$userid",
maxTemperature: { $max: "$temperature"},
minTemperature: { $min: "$temperature"}
}
},
{
"$project": {
"resulttime":1,
maxTemperature:1,
minTemperature:1
}
}
]);
However, the time for which the maximum/minimum temperature has occurred it's not showing up. The result coming is like this:
{ "_id" : 233, "maxTemperature" : 100.67, "minTemperature" : 98.56 }
{ "_id" : 256, "maxTemperature" : 100.67, "minTemperature" : 98.56 }
{ "_id" : 263, "maxTemperature" : 100.67, "minTemperature" : 98.56 }
Any help will be much appreciated.
You need to order by userId + temperature and take max / min documents with $first / $last operators.
Try the query below:
db.temperaterdata.aggregate([
{
"$match": {
"organization_id": 37,
"resulttime": {
"$gte": "2020-05-01 00:00",
"$lte": "2020-05-29 23:59"
}
}
},
{
"$sort": {
"userid": 1,
"temperature": 1
}
},
{
"$group": {
"_id": "$userid",
"maxTemperature": {
"$last": "$$ROOT"
},
"minTemperature": {
"$first": "$$ROOT"
}
}
},
{
"$project": {
"maxresulttime": "$maxTemperature.resulttime",
"minresulttime": "$minTemperature.resulttime",
"maxTemperature": "$maxTemperature.temperature",
"minTemperature": "$minTemperature.temperature"
}
}
])
MongoPlayground

Counting the two value in a attribute using aggregate in mongodb

I have some documents in a collection which looks like this
{
"_id" : "5a2e50b32d43ba00010041e5",
account_id:"23232323"
status:"accepted",
keyname:"java"
},
{
"_id" : "5a2e54332d43ba00010041e5",
account_id:"2323233"
status:"pending",
keyname:"java"
},
{
"_id" : "5a2e54332d43ba00010041e5",
account_id:"23232sdsd3"
status:"pending",
keyname:"Nodejs"
}
I need to get the counts of the pending and accepted status for each keyname for a particular account_id
eg: should give a result like this.
{
keyname:"java",
pending:10,
accepted:10
}
This is the code that I have tried out
db.getCollection("programs").aggregate([
{ "$match": { "account_id": "1" } },
{ "$group": { "_id": "$keyname", "count": { "$sum": 1 } } },
{ "$match": { "_id": { "$ne": null } } }
])
which gives a result like this
{
"_id" : "java",
"count" : 3.0
},
{
"_id" : "nodejs",
"count" : 3.0
},
{
"_id" : "C#",
"count" : 3.0
}
You can use below aggregation
db.collection.aggregate([
{ "$match": { "account_id": "1" } },
{ "$group": {
"_id": "$keyname",
"accepted": {
"$sum": {
"$cond": [
{ "$eq": ["$status", "accepted"] },
0,
1
]
}
},
"pending": {
"$sum": {
"$cond": [
{ "$eq": ["$status", "pending"] },
0,
1
]
}
}
}}
])

Query to get a value by subtracting a value from current and next document

I have a mongo db collection like below,
{
"id": ObjectId("132456"),
reading :[
{
"weight" : {
"measurement" : 82.0,
"unit" : "kg"
}
}
],
"date" : ISODate("2018-09-12T11:45:08.174Z")
},
{
"id": ObjectId("132457"),
reading :[
{
"weight" : {
"measurement" : 80.0,
"unit" : "kg"
}
}
],
"date" : ISODate("2018-09-12T10:45:08.174Z")
},
{
"id": ObjectId("132458"),
reading :[
{
"weight" : {
"measurement" : 85.0,
"unit" : "kg"
}
}
],
"date" : ISODate("2018-09-11T09:45:08.174Z")
}
I need a mongo db query that will give me the current weight and the weight difference between the current and next record.
Example output below,
{
"id": ObjectId("132456"),
"currentWeight": 75.0,
"weightDifference": 2.0,
"date" : ISODate("2018-09-12T11:45:08.174Z")
},
{
"id": ObjectId("132457"),
"currentWeight": 80.0,
"weightDifference": -5.0,
"date" : ISODate("2018-09-12T10:45:08.174Z")
}
I was not able to get the weight from next document to subtract the weight from current document.
Thanks in advance for your help
My try for the above problem,
db.measurementCollection.aggregate([
{
$match : { "date" : { $gte : new ISODate("2018-09-01T00:00:00.000Z") , $lte : new ISODate("2018-09-12T23:59:59.000Z") } }
},
{
$project : { "date" : 1 ,
"currentWeight" : {$arrayElemAt: [ "$reading.weight.measurement", 0 ]}
},
{ $sort: {"date":-1} },
{
$addFields : {
"weigtDifference" :
{
{
$limit: 2
},
{
$group: {
_id: null,
'count1': {$first: '$currentWeight'},
'count2': {$last: '$currentWeight'}
}
},
{
$subtract: ['$count1', '$count2']
}
}
}
}
])
You can try below aggregation but I will not recommend you to use this with the large data set.
db.collection.aggregate([
{ "$match": {
"date" : {
"$gte": new ISODate("2018-09-01T00:00:00.000Z"),
"$lte": new ISODate("2018-09-12T23:59:59.000Z")
}
}},
{ "$unwind": "$reading" },
{ "$sort": { "date": -1 }},
{ "$group": { "_id": null, "data": { "$push": "$$ROOT" }}},
{ "$project": {
"data": {
"$filter": {
"input": {
"$map": {
"input": { "$range": [0, { "$size": "$data" }] },
"as": "tt",
"in": {
"$let": {
"vars": {
"first": { "$arrayElemAt": ["$data", "$$tt"] },
"second": { "$arrayElemAt": ["$data", { "$add": ["$$tt", 1] }] }
},
"in": {
"currentWeight": "$$first.reading.weight.measurement",
"weightDifference": { "$subtract": ["$$second.reading.weight.measurement", "$$first.reading.weight.measurement"] },
"_id": "$$first._id",
"date": "$$first.date"
}
}
}
}
},
"cond": { "$ne": ["$$this.weightDifference", null] }
}
}
}
},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" }}
])

Sum array in aggregation query

I'm pretty new to MongoDB, and having some problems getting my query as I want it. The documents contain "errors" that have happened a specific time. The result I want from the query is an error count for each month per user. This I have already figured out, but additionally I want the total errorcount per user.
This is what I've got so far:
db.Logger.aggregate([
{ "$group": {
"_id": {
"name": "$name",
"month": { "$month": "$errorTime" }
},
"totalErrors": { "$sum": 1 }
}},
{ $group :
{ _id: { name : "$_id.name"},
errors: { $addToSet: { totalErrors: { errorsThisMonth: "$totalErrors", currentMonth : "$_id.month" } } },
}
}
])
The result is:
{
"_id" : {
"name" : "abhos"
},
"errors" : [
{
"totalErrors" : {
"errorsThisMonth" : 6,
"currentMonth" : 2
}
},
{
"totalErrors" : {
"errorsThisMonth" : 6,
"currentMonth" : 1
}
}
]
},
Will it be possible to get what I want by adding to that query?
All you need is an additional $sum in your second $group:
db.Logger.aggregate([
{ "$group": {
"_id": {
"name": "$name",
"month": { "$month": "$errorTime" }
},
"totalErrors": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.name",
"errors": {
"$addToSet": {
"errorsThisMonth": "$totalErrors",
"currentMonth" : "$_id.month"
}
},
"totalErrors": { "$sum": "$totalErrors" }
}}
])
Also you have a few extra document levels you do not need in there, such as extra fields under the _id and the "errors" "set" produced in the grouping. This output is just a little different without those additional levels:
{
"_id": "abhos"
"errors" : [
{
"errorsThisMonth" : 6,
"currentMonth" : 2
},
{
"errorsThisMonth" : 6,
"currentMonth" : 1
}
],
"totalErrors": 12
},