Multiple groups and project using mongoose - node.js

I'm trying display aggregated data by a time range in Mongoose. My Data is like:
{company: "1" createdTime:"2017-01-01"}
{company: "1" createdTime:"2017-01-01"}
{company: "1" createdTime:"2017-01-02"}
{company: "2" createdTime:"2017-01-02"}
{company: "2" createdTime:"2017-01-03"}
{company: "3" createdTime:"2017-01-03"}
{company: "3" createdTime:"2017-01-03"}
I have created a query like:
{
"aggregate": [{
"$project": {
"frequency": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$createdTime"
}
}
}
}, {
"$group": {
"_id": {
"date": "$frequency"
},
"count": {
"$sum": 1
}
}
}
]
}
and returns:
[
{
"_id": {
"date": "2017-01-01"
},
"count": 2
},
{
"_id": {
"date": "2017-01-02"
},
"count": 2
},
{
"_id": {
"date": "2017-01-03"
},
"count": 3
}
]
Which gives me the count for the overall occurrences for that particular date, which is fine.
I'm wanting to further break that down to give me the sum of occurrences per company for that date which should look something like this:
[
{
"_id": {
"date": "2017-01-01",
"companies": [{
"company": 1,
"count": 2
}]
},
"count": 2
},
{
"_id": {
"date": "2017-01-02",
"companies": [{
"company": 1,
"count": 1
},{
"company": 2,
"count": 1
}]
},
"count": 2
},
{
"_id": {
"date": "2017-01-03",
,
"companies": [{
"company": 2,
"count": 1
},{
"company": 3,
"count": 2
}]]
},
"count": 3
}
]
I've tried all sort of combinations but always get errors when i put in a second group and i can't work out how to do it?
EDIT
I wasn't able to use examples from the comments or they didn't work for my use case however, combining this information i came up with this:
{
"aggregate": [{
"$group": {
"_id": {
"company": "$company",
"date": {
"$dayOfMonth": "$createdTime"
},
"month": {
"$month": "$createdTime"
},
"year": {
"$year": "$createdTime"
}
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": "$_id.company",
"events": {
"$push": {
"date": "$_id.date",
"month": "$_id.month",
"year": "$_id.year",
"count": "$count"
}
},
"totalEvents": {
"$sum": "$count"
}
}
}
]
}
which renders some results like:
[
{
"_id": "1",
"dateCount": [
{
"date": 3,
"month": 12,
"year": 2017,
"count": 40
},
{
"date": 10,
"month": 12,
"year": 2017,
"count": 8
}
]
}
]
Ideally the date would be yyyy-mm-dd but using the full date in the query would include the time also resulting in an entry for each time not date. I guess this will do for now, i can easily create a date from this albeit it's a little more cumbersome but it'll do until anything better comes along.
Edit 2
[{
"_id": ObjectId("5a38a03b33ccd28fb87f6dc9"),
"createdBy": ObjectId("5a2e203fa567425b2cb411fc"),
"appId": ObjectId("5a2e203fa567425b2cb411fb"),
"eventName": "event",
"userName": "person",
"userEmail": "company__29",
"organisation": "company",
"module": "module",
"createdTime": ISODate("2017-12-02T17:07:25.000Z")
"__v": 0
}
]
Query:
{
"aggregate": [{
"$project": {
"frequency": {
"$dateToString": {
"format": "%U",
"date": "$createdTime"
}
}
}
}, {
"$group": {
"_id": {
"frequency": "$frequency",
"organisation": "$organisation"
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": {
"date": "$_id.frequency"
},
"count": {
"$sum": 1
},
"companies": {
"$push": {
"organisation": "$organisation",
"count": "$count"
}
}
}
}
]
}

Related

How to set up aggregation pipeline for specific use case in MongoDB

`I have two collections in MongoDB. One is users and the other is companies. I need to be able to populate the users.endpoints with the endpoints from companies that match ObjectID's which are in the user.endPoints property. It is a bit hard for me to wrap my head around considering there are two steps.
This is the companies collection.
{
"_id": {
"$oid": "63ed39162bfc2cf8065b76cf"
{
"_id": {
"$oid": "63ed39162bfc2cf8065b76cf"
},
"companyName": "Sowegatel",
"amount": 0,
"signalwireSid": "id",
"numbers": [
{
"number": "+12068133580",
"_id": {
"$oid": "63ed39c72bfc2cf8065b76f0"
},
"createdAt": {
"$date": {
"$numberLong": "1676491207931"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1676491207931"
}
}
}
],
"endPoints": [
{
"userName": "4009",
"_id": "fb1d0ef9-c713-400e-a3c9-cbc6823aaf57"
},
{
"userName": "4019",
"_id": "506e710d-14a6-4345-bc89-8488af4cabe4"
},
{
"userName": "4020",
"_id": "c80bd1ab-ca8d-4649-9d35-56d64ef8fab5",
"type": "sip_endpoint"
}
],
"__v": 6
} },
"companyName": "Sowegatel",
"amount": 0,
"signalwireSid": "id",
"numbers": [
{
"number": "+12068133580",
"_id": {
"$oid": "63ed39c72bfc2cf8065b76f0"
},
"createdAt": {
"$date": {
"$numberLong": "1676491207931"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1676491207931"
}
}
}
],
"endPoints": [
{
"userName": "4009",
"_id": "fb1d0ef9-c713-400e-a3c9-cbc6823aaf57"
},
{
"userName": "4019",
"_id": "506e710d-14a6-4345-bc89-8488af4cabe4"
},
{
"userName": "4020",
"_id": "c80bd1ab-ca8d-4649-9d35-56d64ef8fab5",
"type": "sip_endpoint"
}
],
"__v": 6
}
This is the users collection.
{
"_id": {
"$oid": "63ed39162bfc2cf8065b76d2"
},
"firstName": "mark",
"lastName": "thomas",
"email": "mt#st.com",
"password": "",
"companyId": {
"$oid": "63ed39162bfc2cf8065b76cf"
},
"active": true,
"role": "companyAdmin",
"tokens": [],
"__v": 2,
"endpoints": [
// I need to populate to endpoints here!!!! //
"c80bd1ab-ca8d-4649-9d35-56d64ef8fab5"
]
}
I can do a single step aggregation, but this is a little complicated for me.`

Aggregate array field and text field in a collection mongo Node

I want to merge two document in same collection by grouping two fields, one field is just a normal text field (WorkFlow), and another one is an array field (Service), i was able to group the normal field(WorkFlow) but am stuck to group the array field, please help me I have a short in time to do it, I've gone through some article but I couldn't find any solution, please help me to overcome this..
** i need to get the output, in any method if its not in aggregate then any other solutions
I have the document in collection like below:
[{
"_id": {
"$oid": "62f9fd54259335683bc54ac3"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": {
"$oid": "62f9f5f9e26c8912b86c61b8"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b538a216f20e1b00ad46"
},
{
"value": "6235b52ea216f20e1b00ad43"
}
],
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}]
and I need an output structure like below:
[{
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"Assets": [
{
"_id": "62f9fd54259335683bc54ac3",
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": "62f9f5f9e26c8912b86c61b8",
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}
]
}]
I think this pipeline will help you get the output you want:
db.collection.aggregate([
{
"$unwind": "$Service"
},
{
"$sort": {
"Service.value": 1
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"WorkFlow": "$WorkFlow",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
},
"Service": {
"$push": "$Service"
}
}
},
{
"$project": {
"_id": "$_id._id",
"WorkFlow": "$_id.WorkFlow",
"LevelArray": "$_id.LevelArray",
"AssetServiceClassification": "$_id.AssetServiceClassification",
"Service": "$Service"
}
},
{
"$group": {
"_id": {
"WorkFlow": "$WorkFlow",
"Service": "$Service"
},
"Assets": {
"$push": {
"_id": "$_id",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
}
}
}
}
])

