Display mongodb multiple group by query results in a nested format - mongodb

Given the following dataset:
[
{
"account_id" : "1111"
"task_id" : "aaaa",
"workweek" : "20",
"hours": "18"
},
{
"account_id" : "1111"
"task_id" : "aaaa",
"workweek" : "20",
"hours": "12"
},
{
"account_id" : "1111"
"task_id" : "aaaa",
"workweek" : "21",
"hours": "10"
},
{
"account_id" : "1111"
"task_id" : "bbbb",
"workweek" : "21",
"hours": "5"
},
{
"account_id" : "2222"
"task_id" : "cccc",
"workweek" : "21",
"hours": "15"
}
]
I'd like to group the documents and have the results in the following format:
[
{
"account_id": "1111",
"tasks": [
{
"task_id": "aaaa",
"workweeks": [
{
"workweek": "20",
"total_hours": "30"
},
{
"workweek": "21",
"total_hours": "10"
}
]
},
{
"task_id": "bbbb",
"workweeks": [
{
"workweek": "21",
"total_hours": "5"
}
]
}
]
},
{
"account_id": "2222",
"tasks": [
{
"task_id": "cccc",
"workweeks": [
{
"workweek": "21",
"total_hours": "15"
}
]
},
]
}
]
My broken aggregation code is the following:
db.tasks.aggregate([
{"$group": {
"_id": {
"account_id": "$account_id",
"task_id": "$task_id",
"workweek": "$workweek",
},
"total_hours": {$sum: "$hours"}
}},
{"$group": {
"_id": "$_id.account_id",
"tasks": {
"$push": {
"task_id": "$_id.task_id",
"workweeks": {
"$push": {
"workweek": "$_id.workweek",
"total_hours": "$total_hours"
}
}
}
}
}}
]);
I receive this error:
"errmsg" : "exception: invalid operator '$push'",
And I assume it is because I can only $push once per $group block.
Any ideas on how to achieve the desired result?

Please try with one additional (middleware) "group-by" step:
db.tasks.aggregate([
{"$group": {
"_id": {
"account_id": "$account_id",
"task_id": "$task_id",
"workweek": "$workweek",
},
"total_hours": {$sum: "$hours"}
}},
{"$group": {
"_id": {
"$_id.account_id",
"$_id.task_id"
},
"workweeks": {
"$push": {
"workweek": "$_id.workweek",
"total_hours": "$total_hours"
}
}
}},
{"$group": {
"_id": "$_id.account_id",
"tasks": {
"$push": {
"task_id": "$_id.task_id",
"workweeks": "$workweeks"
}
}
}}
]);

Related

I am having difficulty in querying the follwing nested document using pymongo

If these are the following nested documents
[
{
"_id": 5,
"name": "Wilburn Spiess",
"scores": [
{
"score": 44.87186330181261,
"type": "exam"
},
{
"score": 25.72395114668016,
"type": "quiz"
},
{
"score": 63.42288310628662,
"type": "homework"
}
]
},
{
"_id": 6,
"name": "Jenette Flanders",
"scores": [
{
"score": 37.32285459166097,
"type": "exam"
},
{
"score": 28.32634976913737,
"type": "quiz"
},
{
"score": 81.57115318686338,
"type": "homework"
}
]
},
{
"_id": 7,
"name": "Salena Olmos",
"scores": [
{
"score": 90.37826509157176,
"type": "exam"
},
{
"score": 42.48780666956811,
"type": "quiz"
},
{
"score": 96.52986171633331,
"type": "homework"
}
]
}
]
I need to access the score part 'type' = exam.
Can somebody help me with this?
If you're asking for a python program to access the score, you can print them out like:
collection = mongo_connection['db']['collection']
documents = collection.find({})
for doc in documents:
for score in doc['scores']:
if score['type'] == 'exam':
print(f'Score: {score["score"]}')
If you are trying to retrieve only the scores and ignore the rest, I'd do an $unwind on the scores, $match on the type, and then project the fields you want (or not).
db.test.aggregate([
{
$unwind: '$scores'
},
{
$match: {
'scores.type': 'exam'
}
},
{
$project: {
'name': '$name',
'score': '$scores.score'
}
}
])
This would output:
{
"_id" : 5,
"name" : "Wilburn Spiess",
"score" : 44.8718633018126
},
{
"_id" : 6,
"name" : "Jenette Flanders",
"score" : 37.322854591661
},
{
"_id" : 7,
"name" : "Salena Olmos",
"score" : 90.3782650915718
}

