MongoDB group subobjects with same id and get average of values - mongodb

I have documents like this:
{
"_id" : ObjectId("588e505fcdefc41e84c184cb"),
"Id" : 58614891,
"modifyDate" : 1485567717000,
"data" : [
{
"id" : 99,
"stats" : {
"totalDepth" : 4,
"totalSpeed" : 2,
"totalLostSessions" : 2,
"KDI" : 8,
}
},
{
"id" : 18,
"stats" : {
"totalDepth" : 2,
"totalSpeed" : 1,
"totalLostSessions" : 1,
"KDI" : 2,
}
}
],
"timestampPull" : 1485721695291,
"region" : "eu",
"Status" : 200
}
{
"_id" : ObjectId("588e5060cdefc41e84c184cd"),
"Id" : 38004043,
"modifyDate" : 1485515118000,
"data" : [
{
{
"id" : 18,
"stats" : {
"totalDepth" : 5,
"totalSpeed" : 3,
"totalLostSessions" : 2,
"KDI" : 14,
}
},
{
"id" : 62,
"stats" : {
"totalDepth" : 1,
"totalSpeed" : 0,
"totalLostSessions" : 1,
"KDI" : 1,
}
},
{
"id" : 0,
"stats" : {
"totalDepth" : 155,
"totalSpeed" : 70,
"totalLostSessions" : 85,
"KDI" : 865,
}
}
],
"timestampPull" : 1485721696025,
"region" : "na",
"Status" : 200
}
And i want to calculate average values of every stats if "data" id match.
{
"id" : 99,
"stats" : {
"totalDepth" : 4,
"totalSpeed" : 2,
"totalLostSessions" : 2,
"KDI" : 8,
}
},
{
"id" : 18,
"stats" : {
"totalDepth" : 3.5,
"totalSpeed" : 2,
"totalLostSessions" : 1.5,
"KDI" : 8,
}
} ...
It is possible to perform such operation on mongoDB? I can easily pull every data to application and average it there, but that's not very effective.

You can try below aggregation.
$unwind the data array.
$group by id and calculate the $avg of values and count to $sum the number of values.
$match to keep the data where count is gt than 1.
db.collection.aggregate({
$unwind: "$data"
}, {
$group: {
_id: "$data.id",
count: {
$sum: 1
},
"totalDepth": {
$avg: "$data.stats.totalDepth"
},
"totalSpeed": {
$avg: "$data.stats.totalSpeed"
},
"totalLostSessions": {
$avg: "$data.stats.totalLostSessions"
},
"KDI": {
$avg: "$data.stats.KDI"
}
}
}, {
$match: {
count: {
$gt: 1
}
}
})

Related

Aggregate value of each hour by MongoDB

Like the image, the above table represents my original data, time field is irregular. Now I want to get the data that represents the average value between every hour. What I thought was by using $match, $group, $project even with for method. I don't get an accurate idea and method.
id: ObjectId,
value: Number,
time: Date()
I have sample collection, hours.:
{ "_id" : 1, "value" : 10, "dt" : ISODate("2019-10-17T00:01:32Z") }
{ "_id" : 2, "value" : 16, "dt" : ISODate("2019-10-17T00:02:12Z") }
{ "_id" : 3, "value" : 8, "dt" : ISODate("2019-10-17T01:04:09Z") }
{ "_id" : 4, "value" : 12, "dt" : ISODate("2019-10-17T02:14:21Z") }
{ "_id" : 5, "value" : 6, "dt" : ISODate("2019-10-17T02:54:02Z") }
{ "_id" : 6, "value" : 11, "dt" : ISODate("2019-10-17T04:06:31Z") }
The following aggregation query returns the average value by the hour (the hour is of the date field):
db.hours.aggregate( [
{ $project: { value: 1, hr: { $hour: "$dt" } } } ,
{ $addFields: { hour: { $add: [ "$hr", 1 ] } } },
{ $group: { _id: "$hour",
count: { $sum: 1 },
totalValue: { $sum: "$value" },
avgValue: { $avg: "$value" }
}
},
{ $project: { hour: "$_id", _id: 0, count: 1, totalValue: 1, avgValue: 1} }
] )
=>
{ "count" : 2, "totalValue" : 18, "avgValue" : 9, "hour" : 3 }
{ "count" : 1, "totalValue" : 8, "avgValue" : 8, "hour" : 2 }
{ "count" : 1, "totalValue" : 11, "avgValue" : 11, "hour" : 5 }
{ "count" : 2, "totalValue" : 26, "avgValue" : 13, "hour" : 1 }
Finally, I solve this issue. Below is my code.

MongoDB: How To use $group aggregation to get all data using similar field?

I am trying to perform a query using golang mgo to effectively get similar values from a join.
My structure is like this:
result: [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
{
"_id" : 4,
"booking_id" : 95,
"provider_id" : 20,
"type" : "abc",
"time" : NumberLong(1541163544),
"location" : {
"lat" : 30.711858,
"lng" : 76.729649
},
},
{
"_id" : 8,
"booking_id" : 95,
"provider_id" : 20,
"type" : "aaa",
}
]
I have to group data of similar booking_id, Now how can I get a data of simlilar booking id using $group aggregation.I want a data in a following structure::
result: [
0:[
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
],
1:[
{
"_id" : 4,
"booking_id" : 95,
"provider_id" : 20,
"type" : "abc",
"time" : NumberLong(1541163544),
"location" : {
"lat" : 30.711858,
"lng" : 76.729649
},
},
{
"_id" : 8,
"booking_id" : 95,
"provider_id" : 20,
"type" : "aaa",
}
]
]
I have created a function which is returing result of this collection and using $group like this:
query := []bson.M{
{"$group": bson.M{
"_id": bson.M{"booking_id": "$booking_id"},
"count": bson.M{"$sum": 1}}}}
pipe := getCollection.Pipe(query)
err = pipe.All(&result)
But it will return this output to me:
[
{
"id": 0,
"booking_id": 0,
"provider_id": 0
}
]
Here I am mentioning only two booking ids data, I have 1000 of booking id records in my database.
I want to show data grouped by booking id, Is it possible using mongodb $group aggregation?? Or if not then how can I achieve this thing in mongodb using mgo package for golang.
You can use $group and $$ROOT, which references the document currently being processed in the pipeline.
Your aggregation would be something like:
{
$group: {
_id: '$booking_id',
items: {
$push: '$$ROOT'
}
}
}
Which would result in this:
[
{
"_id": 95,
"items": [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
]
}
],
...
EDIT:
If you want to show both booking_id and provider_id, you can group by these both fields and project the data the way you want. Like this:
[
{
$group: {
_id: { 'booking_id': '$booking_id', 'provider_id': '$provider_id' },
items: { $push: '$$ROOT' }
}
},
{
$project: {
_id: 0,
booking_id: '$_id.booking_id',
provider_id: '$_id.provider_id',
items: 1
}
}
]
Giving this structure:
[
{
"booking_id": 96,
"provider_id": 20,
"items": [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
]
}
],
...

mongodb where condition fails

I am working on mongodb for my current project my collection as follows
{
"_id" : ObjectId("5a3a567a8fb6e20f67cb10f7"),
"player_id" : "5a26453db767c01262eddc4e",
"quiz_type" : "Single",
"created_date" : ISODate("2017-12-20T12:24:26Z"),
"questions_answered" : [
{
"question_id" : ObjectId("5a3a0bfc2d53f131068b4567"),
"player_selection_status" : "Wrong",
"time_taken" : 10,
"points_gained" : 0,
"like" : 1,
"answered_date" : "2017-12-20T17:54:30+05:30"
},
{
"question_id" : ObjectId("5a3a0bfc2d53f131068b4568"),
"player_selection_status" : "Correct",
"time_taken" : 10,
"points_gained" : 5,
"like" : 1,
"answered_date" : "2017-12-20T17:54:32+05:30"
},
{
"question_id" : ObjectId("5a3a0bfc2d53f131068b4569"),
"player_selection_status" : "Correct",
"time_taken" : 10,
"points_gained" : 5,
"like" : 1,
"answered_date" : "2017-12-20T17:54:34+05:30"
},
{
"question_id" : ObjectId("5a3a0bfc2d53f131068b456a"),
"player_selection_status" : "Wrong",
"time_taken" : 10,
"points_gained" : 0,
"like" : 1,
"answered_date" : "2017-12-20T17:54:35+05:30"
},
{
"question_id" : ObjectId("5a3a0bfc2d53f131068b456c"),
"player_selection_status" : "Correct",
"time_taken" : 10,
"points_gained" : 5,
"like" : 1,
"answered_date" : "2017-12-20T17:54:37+05:30"
}
],
"__v" : 0
}
I need get data of points_gained : 5 only and my query is
db.player_quiz.find({player_id: "5a26453db767c01262eddc4e", 'questions_answered.points_gained': 5}).pretty()
using above query i am getting all results.. i need only records having questions_answered.points_gained: 5 only
please help me with a solution.
db.player_quiz.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
"path": '$questions_answered'
}
},
// Stage 2
{
$match: {
'questions_answered.points_gained': 5
}
},
// Stage 3
{
$group: {
_id: '$_id',
questions_answered: {
$addToSet: '$questions_answered'
},
doc: {
$first: '$$ROOT'
}
}
},
// Stage 4
{
$project: {
questions_answered: 1,
"player_id": '$doc.player_id',
'quiz_type': '$doc.quiz_type',
'created_date': '$doc.created_date'
}
},
]
);

