mongo multiple count and devide result - mongodb

I have a mongodb collection as follow :
[
{
"_id" : ObjectId("5ba0e5a99e7537012371855a"),
"user_id" : 2,
"action" : 0,
"source" : 1,
"service" : "FootPlus",
"free" : false,
"source_detail" : {
"source_type" : "landing",
"source_id" : 2,
"promoter_id" : 1
},
"created_at" : ISODate("2018-09-18T11:46:49.000Z")
}
{
"_id" : ObjectId("5ba0e5cc9e7537013d57e37a"),
"user_id" : 2,
"action" : 1,
"service" : "FootPlus",
"source" : 0,
"created_at" : ISODate("2018-09-18T11:47:24.000Z"),
"source_detail" : {
"source_type" : "landing",
"source_id" : 2,
"promoter_id" : 1
}
}
]
I want to group by source_detail.promoter_id and count action = 0 and action = 1
and also divide count of action = 0 to count of action = 1 per promoter
so my result should be :
[
{
"promoter_id": 2
"action_0": 27,
"action_1": 9,
"devide": 3
},
{
"promoter_id": 3
"action_0": 18,
"action_1": 3,
"devide": 6
}
]
how can I achieve this result ?
thanks.

You can use below aggregation.
Something like
db.colname.aggregate([
{"$group":{
"_id":"$source_detail.promoter_id",
"action_0":{"$sum":{"$cond":[{"$eq":["$action",0]},1,0]}},
"action_1":{"$sum":{"$cond":[{"$eq":["$action",1]},1,0]}}
}},
{"$addFields":{"divide":{"$divide":["$action_0","$action_1"]}}}
])

Related

Mongo aggregation - Sorting using a field value from previous pipeline as the sort field

I have produced the below output using mongodb aggregation (including $group pipeline inside levelsCount field) :
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 8 }
]
}
{
"_id" : "2",
"name" : "Second",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 5 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 2 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "3",
"name" : "Third",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 3 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 2 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
Now, I need to sort these documents based on the levelNum and count fields of levelsCount array elements. I.e. If two documents both had the count 5 forlevelNum: 1 (level_One), then the sort goes to compare the count of levelNum: 2 (level_Two) field and so on.
I see how $sort pipeline would work on multiple fields (Something like { $sort : { level_One : 1, level_Two: 1 } }), But the problem is how to access those values of levelNum of each array element and set that value as a field name to do sorting on that. (I couldn't handle it even after $unwinding the levelsCount array).
P.s: The initial order of levelsCount array's elements may differ on each document and is not important.
Edit:
The expected output of the above structure would be:
// Sorted result:
{
"_id" : "2",
"name" : "Second",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 5 }, // "level_One's count: 5" is greater than "level_One's count: 1" in two other documents, regardless of other level_* fields. Therefore this whole document with "name: Second" is ordered first.
{ "_id" : "level_Two", "levelNum" : 2, "count" : 2 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "3",
"name" : "Third",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 3 }, // "level_Two's count" in this document exists with value (3) while the "level_Two" doesn't exist in the below document which mean (0) value for count. So this document with "name: Third" is ordered higher than the below document.
{ "_id" : "level_Three", "levelNum" : 3, "count" : 2 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 8 }
]
}
Of course, I'd prefer to have an output document in the below format, But the first problem is to sort all docs:
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "level_One" : 1 },
{ "level_Three" : 1 },
{ "level_Four" : 8 }
]
}
You can sort by levelNum as descending order and count as ascending order,
db.collection.aggregate([
{
$sort: {
"levelsCount.levelNum": -1,
"levelsCount.count": 1
}
}
])
Playground
For key-value format result of levelsCount array,
$map to iterate loop of levelsCount array
prepare key-value pair array and convert to object using $arrayToObject
{
$addFields: {
levelsCount: {
$map: {
input: "$levelsCount",
in: {
$arrayToObject: [
[{ k: "$$this._id", v: "$$this.levelNum" }]
]
}
}
}
}
}
Playground

Conditional check column value in mongodb

I have document like below
[
{
"_id" : ObjectId("601992e354787877866"),
"status" : 1,
"user_id" : 2,
},
{
"_id" : ObjectId("601992e354787877866"),
"status" : 2,
"user_id" : 2,
},
{
"_id" : ObjectId("601992e354787877866"),
"status" : 2,
"user_id" : 3,
},
{
"_id" : ObjectId("601992e354787877866"),
"status" : 3,
"user_id" : 4,
}
]
status column can have value 1, 2 or 3. I want to match user_id only if status column value is 2 and document having status other than 2 will come in result as it is.
I have tried with match query
db.collection.find({"status" : 1});
But it will only match the status
Now if my user id is 2 then output should b
[
{
"_id" : ObjectId("601992e354787877866"),
"status" : 1,
"user_id" : 2,
},
{
"_id" : ObjectId("601992e354787877866"),
"status" : 2,
"user_id" : 2,
},
{
"_id" : ObjectId("601992e354787877866"),
"status" : 3,
"user_id" : 4,
}
]
Here you can see document which status is 2 and user_id is not equal to 2 is not in expected output but for document which status is other than 2 will come is result
You could use the $or operator: https://mongoplayground.net/p/y9vR2PEGYdI
db.collection.find({
$or: [
{
status: 2,
user_id: 2
},
{
status: {
$ne: 2
}
}
]
})

