Sum of column inside a array in Mongodb - mongodb

I am new to MongoDB and trying to find the sum of device_status out of
device_execution array for a given serial number.e.g serial number
ELMESR0719PXN6 has two device status respectively 200 and 400 at a given
time.After each 5 minutes device status keep coming and we are storing it.So
we want to add the sum of status 200 or 400 per serial number.Below is the
document which contains all the details :
Output should be like below :
Serial number Device_status Sum
ELMESR0719PXN6 200 5
400 5
{
"_id" : ObjectId("5e57b3376c8b9aabd5312840"),
"ve_serial_number" : "ELMESR0719PXN6",
"ve_type" : "eVE",
"start_ts" : "2020-02-19T00:00:00.000+00:00",
"end_ts" : "2020-02-19T23:59:59.999+00:00",
"device_execution" : [
{
"execution_time" : "2020-02-19T00:00:00.000+00:00",
"device_status" : {
"200" : 4,
"400" : 2
}
},
{
"execution_time" : "2020-02-19T00:02:00.000+00:00",
"device_status" : {
"200" : 1,
"400" : 3
}
}
]
}
Can you please help me in finding the solution?Thanks in advance.

This one gives desired result:
db.collection.aggregate([
{ $unwind: "$device_execution" },
{
$group: {
_id: "$ve_serial_number",
"400": { $sum: "$device_execution.device_status.400" },
"200": { $sum: "$device_execution.device_status.200" }
}
}
])
Result:
{
"_id" : "ELMESR0719PXN6",
"200" : 5.0,
"400" : 5.0
}
The format is not the final one, this I leave up to you.

I think you can achieve this by using $unwind aggregation operator for "device_execution" field
Then you can just $group them by "ve_serial_number" and "device_status" and sum them up.

Related

Count of a nested value of all entries in mongodb collection

I have a collection named outbox which has this kind of structure
"_id" :ObjectId("5a94e02bb0445b1cc742d795"),
"track" : {
"added" : {
"date" : ISODate("2020-12-03T08:48:51.000Z")
}
},
"provider_status" : {
"job_number" : "",
"count" : {
"total" : 1,
"sent" : 0,
"delivered" : 0,
"failed" : 0
},
"delivery" : []
}
I have 2 tasks. First I want the sum of all the "total","sent","failed" on all the entries in the collection no matter what their objectId is. ie I want sum of all the "total","sent","delivered" and "failed". Second I want all these only for a given object Id between Start and End date.
I am trying to find total using this query
db.outbox.aggregate(
{ $group: { _id : null, sum : { $sum: "$provider_status.count.total" } } });
But I am getting this error as shown
Since I do not have much experience in mongodb I don't have any idea how to do these two tasks. Need help here.
You are executing this in Robo3t seems like.
You need to enclose this in an array like
db.test.aggregate([ //See here
{
$group: {
_id: null,
sum: {
$sum: "$provider_status.count.total"
}
}
}
])//See here
But it's not the case with playground as they handle them before submitting to the server

MongoDB $divide on aggregate output

