How to add a field to my projected result that is identical to the argument I sent - mongodb

MY MONGOPLAY
How can I add a field to my result that is identical to the argument I initially sent. Note in my or statement I'm sending a date and I wish to add this date to my results (example requested_by).
I know that '$addFields' would do the trick but couldnt figure out how to integrate it.
My Query:
db.collection.aggregate([
{
"$match": {
"$or": [
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-02-09T00:00:00.000Z") //NEED THIS PARAMETER in my RESULT
}
},
{
"car_id": 79,
"timestamp": {
"$lte": ISODate("2020-03-22T00:00:00.000Z") //NEED THIS PARAMETER in my RESULT
}
}
]
}
},
{
"$sort": {
"timestamp": 1
}
},
{
"$group": {
"_id": "$car_id",
"last": {
"$last": "$$ROOT"
}
}
}
] )
expected result:
[
{
"_id": 78,
"last": {
"_id": ObjectId("60a2c0621e5f043b735e36f0"),
"car_id": 78,
"terminal": "LAX",
"timestamp": ISODate("2020-02-08T17:00:00Z"),
"requested_by": ISODate("2020-02-09T00:00:00.000Z") //<--somethign like this
}
},
{
"_id": 79,
"last": {
"_id": ObjectId("60a2c0621e5f043b735e36f3"),
"car_id": 79,
"terminal": "ORD",
"timestamp": ISODate("2020-03-21T17:00:00Z"),
"requested_by": ISODate("2020-03-22T00:00:00.000Z") //<--somethign like this
}
}
]

You can use '$addFields' in your aggregation as following:
db.collection.aggregate([
{
"$match": {
"$or": [
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-02-09T00:00:00.000Z")
}
},
{
"car_id": 79,
"timestamp": {
"$lte": ISODate("2020-03-22T00:00:00.000Z")
}
}
]
}
},
{
"$sort": {
"timestamp": 1
}
},
{
"$addFields": {
"requested_by": "something"
}
},
{
"$group": {
"_id": "$car_id",
"last": {
"$last": "$$ROOT"
}
}
}
])
If your 'requested_by' field should be calculated based on stored value in the object, you can use pipeline operators to calculate its value. For example:
...
{
"$addFields": {
"requested_by": "$timestamp"
}
},
...
or
...
{
"$addFields": {
"requested_by": {
"$add": [
"$car_id",
20
]
}
}
},
...

Related

Mongo aggregate on array objects using count

I have a collection with documents in below format: (shown below 2 sample document)
1st doc:
{
"date": 20221101,
"time":1500,
"productCode": "toycar",
"purchaseHistory": [
{
"clientid": 123,
"status": "SUCCESS"
},
{
"clientid": 456,
"status": "FAILURE"
}
]
}
2nd doc:
{
"date": 20221101,
"time": 1500,
"productCode": "toycar",
"purchaseHistory": [
{
"clientid": 890,
"status": "SUCCESS"
},
{
"clientid": 678,
"status": "SUCCESS"
}
]
}
I want to query above and print output in below format where purchaseHistory.status = 'SUCCESS' and date = 20221101:
{productCode:"toycar", "time": 1500, "docCount": 2, "purchaseHistCount":3}
How can I achieve this?
I tried below:
db.products.aggregate({
$match : {date:20221101, 'purchaseHistory.status':'SUCCESS'},
"$group": {
"_id": {
"pc": "$productCode",
"time": "$time"
},
"docCount": {$sum :1}
}
})
Something like this maybe:
db.collection.aggregate([
{
$match: {
date: 20221101,
"purchaseHistory.status": "SUCCESS"
}
},
{
"$addFields": {
"purchaseHistory": {
"$filter": {
"input": "$purchaseHistory",
"as": "ph",
"cond": {
$eq: [
"$$ph.status",
"SUCCESS"
]
}
}
}
}
},
{
$group: {
_id: {
t: "$time",
pc: "$productCode"
},
docCount: {
$sum: 1
},
purchaseHistCount: {
$sum: {
$size: "$purchaseHistory"
}
}
}
}
])
Explained:
Filter the matched documents.
Filter the purchaseHistory SUCCESS only.
Group the result to see count of matching documents & matching purchaseHistory.
Playground

Or statement returns the first result only (should be 2)