Matching a value in subdocument array and matching array to _id

I have a collection of Users which looks like this:
{
"_id": {
"$oid": "5ff6298f2056ad02272d10f2"
},
"name": "Billy Bob",
"story": {
"posts": [
{"id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"},
{"id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"},
{"id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"}
],
"whoCanView": [{
"$oid": "5ff891bd749ed24316272317"
}, {
"$oid": "5ffc89392056ad02272d10f3"
}]
}
}
I want to get all users and their stories listed in an array which I call "contacts". The user currently querying the collection may only get results if their userId is in the "whoCanView" array.
This is what i have so far but doesn't seem to be working:
users.find({'story' : {
'$elementMatch' : {
'_id' : {'$in' : contacts},
ObjectID(userId) : {'$in' : 'story.whoCanView'}
}
}
});
Any guidance would be appreciated. Thanks!
ObjectID(userId) : {'$in' : 'story.whoCanView'}
Above line along with the usage of $elemMatch operator is wrong in your code. Try this query instead:
let contacts = [
ObjectId("5ff6298f2056ad02272d10f2"),
ObjectId("5ff6298f2056ad02272d10f3"),
ObjectId("5ff6298f2056ad02272d10f4")
];
let userId = ObjectId("5ff891bd749ed24316272317");
db.users.find(
{
"_id": { $in: contacts },
"story.whoCanView": userId
},
{
"strories": "$story.posts"
}
);
Output when userId is ObjectId("5ff891bd749ed24316272317"):
/* 1 createdAt:1/7/2021, 2:50:15 AM*/
{
"_id" : ObjectId("5ff6298f2056ad02272d10f2"),
"strories" : [
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
}
]
},
/* 2 createdAt:1/7/2021, 2:50:15 AM*/
{
"_id" : ObjectId("5ff6298f2056ad02272d10f3"),
"strories" : [
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
}
]
},
/* 3 createdAt:1/7/2021, 2:50:15 AM*/
{
"_id" : ObjectId("5ff6298f2056ad02272d10f4"),
"strories" : [
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
},
{
"id" : "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45"
}
]
}
Output when userId is ObjectId("5ff891bd749ed24316272318"):
[]
Test data in users collection:
[
{
"_id": ObjectId("5ff6298f2056ad02272d10f2"),
"name": "Billy Bob",
"story": {
"posts": [
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" }
],
"whoCanView": [
ObjectId("5ff891bd749ed24316272317"),
ObjectId("5ffc89392056ad02272d10f3")
]
}
},
{
"_id": ObjectId("5ff6298f2056ad02272d10f3"),
"name": "Billy Bob",
"story": {
"posts": [
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" }
],
"whoCanView": [
ObjectId("5ff891bd749ed24316272317"),
ObjectId("5ffc89392056ad02272d10f3")
]
}
},
{
"_id": ObjectId("5ff6298f2056ad02272d10f4"),
"name": "Billy Bob",
"story": {
"posts": [
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" }
],
"whoCanView": [
ObjectId("5ff891bd749ed24316272317"),
ObjectId("5ffc89392056ad02272d10f3")
]
}
},
{
"_id": ObjectId("5ff6298f2056ad02272d10f5"),
"name": "Billy Bob",
"story": {
"posts": [
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" },
{ "id": "b4a12557-4dfa-47eb-9f27-7bbcad0f4c45" }
],
"whoCanView": [
ObjectId("5ff891bd749ed24316272317"),
ObjectId("5ffc89392056ad02272d10f3")
]
}
}
]