Is there a possibility to calculate mathematical operation on already aggregated computed fields?
I have something like this:
([
{
"$unwind" : {
"path" : "$users"
}
},
{
"$match" : {
"users.r" : {
"$exists" : true
}
}
},
{
"$group" : {
"_id" : "$users.r",
"count" : {
"$sum" : 1
}
}
},
])
Which gives an output as:
{ "_id" : "A", "count" : 7 }
{ "_id" : "B", "count" : 49 }
Now I want to divide 7 by 49 or vice versa.
Is there a possibility to do that? I tried $project and $divide but had no luck.
Any help would be really appreciated.
Thank you,
From your question, it looks like you are assuming result count to be 2 only. In that case I can assume users.r can have only 2 values(apart from null).
The simplest thing I suggest is to do this arithmetic via javascript(if you're using it in mongo console) or in case of using it in progam, use the language you're using to access mongo) e.g.
var results = db.collection.aggregate([theAggregatePipelineQuery]).toArray();
print(results[0].count/results[1].count);
EDIT: I am sharing an alternative to above approach because OP commented about the constraint of not using javascript code and the need to be done only via query. Here it is
([
{ /**your existing aggregation stages that results in two rows as described in the question with a count field **/ },
{ $group: {"_id": 1, firstCount: {$first: "$count"}, lastCount: {$last: "$count"}
},
{ $project: { finalResult: { $divide: ['$firstCount','$lastCount']} } }
])
//The returned document has your answer under `finalResult` field

MongoDB: How to update a nested array value after checking if greater than zero?

I'm able to decrease a value in a nested array, but I want to check that the value is greater than zero (so never go to the negative numbers). For example, I have this:
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots" : [
{
"id" : "V2qlAEk7Wp0tWwlyWSfX7KRZ",
"number" : 5.0
},
.......
.......
{
"id" : "VfB4f8G1KcgRA8qx0aby5nI0",
"number" : 0.0
}]
}
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839ag"),
"slots" : [
{
"id" : "V2qlAEEESbrEB4bwberbResbd",
"number" : 10.0
},
.......
.......
{
"id" : "DFwseEb5enRbfsbre54rtFfds",
"number" : 1.0
}]
}
If I want to decrease the number value of the first document [id=5a3b0bd69c0000c2a1d839af] of the slots.id = V2qlAEk7Wp0tWwlyWSfX7KRZ, I use:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ"
},
{
"$inc":{"slots.$.number":-1}
})
But I don't know how to check if the number is greater than 0 before decrease the value. I tried to use The filtered positional operator $[<identifier>], but I have an error:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ"
},
{
"$inc":{"slots.$[elm].number":-1}
},
{
arrayFilters: [ { "elm.number": {"$gt" : 0}} ]
})
The error is:
cannot use the part (slots of slots.$[elm].number) to traverse the element....
Also, apply the condition in the first part of the query doesn't fix the problem, cause mongo traverses all the array, so also if the number is zero it simply move to another element in the array until it finds number > 0 and when it finds it, it starts to decrease the number value of another element completely ignoring the two id conditions [because now the $ then point to another element of the array]:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ",
"slots.number":{"$gt" : 0}
},
{
"$inc":{"slots.$.number":-1}
})
I think the correct way to obtain what I want is to use the arrayFilters but I don't understand where is the problem.
Fixed using:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots" :
{
"$elemMatch":
{
"id":"V2qlAEk7Wp0tWwlyWSfX7KRZ",
"number":
{
"$gt" : 0
}
}
}
},
{
"$inc":
{
"slots.$.number":-1
}
})
Thanks

Remove redundant data from sensors by using date and value