I have an OR statement where the expected result should return 2 documents but the return result is only 1 document. I have a list of cars docs:
[
{
"_id": ObjectId("60a2c0621e5f043b735e36ef"),
"car_id": 78,
"terminal": "JFK",
"timestamp": ISODate("2020-01-01T17:00:00.000Z"),
},
{
"_id": ObjectId("60a2c0621e5f043b735e36f0"),
"car_id": 78,
"terminal": "LAX",
"timestamp": ISODate("2020-02-08T17:00:00.000Z"),
},
{
"_id": ObjectId("60a2c0621e5f043b735e36f1"),
"car_id": 78,
"terminal": "ORD",
"timestamp": ISODate("2020-03-01T17:00:00.000Z"),
},
]
and my query asks for 2 instances with SAME CAR ID but with DIFFERENT timestamp:
db.collection.aggregate([
{
"$match": {
"$or": [
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-02-15T05:00:11.000Z")
}
},
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-03-02T11:07:27.000Z")
}
}
]
}
},
{
"$sort": {
"timestamp": 1
}
},
{
"$group": {
"_id": "$car_id",
"last": {
"$last": "$$ROOT"
}
}
}
])
So I expect to get 2 results (for each request).
Expected result:
[
{
"_id": 78,
"last": {
"_id": ObjectId("60a2c0621e5f043b735e36f1"),
"car_id": 78,
"terminal": "ORD",
"timestamp": ISODate("2020-03-01T17:00:00Z")
}
},
{
"_id": 78,
"last": {
"_id": ObjectId("60a2c0621e5f043b735e36f0"),
"car_id": 78,
"terminal": "LAX",
"timestamp": ISODate("2020-02-08T17:00:00Z")
}
}
]
But I only get the first result. How can I get the desired 2 results?
mongoplayground
Because all of the returned docs has the same car_id and you use car_id in $group stage so they will be grouped into one result. To get your expected result, you can add a field depend on timestamp of the doc then use that filed in $group:
db.collection.aggregate([
{
"$match": {
"$or": [
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-02-15T05:00:11.000Z")
}
},
{
"car_id": 78,
"timestamp": {
"$lte": ISODate("2020-03-02T11:07:27.000Z")
}
}
]
}
},
{
"$sort": {
"timestamp": 1
}
},
{
$addFields: {
group: {
$cond: {
if: {
$lte: [
"$timestamp",
ISODate("2020-02-15T05:00:11.000Z")
]
},
then: 1,
else: 2
}
}
}
},
{
"$group": {
"_id": "$group",
"last": {
"$last": "$$ROOT"
}
}
}
])
Mongoplayground
Note: If you have more than one car_id in the result, you can change the $group to:
{
"$group": {
"_id": {
car_id: "$car_id",
group: "$group",
},
"last": {
"$last": "$$ROOT"
}
}
}
Mongoplayground

Mongo MQL group by date and add counts of other field values

I'm struggling to understand how to query my data using MQL. My dataset looks a bit like this:
{
"_id": {
"$oid": "5dcadda84d59f2e0b0d56974"
},
"object_kind": "pipeline",
"object_attributes": {
"status": "success",
"created_at": "2019-11-12 16:28:22 UTC",
"variables": []
}
},
{
"_id": {
"$oid": "5dcadda84d59f2e0b0d56998"
},
"object_kind": "pipeline",
"object_attributes": {
"status": "failed",
"created_at": "2019-11-13 12:22:22 UTC",
"variables": []
}
}
I'm adding $eventDate using this in my aggregation, which works:
{
eventDate: { $dateFromString: {
dateString: {
$substr: [ "$object_attributes.created_at",0, 10 ]
}
}},
}
And I'm trying to turn it into this:
{
"eventDate": "2019-11-12",
"counts": {
"success": 1,
"failure": 0
}
},
{
"eventDate": "2019-11-13",
"counts": {
"success": 0,
"failure": 1
}
},
So far I can't seem to understand how to group the data twice, as if I group by "$eventDate" then I can't then group by status. Why can't I just group all docs from the same $eventDate into an array, without losing all the other fields?
It would be ideal if the success and failure fields which could be inferred from different statuses that appear in object_attributes.status
This can be done in several different ways, heres a quick example using a conditional sum:
db.collection.aggregate([
{
"$addFields": {
"eventDate": {
"$dateFromString": {
"dateString": {
"$substr": [
"$object_attributes.created_at",
0.0,
10.0
]
}
}
}
}
},
{
"$group": {
"_id": "$eventDate",
"success": {
"$sum": {
"$cond": [
{
"$eq": [
"$object_attributes.status",
"success"
]
},
1.0,
0.0
]
}
},
"failure": {
"$sum": {
"$cond": [
{
"$eq": [
"$object_attributes.status",
"failed"
]
},
1.0,
0.0
]
}
}
}
},
{
"$project": {
"eventDate": "$_id",
"counts": {
"success": "$success",
"failure": "$failure"
},
"_id": 0
}
}
]);