Aggregate and grouping in Mongo

How do I get a summary count in Mongo. I have the following record structure and I would like to get a summary by date and status
{
"_id": "1",
"History": [
{
"id": "11",
"message": "",
"status": "send",
"resultCount": 0,
"createdDate": "",
"modifiedDate": ""
},
{
"id": "21",
"message": "",
"status": "skipped",
"resultCount": 0,
"createdDate": "",
"modifiedDate": ""
}
]
}
This is what I would like..
date x
status :
count : nn
This is my Mongo structure
Let's assume you have the following data in 'history' collection:
{
"_id": "1",
"History": [
{
"id": "21",
"message": "",
"status": "send",
"resultCount": 0,
"createdDate": "date1",
"modifiedDate": ""
},
{
"id": "22",
"message": "",
"status": "skipped",
"resultCount": 0,
"createdDate": "date1",
"modifiedDate": ""
},
{
"id": "23",
"message": "",
"status": "skipped",
"resultCount": 0,
"createdDate": "date2",
"modifiedDate": ""
},
{
"id": "24",
"message": "",
"status": "skipped",
"resultCount": 0,
"createdDate": "date2",
"modifiedDate": ""
}
]
}
You can design your query in the following way to get the desired summary.
db.history.aggregate([
{
$unwind:"$History"
},
{
$group:{
"_id":{
"createdDate":"$History.createdDate",
"status":"$History.status"
},
"createdDate":{
$first: "$History.createdDate"
},
"status":{
$first:"$History.status"
},
"count":{
$sum:1
}
}
},
{
$group:{
"_id":"$createdDate",
"createdDate":{
$first:"$createdDate"
},
"info":{
$push:{
"status":"$status",
"count":"$count"
}
}
}
},
{
$project:{
"_id":0
}
}
]).pretty()
It would result in the following:
{
"createdDate" : "date1",
"info" : [
{
"status" : "skipped",
"count" : 1
},
{
"status" : "send",
"count" : 1
}
]
}
{
"createdDate" : "date2",
"info" : [
{
"status" : "skipped",
"count" : 2
}
]
}
Aggregation stages details:
Stage I: The 'History' array is unwinded i.e. the array would be split and each element would create an individual document.
Stage II: The data is grouped on the basis of 'createdDate' and 'status'. In this stage, the count of status is also calculated.
Stage III: The data is further grouped on the basis of 'createdDate'
only
Stage IV: Eliminating non-required fields from the result

Join on Related properties of two Arrays