Nested conditional MongoDB query

Im having a hard time trying to run some nested queries with a conditional statement of an item inside an array.
this is how my documents looks like.
I would like to get a summary such as sum and average and alarmedCount (count every time Channels.AlarmStatus == "alarmed") of each "Channel" based on Channels.Id. I got sum and average to work but cant get the right query for alarmedCount
{
"_id" : "55df8e4cd8afa4ccer1915ee"
"location" : "1",
"Channels" : [{
"_id" : "55df8e4cdsafa4cc0d1915r1",
"ChannelId" : 1,
"Value" : 14,
"AlarmStatus" : "normal"
},
{
"_id" : "55df8e4cdsafa4cc0d1915r9",
"ChannelId" : 2,
"Value" : 20,
"AlarmStatus" : "alarmed"
},
{
"_id" : "55df8e4cdsafa4cc0d1915re",
"ChannelId" : 3,
"Value" : 10,
"AlarmStatus" : "alarmed"},
]
}
{
"_id" : "55df8e4cd8afa4ccer1915e0"
"location" : "1",
"Channels" : [{
"_id" : "55df8e4cdsafa4cc0d19159",
"ChannelId" : 1,
"Value" : 50,
"AlarmStatus" : "normal"
},
{
"_id" : "55df8e4cdsafa4cc0d1915re",
"ChannelId" : 2,
"Value" : 16,
"AlarmStatus" : "normal"
},
{
"_id" : "55df8e4cdsafa4cc0d1915g7",
"ChannelId" : 3,
"Value" : 9,
"AlarmStatus" : "alarmed"},
]
}
I got it to work to group them and show some calculations
using this aggregate
db.records.aggregate( [
{
"$unwind" : "$Channels"
},
{
"$group" : {
"_id" : "$Channels.Id",
"documentSum" : { "$sum" : "$Channels.Value" },
"documentAvg" : { "$avg" : "$Channels.Value" }
}
}
] )
the result looks like this:
{
"result" : [
{
"_id" : 1,
"documentSum" : 64,
"documentAvg" : 32
},
{
"_id" : 2,
"documentSum" : 36,
"documentAvg" : 18
},
{
"_id" : 3,
"documentSum" : 19,
"documentAvg" : 9.5
},
],
"ok" : 1.0000000000000000
}
I would like to get this type of result
{
"result" : [
{
"_id" : 1,
"documentSum" : 64,
"documentAvg" : 32,
"AlarmedCount" : 0
},
{
"_id" : 2,
"documentSum" : 36,
"documentAvg" : 18,
"AlarmedCount" : 1
},
{
"_id" : 3,
"documentSum" : 19,
"documentAvg" : 9.5,
"AlarmedCount" : 2
}
],
"ok" : 1.0000000000000000
}
Use a project-step before your group-step to convert the field AlarmedStatus to 1 or 0 depending on its value:
$project: {
"Channels.value":"$Channels.Value",
"Channels.AlarmCount":{ $cond: {
if: { $eq: ["$Channels.AlarmedStatus", "alarmed"] },
then: 1,
else: 0 }
}
}
Then sum the newly created field to get the aggregated count:
$group : {
"_id" : "$Channels.Id",
"documentSum" : { "$sum" : "$Channels.Value" },
"documentAvg" : { "$avg" : "$Channels.Value" },
"AlarmCount" : { "$sum" : "$Channels.AlarmCount" }
}

