MongoDB nested aggregation lookup - javascript

I wont to connect two collection so as to workouts._id had an assigned value exercise.name.
first collection "workouts":
{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde3"
},
"id_user": {
"$oid": "60fd50ee1e9d064a5cee0bec"
},
"date": {
"$date": "2021-05-20T22:00:00.000Z"
},
"name": "wielki bicek",
"workouts": [{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde5"
},
"weight": "30",
"repeating": "10"
}]
}, {
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde9"
},
"weight": "30",
"repeating": "10"
}, {
"_id": {
"$oid": "60ffbd531b1cba41e8a7fdea"
},
"weight": "30",
"repeating": "10"
}]
}],
"__v": 0
}
second collection "exercises":
{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"name": "Wyciskanie sztangi",
"image": "uploads\\2021-07-03T15-12-24.173Z-test.gif",
"type": "force",
"groupMuscle": {
"$oid": "60daf099c1aaa20b4cfdf0fa"
},
"instruction": "Opierając tułów znazewnątrz.",
"__v": 0
}
I have one collection that I want to run nested aggregation on this, but I don't know how to run this operation.

$unwind unwind workouts
$lookup lookpup exercises
$addFileds get first item
$addFileds get only name field
$project remove useless field
$group group by original id
db.workouts.aggregate([
{
"$unwind": "$workouts"
},
{
"$lookup": {
"from": "exercises",
pipeline: [
{
"$project": {
name: 1
}
}
],
"localField": "workouts._id",
"foreignField": "_id",
"as": "names"
}
},
{
"$addFields": {
"workouts.i": {
"$first": "$names"
}
}
},
{
"$addFields": {
"workouts.name": "$workouts.i.name"
}
},
{
"$project": {
"names": 0,
"workouts.i": 0
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"id_user": "$id_user",
"date": "$date",
"name": "$name"
},
"workouts": {
$push: "$workouts"
}
}
}
])
mongoplayground

Related

MongoDB : - Merge Two Array with same key