I'm developing an application that collects data from sensors and I need to reduce the amount of data that is stored in a mongodb database by using a value (temperature) and a date (timestamp).
The document have the following format:
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:37:50.370Z")
sensorCode:"SENSOR_A1"
}
The problem is that sensors sent data too much frequently so there are too many documents with redudant data in a short period of time (let's say 10 minutes). I meant it is not useful to have multiple equal values in a very short period of time.
Example: here there are data from a sensor that is reporting temperature is 10
// collection: datasensors
[
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:37:50.370Z")
sensorCode:"SENSOR_A1"
},
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:38:50.555Z")
sensorCode:"SENSOR_A1"
},
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:38:51.654Z")
sensorCode:"SENSOR_A1"
}
,
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:50:20.335Z")
sensorCode:"SENSOR_A1"
}
]
Because a minute precission is not required, I would like to remove all documents from 2016-04-29T14:37:50.370Z to 2016-04-29T14:38:51.32Z except one. So the result should be this:
[
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:38:51.654Z")
sensorCode:"SENSOR_A1"
},
{
temperature: 10,
timestamp: ISODate("2016-04-29T14:50:20.335Z")
sensorCode:"SENSOR_A1"
}
]
The remove operation I want to perform should "reduce" equal temperatures in time ranges less than 10 minutes to one value.
Is there any technique to achieve this?
I simplified my solution and decided to keep every unique measurement received in 10 minutes time window.
Mongo 3.2 is required for that
adding a time mark will separate measurements in 10 minutes time groups
Then we are preserving first record in group and storing all ids for futher process
Then removing id of document we want to keep from an array of all ids (let say documents to delete)
Finally as forEach loop we are deleting not needed ids - this line is commented :-)
Copy code below to mongo console, execute and verify ids to delete, then un-comment and GO!
var addTimeMark = {
$project : {
_id : 1,
temperature : 1,
timestamp : 1,
sensorCode : 1,
yearMonthDay : {
$substr : [{
$dateToString : {
format : "%Y%m%d%H%M",
date : "$timestamp"
}
}, 0, 11]
}
}
}
var getFirstRecordInGroup = {
// take only first record froum group
$group : {
_id : {
timeMark : "$yearMonthDay",
sensorCode : "$sensorCode",
temperature : "$temperature"
},
id : {
$first : "$_id"
},
allIds : {
$push : "$_id"
},
timestamp : {
$first : "$timestamp"
},
totalEntries : {
$sum : 1
}
}
}
var removeFirstIdFromAllIds = {
$project : {
_id : 1,
id : 1,
timestamp : 1,
totalEntries : 1,
allIds : {
$filter : {
input : "$allIds",
as : "item",
cond : {
$ne : ["$$item", "$id"]
}
}
}
}
}
db.sensor.aggregate([
addTimeMark,
getFirstRecordInGroup,
removeFirstIdFromAllIds,
]).forEach(function (entry) {
printjson(entry.allIds);
// db.sensor.deleteMany({_id:{$in:entry.allIds}})
})
below document outlook after each step:
{
"_id" : ObjectId("574b5d8e0ac96f88db507209"),
"temperature" : 10,
"timestamp" : ISODate("2016-04-29T14:37:50.370Z"),
"sensorCode" : "SENSOR_A1",
"yearMonthDay" : "20160429143"
}
2:
{
"_id" : {
"timeMark" : "20160429143",
"sensorCode" : "SENSOR_A1",
"temperature" : 10
},
"id" : ObjectId("574b5d8e0ac96f88db507209"),
"allIds" : [
ObjectId("574b5d8e0ac96f88db507209"),
ObjectId("574b5d8e0ac96f88db50720a"),
ObjectId("574b5d8e0ac96f88db50720b")
],
"timestamp" : ISODate("2016-04-29T14:37:50.370Z"),
"totalEntries" : 3
}
and last;
{
"_id" : {
"timeMark" : "20160429143",
"sensorCode" : "SENSOR_A1",
"temperature" : 10
},
"id" : ObjectId("574b5d8e0ac96f88db507209"),
"allIds" : [
ObjectId("574b5d8e0ac96f88db50720a"),
ObjectId("574b5d8e0ac96f88db50720b")
],
"timestamp" : ISODate("2016-04-29T14:37:50.370Z"),
"totalEntries" : 3
}

Project data set into new objects

I have a really simple question which has troubled me for some time. I have a list of objects containing an array of Measurements, where each of these contains a time and multiple values like below:
{
"_id" : ObjectId("5710ed8129c7f31530a537bc"),
"Measurements" : [
{
"_t" : "Measurement",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 1
"Measurement2" : 2
"Measurement3" : 3
},
{
"_t" : "DataType",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 4
"Measurement2" : 5
"Measurement3" : 6
},
{
"_t" : "DataType",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 7
"Measurement2" : 8
"Measurement3" : 9
} ]
},
{
"_id" : ObjectId("5710ed8129c7f31530a537cc"),
"Measurements" : [
{
"_t" : "Measurement",
"_time" : ISODate("2016-04-14T12:31:52.584Z"),
"Measurement1" : 0
....
I want to create a query which projects the following data set into the one below. For example, query for Measurement1 and create an array of objects containing the time and value of Measurement1 (see below) via mongo aggregation framework.
{ "Measurement": [
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 1
}
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 4
}
{
"Time": ISODate("2016-04-14T12:31:52.584Z"),
"Value": 7
}
]}
Seems like a pretty standard operation, so I hope you guys can shed some light on this.
You can do this by first unwinding the Measurements array for each doc and then projecting the fields you need and then grouping them back together:
db.test.aggregate([
// Duplicate each doc, once per Measurements array element
{$unwind: '$Measurements'},
// Include and rename the desired fields
{$project: {
'Measurements.Time': '$Measurements._time',
'Measurements.Value': '$Measurements.Measurement1'
}},
// Group the docs back together to reassemble the Measurements array field
{$group: {
_id: '$_id',
Measurements: {$push: '$Measurements'}
}}
])