Mongo aggregation $subtract between dynamic document value

Need to find the difference between two values of attendance,group by ward_id, based on patient id for two dates. The result has dynamic values based on the array. The difference is between two dates. Key would be ward_id, the difference will be between counts of patient's visit to the ward.
Example sample data
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-03T21:31:29.902Z"),
"ward_id" : 2561
},
"count" : 4112,
"values" : [
{
"count" : 9,
"patient" : ObjectId("54766f973f35473ffc644618")
},
{
"count" : 19,
"patient" : ObjectId("546680e2d660e2dc5ebfea39")
},
{
"count" : 47,
"patient" : ObjectId("546680e3d660e2dc5ebfea72")
},
{
"count" : 1,
"patient" : ObjectId("546a137bdab5f21e612ea7ef")
},
{
"count" : 93,
"patient" : ObjectId("546680e3d660e2dc5ebfea89")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-03T21:31:29.902Z"),
"ward_id" : 3720
},
"count" : 1,
"values" : [
{
"count" : 1,
"patient" : ObjectId("546a136ddab5f21e612ea6a6")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-04T21:31:29.902Z"),
"ward_id" : 2561
},
"count" : 4112,
"values" : [
{
"count" : 10,
"patient" : ObjectId("54766f973f35473ffc644618")
},
{
"count" : 10,
"patient" : ObjectId("546680e2d660e2dc5ebfea39")
},
{
"count" : 6,
"patient" : ObjectId("5474e9e46606f32570fa48ff")
},
{
"count" : 1,
"patient" : ObjectId("5474e9e36606f32570fa48f2")
},
{
"count" : 1,
"patient" : ObjectId("546680e3d660e2dc5ebfea77")
},
{
"count" : 543,
"patient" : ObjectId("546680e2d660e2dc5ebfea43")
},
{
"count" : 1,
"patient" : ObjectId("5485fdc8d27a9122956b1c66")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-04T21:31:29.902Z"),
"ward_id" : 3720
},
"count" : 1,
"values" : [
{
"count" : 7,
"patient" : ObjectId("546a136ddab5f21e612ea6a6")
}
]
}
Output
{
"ward_id":2561,
"result" : [{"person": ObjectId("54766f973f35473ffc644618"),
"count_1": 9,
"count_1": 10,
"difference":1 },{"person": ObjectId("546680e2d660e2dc5ebfea39"),
"count_1": 19,
"count_1": 10,
"difference":-9 } ....]
},
{
"ward_id":3720,
"result" : [{"person": ObjectId("546a136ddab5f21e612ea6a6"),
"count_1": 9,
"count_1": 10,
"difference":1 },{"person": ObjectId("546680e2d660e2dc5ebfea39"),
"count_1": 1,
"count_1": 7,
"difference":-6 }]
}
you can use the aggregation framework's $subtract operator outlined here: http://docs.mongodb.org/manual/reference/operator/aggregation-arithmetic/
db.wards.aggregate([
{
$match: {id: {$elemMatch: {ward_id: my_ward_id, ts: my_desired_ts}}},
},
{
$limit: 2
},
{
$project: {values: 1}
},
{
$unwind: '$values'
},
{
$match: {patient: my_patient_id}
},
{
$group: {
_id: null,
'count1': {$first: '$values.count'},
'count2': {$last: '$values.count'}
}
},
{
$subtract: ['$count1', '$count2']
}
])
i haven't tested this but it would probably look like something above