How to get most repeated string array in Pymongo?

How can I get most repeated value for gender and age respectively?
My data:
[{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe7"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "female","age": "0-10"},
{"Id": 2,"gender": "female","age": "20-30"}]
},
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe8"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "male","age": "0-10"},
{"Id": 2,"gender": "female","age": "30-40"}]
} ,
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fe9"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 1,"gender": "male","age": "10-15"},
{"Id": 2,"gender": "female","age": "30-40"},
{"Id": 3,"gender": "male","age": "0-10"}]
},
{ "_id": ObjectId("5dff27c0ac2d1547d87a1fea"), "time": "2019-12-20 21:09:53",
"object": [{"Id": 2,"gender": "male","age": "40-50"},
{"Id": 3,"gender": "male","age": "0-10"},
{"Id": 4,"gender": "male","age": "0-10"}]
}]
I have written below query,
mongo.db.xyz.aggregate([
{ "$unwind" : "$object"},
{"$group" : {"_id" : "$object.Id","_gen":{"$push":"$object.gender"},"_age":{"$push":"$object.age"}}},
{ "$project": { "_id" : "$_id", "gender":"$_gen","age":"$_age"}}
])
Below is the result I am getting,
[{"_id": 3,"age": ["0-10","0-10"],"gender": ["male","male"]},
{"_id": 2,"age": ["20-30","30-40","30-40","40-50"],"gender": ["female","female","female","male"]},
{"_id": 4,"age": ["0-10"],"gender": ["male"]},
{"_id": 1,"age": ["0-10","0-10","10-15"],"gender": ["female","male","male"]}
]
But I want the output to be ,
[{"_id": 3,"age": "0-10","gender": "male"},
{"_id": 2,"age": "30-40","gender": "female"},
{"_id": 4,"age": "0-10","gender": "male"},
{"_id": 1,"age": "0-10","gender": "male"}
]
Thinking about this problem I realized it was not so simple to get the mode of some fields independently in an array of objects with just one db query. To solve that I created a query to do it generically, based on your sample data.
db.collection.aggregate([
{
$unwind: {
path: "$arr"
}
},
{
$project: {
arr: {
$objectToArray: "$arr"
}
}
},
{
$unwind: {
path: "$arr"
}
},
{
$group: {
_id: {
_id: "$_id",
k: "$arr.k",
v: "$arr.v"
},
count: {
$sum: 1
}
}
},
{
$sort: {
count: -1
}
},
{
$group: {
_id: {
_id: "$_id._id",
k: "$_id.k"
},
v: {
$first: "$_id.v"
},
count: {
$first: "$count"
}
}
},
{
$group: {
_id: {
_id: "$_id._id"
},
arr: {
$push: {
k: "$_id.k",
v: {
mode: "$v",
count: "$count"
}
}
}
}
},
{
$project: {
arr: {
$arrayToObject: "$arr"
}
}
}
])
The secret to do that is to use the $objectToArray and the $arrayToObject operations, along with the $unwind and the correct $group stages. If you need a more detailed response on any stage please ask in the comments.
The output of the sample data is:
[
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fea")
},
"arr": {
"Id": {
"count": 1,
"mode": 3
},
"age": {
"count": 2,
"mode": "0-10"
},
"gender": {
"count": 3,
"mode": "male"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe7")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "0-10"
},
"gender": {
"count": 2,
"mode": "female"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe9")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "30-40"
},
"gender": {
"count": 2,
"mode": "male"
}
}
},
{
"_id": {
"_id": ObjectId("5dff27c0ac2d1547d87a1fe8")
},
"arr": {
"Id": {
"count": 1,
"mode": 2
},
"age": {
"count": 1,
"mode": "30-40"
},
"gender": {
"count": 1,
"mode": "female"
}
}
}
]
After running it for a collection that has an array of objects named "arr" (edit the query if in your collection it has a different name) it will return the mode value and the number of occurrences of that value for each field. Objects that has that field unset will be not considered, but the ones with "null" will.