How do I group by day/month in mongoDB?

Here's how one document looks like:
{
"login_Id": "c",
"name": "Abhishek Soni",
"location": "BLAHBLAH",
"work": [
{
"date":ISODate("2014-01-01"),
"total_time": 100,
},
{
"date":ISODate("2014-09-02"),
"total_time": 100,
},
{
"date":ISODate("2014-01-01"),
"total_time": 10,
},
]
}
What I want to do is to run a query that'll give an output like this:
{login_Id: 'c', work:{'01' : 110, '02': 100, ... and so on}}
Basically, I just want to group the work part month wise.
This is what I have tried:
db.employees.aggregate([
{
"$project": {
"_id": 0,
"login_Id": 1,
"time": {
"$sum": "$work.total_time"
}
}
},
{
"$group": {
"_id": {
"$dayOfYear": "$work.date"
},
"time": {
"$sum": "$work.total_time"
}
}
}
]);
But it outputs null. If I remove the group clause, I get the total sum (i.e., 210) What's wrong?
You can try below aggregation
db.collection.aggregate([
{ "$unwind": "$work" },
{ "$match": { "work.date": { "$type": "date" }}},
{ "$group": {
"_id": { "date": { "$dayOfMonth": "$work.date" }},
"time": { "$sum": "$work.total_time" },
"login_Id": { "$first": "$login_Id" }
}},
{ "$group": {
"_id": "$login_Id",
"data": {
"$push": {
"k": { "$toString": "$_id.date" },
"v": "$time"
}
}
}},
{ "$project": {
"work": { "$arrayToObject": "$data" },
"_id": 0,
"login_id": "$_id"
}}
])
Output
[
{
"login_id": "c",
"work": {
"1": 110,
"2": 100
}
}
]

Query MongoDB for nested Arrays

Need help for formatting query to find/get values using search parameters with nested Array.
I have an collection as follows
[
{
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z",
"deviceid": "123456",
"values": [
{
"minval": 1,
"minvalues": [
{
"secval": 51,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 1
}
},
{
"secval": 52,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 1
}
},
{
"secval": 56,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 0
}
}
]
}
]
}
]
need the out as follows with search properties as "values.minvalues.secvalues.priority"
[
{
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z",
"deviceid": "123456",
"values": [
{
"minval": 1,
"minvalues": [
{
"secval": 56,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 0
}
}
]
}
]
}
]
I tried the following query but with out success
dbRetval.db('ls_gpsdatabase').collection('gpsevent').aggregate([
{ "$match": { "deviceid": { "$in": idList}}},
{ "$sort": { "_id": -1} },
{"$unwind":"$values.minvalues.secvalues"},
//{"$project":{"deviceid":1,"values.minvalues.secvalues.lat":1,"values.minvalues.secvalues.min":1}} ,
{ "$match": { "values.minvalues.secvalues.priority": { "$eq": 1}}},
{ "$group": { "_id": "$deviceid" , "doc": { "$push": "$values.minvalues.secvalues" }}} ]).toArray();
If any can help that would be great full.
You can use $addFields to replace existing field. Since you have two levels of nested arrays you can use $map for outer and $filter for inner to check your condition:
db.col.aggregate([
{
$match: {
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z"
}
},
{
$addFields: {
values: {
$map: {
input: "$values",
as: "value",
in: {
minval: "$$value.minval",
minvalues: {
$filter: {
input: "$$value.minvalues",
as: "minvalue",
cond: {
$eq: [ "$$minvalue.secvalues.priority", 0 ]
}
}
}
}
}
}
}
}
])