I have the following result. Below it would be possible to make a map joining the array each with its objectid.
{
"_id": ObjectId("597233b50e717e0585dbd94a"),
"createdAt": ISODate("2017-07-21T17:02:45.119+0000"),
"name": "cardoso",
"gender": "female",
"profile": [{
"profession": "master",
"_id": ObjectId("597233b50e717e0585dbd94b"),
"departament": ObjectId("597233b50e717e0585dbd94e")
},
{
"_id": ObjectId("59766719003e7d552fe40e8c"),
"profession": "master",
"departament": ObjectId("59766719003e7d552fe40e8b")
},
{
"_id": ObjectId("5976b8f99d8a4326c6bf1ae5"),
"profession": "master",
"departament": ObjectId("5974d8fe398e5b2fd433410f")
}
],
"Institution": {
"_id": ObjectId("597233b50e717e0585dbd94c"),
"cnpj": 64837134000144.0,
"deletedAt": false,
"departament": [{
"title": "cardoso",
"category": "Sub-17",
"_id": ObjectId("597233b50e717e0585dbd94e")
},
{
"sport": "Tênis",
"title": "novo",
"category": "Sub-12",
"_id": ObjectId("59766719003e7d552fe40e8b")
},
{
"_id": ObjectId("5974d8fe398e5b2fd433410f"),
"category": "Intercâmbio",
"title": "testeJJJ",
"sport": "natação"
}
]
}
}
I need to do the following result. I did not want to have to manipulate the result in the node.
{
"sport": "Tênis",
"profession": "master",
"title": "novo",
"category": "Sub-12",
"_id": ObjectId("59766719003e7d552fe40e8b")
}
I already tended to do something but the query ends up getting very big
The basic premise here is to "lookup" the content in the other array whilst processing via $map.
This is either done via $indexOfArray with MongoDB 3.4:
db.collection.aggregate([
{ "$addFields": {
"Institution": {
"departament": {
"$map": {
"input": "$Institution.departament",
"as": "d",
"in": {
"sport": "$$d.title",
"profession": {
"$arrayElemAt": [
"$profile.profession",
{ "$indexOfArray": [ "$profile.departament", "$$d._id" ] }
]
},
"title": "$$d.title",
"category": "$$d.category"
}
}
}
}
}}
])
In that first index we look for the "index position" from the "profile" array that matches the current value of _id on the specified field. Then extract the data at that index via $arrayElemAt.
Or using $filter and the $arrayElemAt "the other way around" with MongoDB 3.2:
db.collection.aggregate([
{ "$addFields": {
"Institution": {
"departament": {
"$map": {
"input": "$Institution.departament",
"as": "d",
"in": {
"sport": "$$d.title",
"profession": {
"$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": "$profile",
"as": "p",
"cond": { "$eq": [ "$$p.departament", "$$d._id" ] }
}
},
"as": "p",
"in": "$$p.profession"
}},
0
]
},
"title": "$$d.title",
"category": "$$d.category"
}
}
}
}
}}
])
In which case the $filter reduces the array content in "profile" down to only matching elements, which should be just one. This is then extracted at the 0 index by $arrayElemAt.
Same result in either case:
{
"_id" : ObjectId("597233b50e717e0585dbd94a"),
"createdAt" : ISODate("2017-07-21T17:02:45.119Z"),
"name" : "cardoso",
"gender" : "female",
"profile" : [
{
"profession" : "master",
"_id" : ObjectId("597233b50e717e0585dbd94b"),
"departament" : ObjectId("597233b50e717e0585dbd94e")
},
{
"_id" : ObjectId("59766719003e7d552fe40e8c"),
"profession" : "master",
"departament" : ObjectId("59766719003e7d552fe40e8b")
},
{
"_id" : ObjectId("5976b8f99d8a4326c6bf1ae5"),
"profession" : "master",
"departament" : ObjectId("5974d8fe398e5b2fd433410f")
}
],
"Institution" : {
"_id" : ObjectId("597233b50e717e0585dbd94c"),
"cnpj" : 64837134000144.0,
"deletedAt" : false,
"departament" : [
{
"sport" : "cardoso",
"profession" : "master",
"title" : "cardoso",
"category" : "Sub-17"
},
{
"sport" : "novo",
"profession" : "master",
"title" : "novo",
"category" : "Sub-12"
},
{
"sport" : "testeJJJ",
"profession" : "master",
"title" : "testeJJJ",
"category" : "Intercâmbio"
}
]
}
}

Mongodb query select embedded document