Aggregate $group returns wrong values

I have a collection named donation
{
"_id" : 1,
"reqAmt" : 20,
"filled" : [
{
userId : 1,
depAmt : 5
}
]
},
{
"_id" : 2,
"reqAmt" : 20,
"filled" : []
},
Expected result
{
"_id" : 1,
"reqAmt" : 20,
"filledAmt" : 5
},
{
"_id" : 2,
"reqAmt" : 20,
"filledAmt" : 0
}
I tried with $unwind .but no luck. It always returns
{ "_id" : null, "filledAmt" : 0 }
Below are the codes
db.donation.aggregate([{$unwind:"$filled"},{$project:{"reqAmt":1,"filledAmt":1} },{$group:{"_id":null,"filledAmt":{"$sum":"$filled.depAmt"}}}])

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" }
}

mongoimport doesn't import the object properly

In Mongodb v2.2 When I try to import one simple json document file like this from my .json file into an empty collection I get 13 objects imported. Here is what I'm doing.
this is the data (I've shortened the field names to protect data):
[
{
"date" : ISODate("2012-08-01T00:00:00Z"),
"start" : ISODate("2012-08-01T00:00:00Z"),
"xxx" : 1,
"yyt" : 5,
"p" : 6,
"aam" : 20,
"dame" : "denon",
"33" : 10,
"xxt" : 8,
"col" : 3,
"rr" : [
{ "name" : "Plugin 1", "count" : 1 },
{ "name" : "Plugin 2", "count" : 1 },
{ "name" : "Plugin 3", "count" : 1 }
],
"xkx" : { "y" : 0, "n" : 1 },
"r" : { "y" : 0, "n" : 1 },
"po" : { "y" : 0, "n" : 1 },
"pge" : { "posts" : 0, "pages" : 1 },
"pol" : { "y" : 0, "n" : 1 },
"lic" : { "y" : 0, "n" : 1 },
"count" : 30,
"tx" : [
{ "zone" : -7, "count" : 1 }
],
"yp" : "daily",
"ons" : [
{ "version" : "9.6.8", "count" : 1 }
],
"ions" : [
{ "version" : "10.0.3", "count" : 1 }
]
}
]
with this command:
mongoimport --db development_report --collection xxx --username xxx --password xxx --file /Users/Alex/Desktop/daily2.json --type json --jsonArray --stopOnError --journal
I get this weired response:
Mon Sep 3 12:09:12 imported 13 objects
and this 13 new documents end up in the collection instead of one:
{ "_id" : ObjectId("5044114815e24c08bcdc988e") }
{ "_id" : ObjectId("5044114815e24c08bcdc988f"), "name" : "Plugin 1", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9890"), "name" : "Plugin 2", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9891"), "name" : "Plugin 3", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9892"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9893"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9894"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9895"), "posts" : 0, "pages" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9896"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9897"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9898"), "zone" : -7, "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9899"), "version" : "9.6.8", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc989a"), "version" : "10.0.3", "count" : 1 }
What am I doing wrong?
The problem you are having is with the two ISODate fields you have at the start of your document.
JSON does not have any "date" type, so it does not handle ISODate fields in your document. You would need to convert these like so:
[
{
"date" : { "$date" : 1343779200000 },
"start" : { "$date" : 1343779200000 },
...
And your import will work.
The reason this comes about is because MongoDB handles more types than are available in the JSON spec. You can see more information in the documentation. There is also an open ticket to make MongoImport handle all the formats MongoDB does here and more details here
This is really frustrating; I couldn't get anywhere fast with the import tool so I used the load() function within the mongo client to load a script which inserted my records.
> load('/Users/Alex/Desktop/daily.json');
I obviously had to modify the json file to include insert commands like so:
>db.mycollection.insert(
{ DOCUMENT 1 },
...
{ DOCUMENT N }
);
This is really late, but in case it can help anyone else - you should not be passing a JSON array. Simply list 1 JSON document per line, and each line will create a separate document. The file below would insert 2 documents:
{ "date" : { "$date": 1354320000000 }, "xxx" : 1, "yyt" : 5, ... }
{ "date" : { "$date": 1354320000000 }, "xxx" : 2, "yyt" : 6, ... }