nodejs + mongodb error exception: FieldPath 'progress' doesn't start with $

I'm trying to modify the second pipeline from this query (which I got from here nodejs + mongoose - query aggregate
db.todos.aggregate([
{
"$group": {
"_id": "$pic",
"open_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "open" ] }, 1, 0 ]
}
},
"progress_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "progress" ] }, 1, 0 ]
}
},
"done_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "done" ] }, 1, 0 ]
}
},
"archive_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "archive" ] }, 1, 0 ]
}
}
}
},
{
"$group": {
"_id": "$_id",
"detail": {
"$push": {
"name": "open",
"$todos": "$open_count"
},
"$push": {
"name": "progress",
"$todos": "$progress_count"
},
"$push": {
"name": "done",
"$todos": "$done_count"
},
"$push": {
"name": "archive",
"$todos": "$archive_count"
}
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}
])
I want this kind of JSON structure so I can put it on google chart, which the format is like this:
[
{
"pic": "A",
"detail": [
{
"name": "open",
"todos": 2
},
{
"name": "progress",
"todos": 1
},
{
"name": "done",
"todos": 8
},
{
"name": "archive",
"todos": 20
}
],
"pic": "B",
"detail": [
{
"name": "open",
"todos": 5
},
{
"name": "progress",
"todos": 2
},
{
"name": "done",
"todos": 5
},
{
"name": "archive",
"todos": 10
}
],
}
]
But I got this error
exception: FieldPath 'progress' doesn't start with $
Try with this aggregation query:
db.todos.aggregate([
{
"$group": {
"_id": {
"pic": "$pic",
"name": "$status"
},
"todos": {
"$sum": 1
}
}
},
{
"$project": {
"_id": 0,
"pic": "$_id.pic",
"detail": {
"name": "$_id.name",
"todos": "$todos"
}
}
},
{
"$group": {
"_id": "$pic",
"detail": {
"$push": "$detail"
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}])

Aggregation with Mongodb using mongoose

Hey im trying some aggregation on mongodb using moongose:
I got this data:
[
{
"school": "1",
"preferences": [
{
"person": "X",
"color": "A"
},
{
"person": "X",
"color": "B"
},
{
"person": "Y",
"color": "A"
}
]
},
{
"school": "2",
"preferences": [
{
"person": "Z",
"color": "A"
},
{
"person": "Y",
"color": "C"
}
]
}
]
I think the data explaisn it self, What i want to get as result is,
when i query for the school that matches '1'. i would like to get this result:
[
{
"_id": "X",
"colors": [
"A",
"B"
]
},
{
"_id": "Y",
"colors": ["A"]
}
]
I used aggregation before, but i cant figure to get this result.
Check this aggregation query :
db.collectionName.aggregate({
"$match": {
"school": "1"
}
}, {
"$unwind": "$preferences"
}, {
"$group": {
"_id": "$preferences.person",
"colors": {
"$addToSet": "$preferences.color" // $addToSet used here only to add distinct color
}
}
})
Edit If you want count the number of color then used group with sum as below :
db.collectionName.aggregate({
"$match": {
"school": "1"
}
}, {
"$unwind": "$preferences"
}, {
"$group": {
"_id": "$preferences.color",
"appears": {
"$sum": 1
}
}
})
EDIT
As per your new requirement you should do following aggregation:
db.collectionName.aggregate({
"$unwind": "$preferences"
},
{
"$group": {
"_id": {
"person": "$preferences.person",
"color": "$preferences.color"
},
"count": {
"$sum": 1
}
}
},
{
"$group": {
"_id": "$_id.person",
"colors": {
"$push": {
"color": "$_id.color",
"count": "$count"
}
}
}
},
{
"$project": {
"_id": 0,
"person": "$_id",
"colors": 1
}
}).pretty()

Resources