I have following Mongodb document. Would like to fetch document where participant = 'xxx' and (message.touserid = 'xxx' or message.fromuserid = 'xxx').
I am using following query but it is returning all messages instead of just one. Could you please let me know how to achieve this result?
{ "$and" : [ { "participants" : { "$regex" : "56d314a8e4b04d7f98cfd0c6"} , "$or" : [ { "messages.touserId" : "56d314a8e4b04d7f98cfd0c6"} , { "messages.formuserId" : "56d314a8e4b04d7f98cfd0c6"}]}]} fields: { "_id" : "0" , "product" : "0" , "participants" : "0" , "messages" : "0"}
{
"_id": {
"$oid": "574eb878027520c2158268d6"
},
"_class": "com.idearealty.product.shopchat.persistence.model.Discussion",
"participants": "56d314a8e4b04d7f98cfd0c6,56d5d48ee4b0cc330f512a47,56d9d599e4b0cc330f512aaa,57130299e4b08c554c1092c7,56841002eceefce22f455c7f",
"messages": [
{
"_id": {
"$oid": "574eb874027520c2158268d2"
},
"formuserId": "56841002eceefce22f455c7f",
"fromuser": "9674642375",
"touserId": "56d314a8e4b04d7f98cfd0c6",
"touser": "debopam_r",
"message": "Creating Discussion",
"isMute": false,
"index": 1,
"createDate": {
"$date": "2016-06-01T10:27:00.500Z"
},
"lastModifiedDate": {
"$date": "2016-06-01T10:27:00.501Z"
},
"createdBy": "9674642375",
"lastModifiedBy": "9674642375"
},
{
"_id": {
"$oid": "574eb875027520c2158268d3"
},
"formuserId": "56841002eceefce22f455c7f",
"fromuser": "9674642375",
"touserId": "56d5d48ee4b0cc330f512a47",
"touser": "Raushan",
"message": "Creating Discussion",
"isMute": false,
"index": 2,
"createDate": {
"$date": "2016-06-01T10:27:01.295Z"
},
"lastModifiedDate": {
"$date": "2016-06-01T10:27:01.295Z"
},
"createdBy": "9674642375",
"lastModifiedBy": "9674642375"
},
{
"_id": {
"$oid": "574eb875027520c2158268d4"
},
"formuserId": "56841002eceefce22f455c7f",
"fromuser": "9674642375",
"touserId": "56d9d599e4b0cc330f512aaa",
"touser": "anirbanshop1",
"message": "Creating Discussion",
"isMute": false,
"index": 3,
"createDate": {
"$date": "2016-06-01T10:27:01.962Z"
},
"lastModifiedDate": {
"$date": "2016-06-01T10:27:01.962Z"
},
"createdBy": "9674642375",
"lastModifiedBy": "9674642375"
},
{
"_id": {
"$oid": "574eb876027520c2158268d5"
},
"formuserId": "56841002eceefce22f455c7f",
"fromuser": "9674642375",
"touserId": "57130299e4b08c554c1092c7",
"touser": "dummyshop",
"message": "Creating Discussion",
"isMute": false,
"index": 4,
"createDate": {
"$date": "2016-06-01T10:27:02.574Z"
},
"lastModifiedDate": {
"$date": "2016-06-01T10:27:02.574Z"
},
"createdBy": "9674642375",
"lastModifiedBy": "9674642375"
}
],
"messageCount": 4,
"createDate": {
"$date": "2016-06-01T10:27:04.041Z"
},
"lastModifiedDate": {
"$date": "2016-06-01T10:27:04.041Z"
},
"createdBy": "9674642375",
"lastModifiedBy": "9674642375"
}
As this is a complex match on elements - $elemMatch cannot be used in this case,
so aggregation framework is a helper.
var match = {
$match : {
participants : /56d314a8e4b04d7f98cfd0c6/
}
}
var unwind = {
$unwind : "$messages"
}
var matchSecond = {
$match : {
$or : [{
"messages.touserId" : "56d314a8e4b04d7f98cfd0c6"
}, {
"messages.formuserId" : "56d314a8e4b04d7f98cfd0c6"
}
]
}
}
var projection = {
$project : {
_id : 0,
messages : 1
}
}
db.deb.aggregate([match, unwind, matchSecond, projection])
and output:
{
"messages" : {
"_id" : {
"oid" : "574eb874027520c2158268d2"
},
"formuserId" : "56841002eceefce22f455c7f",
"fromuser" : "9674642375",
"touserId" : "56d314a8e4b04d7f98cfd0c6",
"touser" : "debopam_r",
"message" : "Creating Discussion",
"isMute" : false,
"index" : 1.0,
"createDate" : {
"date" : "2016-06-01T10:27:00.500Z"
},
"lastModifiedDate" : {
"date" : "2016-06-01T10:27:00.501Z"
},
"createdBy" : "9674642375",
"lastModifiedBy" : "9674642375"
}
}