I have 3 collections, users, attendances, shifts.
I am trying to get attendances and shifts with the help of users
I have a date in shifts and the Date key in attendances and I am trying to merge both data into 1 as per date, and if not present then that key should be none
Below is the query I tried it is working to some extent but not got the final result
User.aggregate([
{ $sort: { workerId: 1 } },
{
$lookup: {
from: "shifts",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
date: {
$gte: new Date(fromDate),
$lte: new Date(toDate),
},
},
},
{
$project: {
date: 1,
shiftCode: 1,
},
},
{
$sort: {
date: 1,
},
},
],
as: "shifts",
},
},
{
$project: {
_id: 1,
workerId: 1,
shiftListData: "$shifts",
},
},
{
$lookup: {
from: "attendances",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
Date: { $gte: new Date(fromDate), $lte: new Date(toDate) },
},
},
{
$project: {
inTime: 1,
name: 1,
Date: 1,
},
},
],
as: "attendances",
},
},
]);
OutPut
[
{
"workerId": "1005",
"shiftListData": [
{
"_id": "63875e8182ebbe13ee9531d4",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d407",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf30",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d409",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
],
"attendances": [
{
"_id": "61307cd385b5055a15cec159",
"Date": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name2"
},
{
"_id": "63b236ef3980cffaf7715d62",
"inTime": "2023-01-02T07:14:08.000Z",
"Date": "2023-01-02T00:00:00.000Z",
"name": "name2"
}
]
},
{
"workerId": "1006",
"shiftListData": [
{
"_id": "63875e8182ebbe13ee9531d2",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d403",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf39",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b277a2f6a8eccb2d95d400",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
],
"attendances": [
{
"_id": "61307cd385b5055a15cec158",
"Date": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name"
},
{
"_id": "63b236ef3980cffaf7715d69",
"inTime": "2023-01-02T07:14:08.000Z",
"Date": "2023-01-02T00:00:00.000Z",
"name": "name"
}
]
}
]
I want to merge shiftListData and attendances as per date into one array
Example : -
[
{
"workerId": "1005",
"newData": [
{
"_id": "63875e8182ebbe13ee9531d4",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z",
"attendanceId": "61307cd385b5055a15cec159",
"attendanceDate": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name2"
},
{
"_id": "63b277a2f6a8eccb2d95d407",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf30",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z",
"attendanceId": "63b236ef3980cffaf7715d62",
"inTime": "2023-01-02T07:14:08.000Z",
"attendanceDate": "2023-01-02T00:00:00.000Z",
"name": "name2"
},
{
"_id": "63b277a2f6a8eccb2d95d409",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
]
},
{
"workerId": "1006",
"newData": [
{
"_id": "63875e8182ebbe13ee9531d2",
"shiftCode": "HOBGS_1100",
"date": "2022-12-31T00:00:00.000Z",
"attendanceId": "61307cd385b5055a15cec158",
"attendanceDate": "2022-12-31T00:00:00.000Z",
"inTime": "2022-12-31T11:16:10.000Z",
"name": "name"
},
{
"_id": "63b277a2f6a8eccb2d95d403",
"shiftCode": "WO",
"date": "2023-01-01T00:00:00.000Z"
},
{
"_id": "63b27787f6a8eccb2d95cf39",
"shiftCode": "HOBGS_1100",
"date": "2023-01-02T00:00:00.000Z",
"attendanceId": "63b236ef3980cffaf7715d69",
"inTime": "2023-01-02T07:14:08.000Z",
"attendanceDate": "2023-01-02T00:00:00.000Z",
"name": "name"
},
{
"_id": "63b277a2f6a8eccb2d95d400",
"shiftCode": "HOBGS_1100",
"date": "2023-01-03T00:00:00.000Z"
}
]
}
]
One option is to add one more step to your pipeline which finds for each shift item its matching item in attendances using $filter:
{$set: {
shiftListData: {$map: {
input: "$shiftListData",
as: "shift",
in: {$mergeObjects: [
"$$shift",
{$ifNull: [
{$first: {
$filter: {
input: "$attendances",
cond: {$eq: ["$$this.Date", "$$shift.date"]}
}
}},
{}
]}
]}
}},
attendances: "$$REMOVE"
}}
See how it works on the playground example

how to update in nested array in MongoDB

I'm trying to update seat_status but i'm new to Mongodb I try to use aggregate function but it's not modifying original document then I try $ operator but not worked as i expected this is my collection.......................................................................................................................................................................................
{
"_id": {
"$oid": "62b0c3342f155a7ad94cc81c"
},
"totalShowByDay": "2",
"totalShowDays": 4,
"movieId": {
"$oid": "62b04c782828dd04f0d1c1ad"
},
"screenId": {
"$oid": "62b04b8e2828dd04f0d1c1ac"
},
"createdAt": 1655751476553,
"showId": {
"$oid": "62b0c3342f155a7ad94cc6db"
},
"show_seats": [{
"showByDate": {
"ShowDate": "2022-06-20",
"shows": [{
"showTime": "2022-06-20T10:00",
"showSeats": [{
"_id": {
"$oid": "62b0c3342f155a7ad94cc6dc"
},
"seat_number": "1",
"tag_name": "A",
"seat_status": false,
"user_id": false,
"price": "110",
"seats_category": "CLASSIC",
"show_time": "2022-06-20T10:00"
}, {
"_id": {
"$oid": "62b0c3342f155a7ad94cc6dd"
},
"seat_number": "2",
"tag_name": "A",
"seat_status": false,
"user_id": false,
"price": "110",
"seats_category": "CLASSIC",
"show_time": "2022-06-20T10:00"
this is the code i try to modify......................................................................................................................................................
db.seats.updateOne({"show_seats.$.showByDate.shows.$.showSeats.$._id:ObjectId("62b0c3342f155a7ad94cc81c")},{$set:{"show_seats.$.showByDate.shows.$.showSeats.$.seat_status":1}})

MongoDb LEFT Join return data not same like MySQl Left JOIN

I have two MongoDB Tabel
App Tabel
{
"_id": {
"$oid": "5cbac9172d962331401d0e48"
},
"appInstallCount": {
"$numberInt": "6"
},
"status": {
"$numberInt": "1"
},
"appName": "PC Access Log",
"appInfo": "You can view users who access your PC",
"appImageUrl": "assets/images/app/userlog-app-thismypc.png",
"userID": "5c3d6a25227a1d01eba0afc9",
"version": "1",
"released_date": {
"$date": {
"$numberLong": "1555745047247"
}
},
"lastUpdate_date": {
"$date": {
"$numberLong": "1555745047247"
}
},
"__v": {
"$numberInt": "0"
}
}{
"_id": {
"$oid": "5cbd300316e4651c78167478"
},
"appInstallCount": {
"$numberInt": "0"
},
"status": {
"$numberInt": "1"
},
"appName": "PC Access Log TEST",
"appInfo": "You can view users who access your PC",
"appImageUrl": "assets/images/app/userlog-app-thismypc.png",
"userID": "5c3d6a25227a1d01eba0afc9",
"version": "1",
"released_date": {
"$date": {
"$numberLong": "1555745047247"
}
},
"lastUpdate_date": {
"$date": {
"$numberLong": "1555745047247"
}
},
"__v": {
"$numberInt": "0"
}
}
and UserAndApps Tabel
{
"_id": {
"$oid": "5cbd327294877a70b510c8b6"
},
"status": {
"$numberInt": "1"
},
"userID": {
"$oid": "5cbd2d3e0afcb16efa795563"
},
"appID": {
"$oid": "5cbac9172d962331401d0e48"
},
"date": {
"$date": {
"$numberLong": "1555903090765"
}
},
"__v": {
"$numberInt": "0"
}
}{
"_id": {
"$oid": "5cbd327394877a70b510c8b7"
},
"status": {
"$numberInt": "1"
},
"userID": {
"$oid": "5cbd2d3e0afcb16efa795563"
},
"appID": {
"$oid": "5cbd300316e4651c78167478"
},
"date": {
"$date": {
"$numberLong": "1555903091265"
}
},
"__v": {
"$numberInt": "0"
}
}{
"_id": {
"$oid": "5cbd329a94877a70b510c8b9"
},
"status": {
"$numberInt": "1"
},
"userID": {
"$oid": "5c3d6a25227a1d01eba0afc9"
},
"appID": {
"$oid": "5cbac9172d962331401d0e48"
},
"date": {
"$date": {
"$numberLong": "1555903130918"
}
},
"__v": {
"$numberInt": "0"
}
}{
"_id": {
"$oid": "5cbd329b94877a70b510c8ba"
},
"status": {
"$numberInt": "1"
},
"userID": {
"$oid": "5c3d6a25227a1d01eba0afc9"
},
"appID": {
"$oid": "5cbd300316e4651c78167478"
},
"date": {
"$date": {
"$numberLong": "1555903131796"
}
},
"__v": {
"$numberInt": "0"
}
}
I use this code to join above two tabels
[
{
'$lookup': {
'from': 'userandapps',
'localField': '_id',
'foreignField': 'appID',
'as': 'appData'
}
}, {
'$unwind': {
'path': '$appData',
'preserveNullAndEmptyArrays': true
}
}, {
'$match': {
'appData.userID': new ObjectId('5cbd2d3e0afcb16efa795563')
}
}
]
The issue is it only returns that fill appData array, but I need that appData array to fill and empty both stages like in MYSQL LEFT JOIN. if 'appData.userID': new ObjectId('5cbd2d3e0afcb16efa795563') not belong one of document. it also returns with an empty appData array. I tried many ways but I did not get any way to work this out. can someone help me with this? Big Help.
I try With this code and it returns empty without any data
[{
$lookup: {
from: 'userandapps',
localField: '_id',
foreignField: 'appID',
as: 'appData'
}
}, {
$unwind: {
path: "$appData",
preserveNullAndEmptyArrays: true
}
}, {
$match: {
"$or": [{
'appData.userID': ObjectId('5cbd2d3e0afcb16efa795563')
}, {
'appData': null
}]
}
}]

Using aggregate $lookup and $mergeObjects

I want to join collection.
before, I used only lookup, so that I could get separated field that is joined.
but I need to get result similar mysql join.
I noticed there is $lookup and $mergeObjects for this action but not working well.
user collection model.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'admin user',
"email": 'admin#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 0,
"approved": true
},{
"id": 2,
"approved": true
}
]
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'userOne',
"email": 'user#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 1,
"approved": true
}
]
}
roles collection model.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'administrator'
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'employeer'
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'freelancer'
}
after join, I want to get result like below.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'admin user',
"email": 'admin#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 0,
"name": "administrator", //join result
"approved": true
},{
"id": 2,
"name": "freelancer", //join result
"approved": true
}
]
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'userOne',
"email": 'user#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 1,
"name": "employeer", //join result
"approved": true
}
]
}
You can use below aggregation with mongodb 3.4
You need to $unwind the roles array first and then $group to rollback again
db.users.aggregate([
{ "$unwind": "$roles" },
{ "$lookup": {
"from": "roles",
"localField": "roles.id",
"foreignField": "id",
"as": "roles.role"
}},
{ "$unwind": "$roles.role" },
{ "$addFields": {
"roles": { "$mergeObjects": ["$roles.role", "$roles"] }
}},
{ "$group": {
"_id": "$_id",
"email": { "$first": "$email" },
"password": { "$first": "$password" },
"roles": { "$push": "$roles" }
}},
{ "$project": { "roles.role": 0 }}
])
Which is quite simple with the mongodb 3.6 and above
db.users.aggregate([
{ "$unwind": "$roles" },
{ "$lookup": {
"from": "roles",
"let": { "roleId": "$roles.id", "approved": "$roles.approved" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$id", "$$roleId"] }}},
{ "$addFields": { "approved": "$$approved" }}
],
"as": "roles"
}},
{ "$unwind": "$roles" },
{ "$group": {
"_id": "$_id",
"email": { "$first": "$email" },
"password": { "$first": "$password" },
"roles": { "$push": "$roles" }
}}
])
Both will give you similar Output
[
{
"_id": ObjectId("5a934e000102030405000004"),
"email": "user#test.com",
"password": "xxxxxxxx",
"roles": [
{
"_id": ObjectId("5a934e000102030405000001"),
"approved": true,
"id": 1,
"name": "employeer"
}
]
},
{
"_id": ObjectId("5a934e000102030405000003"),
"email": "admin#test.com",
"password": "xxxxxxxx",
"roles": [
{
"_id": ObjectId("5a934e000102030405000000"),
"approved": true,
"id": 0,
"name": "administrator"
},
{
"_id": ObjectId("5a934e000102030405000002"),
"approved": true,
"id": 2,
"name": "freelancer"
}
]
}
]

MongoDB aggregate count of items in two arrays across different documents is the same?

Here is my MongoDB collection schema:
company: String
model: String
cons: [String] // array of tags that were marked as "cons"
pros: [String] // array of tags that were marked as "pros"
Here is my query:
[
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": "$pro",
"value": "$$pro"
}
}},
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": "$con",
"value": "$$con"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"theTag": "$data.value"
},
"sumPros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "$pro" ] },
1,
0
]
}
},
"sumCons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "$con" ] },
1,
0
]
}
}
}},
{ "$group": {
"_id": {
"company": "$_id.company",
"model": "$_id.model",
},
"tags": {$push: {
"tag": "$_id.theTag",
"pros": "$sumPros",
"cons": "$sumCons"
}
}}
}]
Here is the output:
{
"_id": {
"company": "Lenovo",
"model": "T400"
},
"tags": [
{
"tag": "Quality",
"pros": 64, // expected value is 54
"cons": 64 // expected value is 10
},
{
"tag": "Value",
"pros": 76, // expected value is 30
"cons": 76 // expected value is 46
}
]
}
...
Notice that pros and cons values are the same. They, for some reason, represent the sum of pros and cons and I can't figure-out why.
What am I doing wrong?
Update:
Here is a document from the collection:
{
"company": "Lenovo",
"model": "X200",
"cons": [
"Quality"
],
"pros": [
"Value",
"Styling"
]
}
As the author of the content you are using in the query and also after asking you to submit some information in the form of data that actually supports the claim in the question here, I have to say that what you are saying is incorrect.
For the record, this is your sample at time of answer:
{
"company": "Lenovo",
"model": "X200",
"cons": [
"Quality"
],
"pros": [
"Value",
"Styling"
]
}
On your sample here, if I run the following query ( and I do extend responsibity for any misleading operations in previous answers and will ammend those immediately ) then the results I see should be what is expected:
db.collection.aggregate([
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": { "$literal": "con" },
"value": "$$con"
}
}},
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": { "$literal": "pro" },
"value": "$$pro"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"tag": "$data.value"
},
"pros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "pro" ] },
1,
0
]
}
},
"cons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "con" ] },
1,
0
]
}
}
}}
])
Which produces from your sample
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Quality"
},
"pros" : 0,
"cons" : 1
}
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Value"
},
"pros" : 1,
"cons" : 0
}
{
"_id" : {
"company" : "Lenovo",
"model" : "X200",
"tag" : "Styling"
},
"pros" : 1,
"cons" : 0
}
Which clearly correctly allocates both "pros" and "cons" totals across the grouping keys as should be expected.
Therefore what "I see" here is that the values are not in fact "the same" but are actually "different" as matches the different conditions given to each field accumulator.
Therefore taking that further, and based on your original question:
db.collection.aggregate([
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": { "$literal": "con" },
"value": "$$con"
}
}},
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": { "$literal": "pro" },
"value": "$$pro"
}
}}
]
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"tag": "$data.value"
},
"pros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "pro" ] },
1,
0
]
}
},
"cons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "con" ] },
1,
0
]
}
}
}},
{ "$group": {
"_id": {
"company": "$_id.company",
"model": "$_id.model"
},
"data": { "$push": {
"tag": "$_id.tag",
"pros": "$pros",
"cons": "$cons"
}}
}}
])
Produces:
{
"_id" : {
"company" : "Lenovo",
"model" : "X200"
},
"data" : [
{
"tag" : "Quality",
"pros" : 0,
"cons" : 1
},
{
"tag" : "Value",
"pros" : 1,
"cons" : 0
},
{
"tag" : "Styling",
"pros" : 1,
"cons" : 0
}
]
}
Which is exactly what you are asking for.

Categories