We are using the size bucket pattern for our datas and we are wondering how could we aggregate the nested array for getting a average every 1, 5 or 10 minutes on this nested array. The structure is as follow:
{
"_id" : ObjectId("62ad1548a7d67adf4943d2f7"),
"data" : ObjectId("6262a3048cbdacd24c38fbc4"),
"day" : ISODate("2022-06-17T00:00:00.000Z"),
"first" : ISODate("2022-06-17T23:59:04.659Z"),
"last" : ISODate("2022-06-17T23:59:59.915Z"),
"nSamples" : 97,
"samples" : [
{
"time" : ISODate("2022-06-17T23:59:04.659Z"),
"value" : 8
},
{
"time" : ISODate("2022-06-17T23:59:50.706Z"),
"value" : 6
},
///// MAX 200 Documents in nested array
{
"time" : ISODate("2022-06-17T23:59:59.342Z"),
"value" : 4
},
{
"time" : ISODate("2022-06-17T23:59:59.915Z"),
"value" : 12
}
],
"version" : 0
}
{
"_id" : ObjectId("62ad1548a7d67adf4943d2f7"),
"data" : ObjectId("62629d178cbdac1e9938fb9e"),
"day" : ISODate("2022-06-17T00:00:00.000Z"),
"first" : ISODate("2022-06-17T23:32:04.659Z"),
"last" : ISODate("2022-06-17T23:40:59.915Z"),
"nSamples" : 97,
"samples" : [
{
"time" : ISODate("2022-06-17T23:32:04.659Z"),
"value" : 2
},
{
"time" : ISODate("2022-06-17T23:33:50.706Z"),
"value" : 65
},
///// MAX 200 Documents in nested array
{
"time" : ISODate("2022-06-17T23:36:59.342Z"),
"value" : 45
},
{
"time" : ISODate("2022-06-17T23:40:59.915Z"),
"value" : 22
}
],
"version" : 0
}
Is it possible to get this structure ?
{
"_id" : ObjectId("62ad1548a7d67adf4943d2f7"),
"data" : ObjectId("6262a3048cbdacd24c38fbc4"),
"day" : ISODate("2022-06-17T00:00:00.000Z"),
"first" : ISODate("2022-06-17T23:59:04.659Z"),
"last" : ISODate("2022-06-17T23:59:59.915Z"),
"nSamples" : 97,
"samples" : [
// Less documents
{
"time" : ISODate("2022-06-17T23:56:00.000Z"),
"value" : 7
},
{
"time" : ISODate("2022-06-17T23:58:00.000Z"),
"value" : 6
},
{
"time" : ISODate("2022-06-17T23:59:00.000Z"),
"value" : 4
},
],
"version" : 0
}
{
"_id" : ObjectId("62ad1548a7d67adf4943d2f7"),
"data" : ObjectId("62629d178cbdac1e9938fb9e"),
"day" : ISODate("2022-06-17T00:00:00.000Z"),
"first" : ISODate("2022-06-17T23:59:04.659Z"),
"last" : ISODate("2022-06-17T23:59:59.915Z"),
"nSamples" : 97,
"samples" : [
// Less docs
{
"time" : ISODate("2022-06-17T23:32:00.000Z"),
"value" : 2
},
{
"time" : ISODate("2022-06-17T23:33:00.000Z"),
"value" : 65
},
{
"time" : ISODate("2022-06-17T23:36:00.000Z"),
"value" : 45
},
{
"time" : ISODate("2022-06-17T23:37:00.000Z"),
"value" : 22
}
],
"version" : 0
}
So far, I first start by a $match for selecting values and add a "fromDate" value for searching:
$match: {
$and: [
{
data: {"$in": [ObjectId("62629d178cbdac1e9938fb9e"), ObjectId("62629d178cbdac1e9938fb9f"), ObjectId("62629d178cbdac1e9938fb9d")]} ,
},
{
first: {
$gte: new Date('2022-07-08T08:10:22.525+00:00')
}
}
],
}
Then by using the $unwind aggregate on samples
$unwind: {
path: "$samples",
preserveNullAndEmptyArrays: true
}
And then grouping the values with an interval:
$group: {
_id: {
"$toDate": {
"$subtract": [
{ "$toLong": "$samples.time" },
{ "$mod": [ { "$toLong": "$samples.time" }, 1000 * 60 * 1 ] } // 1 for 1 minute could be replaced by 5, 10 etc...
]
}
},
values: {
$avg: "$samples.value"
}
}
The problem here is that it does not take in consideration that I have multiple "data"... How can I group them by data as well ? Is it possible to keep the same structure and having "less root documents" ?
One option to continue your query is:
db.collection.aggregate([
{$match: {
data: {"$in": [
ObjectId("62629d178cbdac1e9938fb9e"),
ObjectId("6262a3048cbdacd24c38fbc4"),
ObjectId("62629d178cbdac1e9938fb9d")
]
},
first: {$gte: ISODate("2021-07-08T08:10:22.525+00:00")}
}
},
{$unwind: {path: "$samples", preserveNullAndEmptyArrays: true}},
{$group: {
_id: {
time: {$toDate: {
$subtract: [
{$toLong: "$samples.time"},
{$mod: [{$toLong: "$samples.time"}, 60000]}
]
}
},
orig_id: "$_id"
},
values: {$avg: "$samples.value"},
root: {$first: "$$ROOT"}
}
},
{$sort: {"_id.time": 1}},
{$group: {_id: "$_id.orig_id", root: {$first: "$root"},
samples: {$push: {time: "$_id.time", value: "$values"}}}
},
{$set: {"root.samples": "$samples", "root._id": "$_id"}},
{$replaceRoot: {newRoot: "$root"}}
])
See how it works on the playground example
Related
The only thing I am trying to do is to get the average of Emision_C02 consumed at 10pm for all the days in location:1. The collection, db.datos_sensores2, has documents within like:
{
"_id" : ObjectId("609c2c2d420a73728827e87f"),
"timestamp" : ISODate("2020-07-01T02:15:00Z"),
"sensor_id" : 1,
"location_id" : 1,
"medidas" : [
{
"tipo_medida" : "Temperatura",
"valor" : 14.03,
"unidad" : "ÂșC"
},
{
"tipo_medida" : "Humedad_relativa",
"valor" : 84.32,
"unidad" : "%"
}
]
}
{
"_id" : ObjectId("609c2c2d420a73728827e880"),
"timestamp" : ISODate("2020-07-01T02:15:00Z"),
"sensor_id" : 2,
"location_id" : 1,
"medidas" : [
{
"tipo_medida" : "Emision_CO2",
"valor" : 1.67,
"unidad" : "gCO2/m2"
},
{
"tipo_medida" : "Consumo_electrico",
"valor" : 0.00155,
"unidad" : "kWh/m2"
}
]
}
I wrote this:
db.datos_sensores2.aggregate([
{$project:{timestamp:{$dateFromString:{dateString:'$timestamp'}},"_id":0, "me-didas":{$slice:["$medidas",-1]},"location_id":1}},
{$addFields:{Hora:{$hour:"$timestamp"}}},
{$match:{'Hora':{$in:[10]},'medidas.tipo_medida':"Emision_CO2", "location_id":1}},
{$group:{ _id: null, Avg_Emision_CO2:{$avg: "$medidas.valores"}}}])
But nothing happen....
pls refer to https://mongoplayground.net/p/-LqswomHWsY
I have noticed few things first of all hour comes to be 2 in above example and not 10. Second the variable/field names are not correct so i have updated it.
[{$unwind: {
path: '$medidas',
}}, {$addFields: {
Hora: {
$hour: "$timestamp"
}
} }, {$match: {
"Hora": {
$in: [2]
},
"medidas.tipo_medida": "Emision_CO2",
"location_id": 1
} }, {$group: {
_id: null,
Avg_Emision_CO2: {
$avg: "$medidas.valor"
}
}}]
Pipeline stages:
unwind: as $medidas is array we can unwind it so it will be easy to filter only "Emision_CO2",
addfield: add houre from timestamp
match: to match "medidas.tipo_medida": "Emision_CO2",
group: to get average
How to get percentage total of data with group by date in MongoDB ?
Link example : https://mongoplayground.net/p/aNND4EPQhcb
I have some collection structure like this
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date" : "2019-05-03T10:39:53.108Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date" : "2019-05-03T10:39:53.133Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date" : "2019-05-03T10:39:53.180Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4e"),
"date" : "2019-05-03T10:39:53.218Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
And I have query in mongodb to get data of collection, how to get percentage of total data. in bellow example query to get data :
db.name_collection.aggregate(
[
{ "$match": {
"update_at": { "$gte": "2019-11-04T00:00:00.0Z", "$lt": "2019-11-06T00:00:00.0Z"},
"id": { "$in": [166] }
} },
{
"$group" : {
"_id": {
$substr: [ '$update_at', 0, 10 ]
},
"count" : {
"$sum" : 1
}
}
},
{
"$project" : {
"_id" : 0,
"date" : "$_id",
"count" : "$count"
}
},
{
"$sort" : {
"date" : 1
}
}
]
)
and this response :
{
"date" : "2019-11-04",
"count" : 39
},
{
"date" : "2019-11-05",
"count" : 135
}
how to get percentage data total from key count ? example response to this :
{
"date" : "2019-11-04",
"count" : 39,
"percentage" : "22%"
},
{
"date" : "2019-11-05",
"count" : 135,
"percentage" : "78%"
}
You have to group by null to get total count and then use $map to calculate the percentage. $round will be a useful operator in such case. Finally you can $unwind and $replaceRoot to get back the same number of documents:
db.collection.aggregate([
// previous aggregation steps
{
$group: {
_id: null,
total: { $sum: "$count" },
docs: { $push: "$$ROOT" }
}
},
{
$project: {
docs: {
$map: {
input: "$docs",
in: {
date: "$$this.date",
count: "$$this.count",
percentage: { $concat: [ { $toString: { $round: { $multiply: [ { $divide: [ "$$this.count", "$total" ] }, 100 ] } } }, '%' ] }
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: { newRoot: "$docs" }
}
])
Mongo Playground
db.test.aggregate([
{$match: { "colx" : 8323, "id" : {$in: [802, ....]}}},
{$group: {_id : { coly : "$coly" , id : "$id" } , total: {$sum: "$val"} } },
{$group: {_id: '$_id.coly', items: {$push: {id: '$_id.id', total: '$total'}} } }
])
the result is :
{
"_id": 898,
"items": [
{
"id" : 801
"total" : 3355560
},
{
"id" : 805
"total" : 2760139
}
]
}
is there a way to update all documents with an extra item after the last operation $group so that we get a result similar to this :
{
"_id": 898,
"items": [
{
"id" : 801
"total" : 3355560
},
{
"id" : 805
"total" : 2760139
},
{ //extra item
"id" : 817
"total" : 0
}
]
}
I have a collection with multiple documents like
{
"_id" : ObjectId("5a64d076bfd103df081967ae"),
"status" : "",
"Number" : 53,
"values" : [
{
"date" : "2015-05-18",
"value" : 12.41
},
{
"date" : "2015-05-19",
"value" : 12.45
},
],
"Name" : "ABC Banking",
"scheme":"ABC1",
"createdDate" : "21-01-2018"
}
{
"_id" : ObjectId("5a64d076bfd103df081967ae"),
"status" : "",
"Number" : 53,
"values" : [
{
"date" : "2015-05-18",
"value" : 13.41
},
{
"date" : "2015-05-19",
"value" : 13.45
},
],
"Name" : "ABC Banking",
"scheme":"ABC2",
"createdDate" : "21-01-2018"
}
I am Querying collection based on Number field like
db.getCollection('mfhistories').find({'Number':53})
to get all the documents with this Number.
Now I want to group all the collection with Name 'ABC Banking' into an array. so that I will get result based on Name.
so the result should be like
{
"Name":"ABC Banking",
[
{
"_id" : ObjectId("5a64d076bfd103df081967ae"),
"status" : "",
"Number" : 53,
"values" : [
{
"date" : "2015-05-18",
"value" : 13.41
},
{
"date" : "2015-05-19",
"value" : 13.45
},
],
"scheme":"ABC1",
"createdDate" : "21-01-2018"
},
{
"_id" : ObjectId("5a64d076bfd103df081967ae"),
"status" : "",
"Number" : 53,
"values" : [
{
"date" : "2015-05-18",
"value" : 13.41
},
{
"date" : "2015-05-19",
"value" : 13.45
}
],
"scheme":"ABC2",
"createdDate" : "21-01-2018"
}
]
}
Please help..
Thanks,
J
You can use Aggregation Framework for that:
db.col.aggregate([
{
$match: { Number: 53, Name: "ABC Banking" }
},
{
$group: {
_id: "$Name",
docs: { $push: "$$ROOT" }
}
},
{
$project: {
Name: "$_id",
_id: 0,
docs: 1
}
}
])
$$ROOT is a special variable which captures entire document. More here.
db.mfhistories.aggregate(
// Pipeline
[
// Stage 1
{
$match: {
Number: 53
}
},
// Stage 2
{
$group: {
_id: {
Name: '$Name'
},
docObj: {
$addToSet: '$$CURRENT'
}
}
},
// Stage 3
{
$project: {
Name: '$_id.Name',
docObj: 1,
_id: 0
}
}
]
);
I have below collection structure and I want to find minimum score for each student.
>db.students.findOne()
{
"_id" : 0,
"name" : "aimee Zank",
"scores" : [
{
"type" : "exam",
"score" : 1.463179736705023
},
{
"type" : "quiz",
"score" : 11.78273309957772
},
{
"type" : "homework",
"score" : 6.676176060654615
},
{
"type" : "homework",
"score" : 35.8740349954354
}
]
}
I use below aggregate command
db.students.aggregate([
{
$group: {_id: "$_id" , min: {$min: '$scores.score'}}
}
])
below is the output:
{ "_id" : 199, "min" : [ 82.11742562118049, 49.61295450928224, 28.86823689842918, 5.861613903793295 ] }
{ "_id" : 198, "min" : [ 11.9075674046519, 20.51879961777022, 55.85952928204192, 64.85650354990375 ] }
{ "_id" : 95, "min" : [ 8.58858127638702, 88.40377630359677, 25.71387474240768, 23.73786528217532 ] }
{ "_id" : 11, "min" : [ 78.42617835651868, 82.58372817930675, 87.49924733328717, 15.81264595052612 ] }
{ "_id" : 94, "min" : [ 6.867644836612586, 63.4908039680606, 85.41865347441522, 26.82623527074511 ] }
it returns all scores for each student instead of the minimum one. What wrong with my query command? I am using mongo 3.4.
After some searching, I found that the solution is to add $unwind on scores.score. The complete command is:
stus = db.students.aggregate([
{
"$unwind": "$scores"
},
{
$group: {_id: "$_id" , minScore: {$min: '$scores.score'}}
}
])