$lookup aggregation for nested array object - mongodb

I have category collection which has subcategory embedded object. I need to write aggregation script to first lookup from this collection to storelisting collection. Then add the storelisting fields to the corresponding subcategories.
category
{
"_id": "",
"categoryName": "",
"subCategories": [
{
"subCategoryId": "",
"subCategoryName": "",
"storeListingIds": [
"1",
"2"
]
},
{
"subCategoryId": "",
"subCategoryName": "",
"storeListingIds": [
"3","4","5"
]
}
],
"order": 2,
"createdAt": {
"$date": "2020-12-01T22:26:11.669Z"
},
"updatedAt": {
"$date": "2021-04-27T17:17:25.442Z"
},
"_class": ""
}
storelisting
{
"_id": "1",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "2",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "3",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "4",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "5",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
}
I need to lookup form above collection to storeListing collection. I want all the fields of storeListing also.
Result should be look like this:
{
"_id": "",
"categoryName": "",
"subCategories": [
{
"subCategoryId": "",
"subCategoryName": "",
"storeListingIds": [
"1",
"2"
],
"storeListings":[
{
"_id": "1",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "2",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
}
]
},
{
"subCategoryId": "",
"subCategoryName": "",
"storeListingIds": [
"3","4","5"
],
"storeListings":[
{
"_id": "3",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "4",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
},
{
"_id": "5",
"storeListingName": "",
"storeListingUrl": "",
"catalogueIds": [
""
],
"_class": ""
}
]
}
],
"order": 2,
"createdAt": {
"$date": "2020-12-01T22:26:11.669Z"
},
"updatedAt": {
"$date": "2021-04-27T17:17:25.442Z"
},
"_class": ""
}

Explanation
We perform $lookup for nested storeListingId and store the result in the tmp field
We apply filtering and merge subCategories object with storeListings values
Note: The reason why we use $mergeObjects is to avoid naming every field in subCategories
$map: {
input: "$subCategories",
as: "subCat",
in: {
subCategoryId:"$$subCat.subCategoryId",
subCategoryName:"$$subCat.subCategoryName",
storeListingIds: "$$subCat.storeListingIds",
storeListening: {
$filter: {
input: "$tmp",
cond: {
$in: [ "$$this._id", "$$subCat.storeListingIds" ]
}
}
}
}
}
Try this one:
db.category.aggregate([
{
$lookup: {
from: "storelisting",
localField: "subCategories.storeListingIds",
foreignField: "_id",
as: "tmp"
}
},
{
$addFields: {
tmp: "$$REMOVE",
subCategories: {
$map: {
input: "$subCategories",
as: "subCat",
in: {
"$mergeObjects": [
"$$subCat",
{
storeListings: {
$filter: {
input: "$tmp",
cond: {
$in: [ "$$this._id", "$$subCat.storeListingIds" ]
}
}
}
}
]
}
}
}
}
}
])
MongoPlayground

Related

Adding Node to exisitng documents mongodb

I am trying to update the existing document with one extra field as a new requirement in DB needs that
Existing field
[{
"_id": {
"$oid": "62a8ad644035e93c7ff24607"
},
"user_name": "demo2#devia.com",
"password": "1234",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"first_name": "ghgh",
"middle_name": "",
"last_name": "gh",
"email": "demo2#devia.com",
"mobile_number": 1234567890
},
"address_details": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2381c4"
},
"state_code": "MP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21f1a9"
},
"city_name": "Akodia"
},
"address_line1": "",
"address_line2": "",
"pincode": ""
},
"is_verify": false,
"last_accessed": {
"$date": {
"$numberLong": "1655221604144"
}
},
"created_at": {
"$date": {
"$numberLong": "1655221604144"
}
},
"course_details": {}
}]
Needs to update all fields
[{
"_id": {
"$oid": "62bf2cf2a8c782741bbcc398"
},
"user_name": "a#exaple.com",
"password": "$2b$12$NgLNYm5jUBmFUq.w15.qCO35GiTJX09jAnOpoGuPt9G7GNuOkVN7K",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"email": "a#example.com",
"mobile_number": "9898989898",
"first_name": "Fahim",
"middle_name": "",
"last_name": "Ashhab",
"nationality": "Antiguans",
"date_of_birth": "2003-02-04",
"admission_year": "2022-23",
"gender": "Male",
"category": "General",
"para_ability": {
"is_disable": false,
"name_of_disability": ""
}
},
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
},
"is_verify": true,
"last_accessed": {
"$date": {
"$numberLong": "1656696050670"
}
},
"created_at": {
"$date": {
"$numberLong": "1656696050670"
}
},
"allocate_to_counselor": {
"counselor_id": {
"$oid": "62bfd13a5ce8a398ad101bd7"
},
"counselor_name": "test",
"last_update": {
"$date": {
"$numberLong": "1656817948401"
}
}
},
"course_details": {
"BSc": {
"course_id": {
"$oid": "628e03d94e22276b98231407"
},
"course_name": "BSc",
"application_id": {
"$oid": "62bf2cf2a8c782741bbcc399"
},
"status": "Incomplete",
"specs": [
{
"spec_name": "Physician Assistant",
"is_activated": true
}
]
}
}
}]
Query Tried nothing is happening
db.collection.aggregate([
{$match:{"address_details.country": {$exists:true}}},
{$set:{address_details:{communication_address:"$address_details"}}}
])
2nd Query Tried didn't go through
db.studentsPrimaryDetails.updateMany({
"address_details.communication_address": {"$exist":false}},
{$set: {"address_details.communication_address":"$address_details"}})
The desired result needs to be
from this
"address_details": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2381c4"
},
"state_code": "MP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21f1a9"
},
"city_name": "Akodia"
},
"address_line1": "",
"address_line2": "",
"pincode": ""
}
to this
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
}
Don't know what I am doing wrong.
Edit :
Desired full output
[{
"_id": {
"$oid": "62bf2cf2a8c782741bbcc398"
},
"user_name": "a#example.com",
"password": "12345678",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"email": "a#example.com",
"mobile_number": "9898989898",
"first_name": "Fahim",
"middle_name": "",
"last_name": "Ashhab",
"nationality": "Antiguans",
"date_of_birth": "2003-02-04",
"admission_year": "2022-23",
"gender": "Male",
"category": "General",
"para_ability": {
"is_disable": false,
"name_of_disability": ""
}
},
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
},
"is_verify": true,
"last_accessed": {
"$date": {
"$numberLong": "1656696050670"
}
},
"created_at": {
"$date": {
"$numberLong": "1656696050670"
}
},
"allocate_to_counselor": {
"counselor_id": {
"$oid": "62bfd13a5ce8a398ad101bd7"
},
"counselor_name": "viru chaudhary",
"last_update": {
"$date": {
"$numberLong": "1656817948401"
}
}
},
"course_details": {
"BSc": {
"course_id": {
"$oid": "628e03d94e22276b98231407"
},
"course_name": "BSc",
"application_id": {
"$oid": "62bf2cf2a8c782741bbcc399"
},
"status": "Incomplete",
"specs": [
{
"spec_name": "Physician Assistant",
"is_activated": true
}
]
}
}
}]```
One option is:
Use $exists instead of $exist
Use pipeline in order to be able to $set the value from another fields' value
db.collection.updateMany(
{"address_details.communication_address": {$exists: false}},
[{$set: {address_detailsB: {communication_address: "$address_details"}}},
{$set: {
address_details: "$address_detailsB",
address_detailsB: "$$REMOVE"}
}
])
See how it works on the playground example
The aggregation query that you first tried, does not not update the db, it only returns data. In order to use it as an updater you need to add a $merge step.

Adding a nested value as a field - MongDB aggregation

So I have a parent document with users, as well as an array that has users too. I want to add the DisplayName from the nested users array to the aggregation output. Any ideas?
Output I'm looking to achieve:
[
{
"user": {
"_id": "11",
"Name": "Dave",
"DocID": "1",
"DocDisplyName": "ABC"
},
{
"user": {
"_id": "33",
"Name": "Henry",
"DocID": "1",
"DocDisplyName": "ABC",
"BranchDisplayName:"BranchA"
}
}
]
And so on.. So an array of all users and for users that belong to a branch, add the branch display Name to the output.
// Doc 1
{
"_id": "1",
"DisplayName": "ABC",
"Users": [
{ "_id": "11", "Name": "Dave" },
{ "_id": "22", "Name": "Steve" }
],
"Branches": [
{
"_id": "111",
"DisplayName": "BranchA",
"Users": [
{ "_id": "33", "Name": "Henry" },
{ "_id": "44", "Name": "Josh" },
],
},
{
"_id": "222",
"DisplayName": "BranchB",
"Users": [
{ "_id": "55", "Name": "Mark" },
{ "_id": "66", "Name": "Anton" },
],
}
]
}
``Doc 2
{
"_id": "2",
"DisplayName": "DEF",
"Users": [
{ "_id": "77", "Name": "Josh" },
{ "_id": "88", "Name": "Steve" }
],
"Branches": [
{
"_id": "333",
"DisplayName": "BranchA",
"Users": [
{ "_id": "99", "Name": "Henry" },
{ "_id": "10", "Name": "Josh" },
],
},
{
"_id": "444",
"DisplayName": "BranchB",
"Users": [
{ "_id": "112", "Name": "Susan" },
{ "_id": "112", "Name": "Mary" },
],
}
]
}
Collection.aggregate([
{
$addFields: {
branchUsers: {
$reduce: {
input: "$Branches.Users",
initialValue: [],
in: {
$concatArrays: ["$$this", "$$value"],
},
},
},
},
},
{
$addFields: {
user: {
$concatArrays: ["$branchUsers", "$Users"],
},
},
},
{
$addFields: {
"user.DocID": "$_id","user.DocDisaplyName": "$DisplayName"
},
},
{
$unwind: "$user",
},
{
$project: {
_id: 0,
user: 1,
},
}
])
Thanks in advance!
OK I found a solution.
{
$addFields: {
"branchUsers.BranchDisplayName": {
$let: {
vars: {
first: {
$arrayElemAt: [ "$Branches", 0 ]
}
},
in: "$$first.DisplayName"
}
}
}
},
This creates the field only for the users that belong to the branch

Mongodb: How to merge $facet output 2 by 2?

Playground:https://mongoplayground.net/p/YUV_fReyGsr
I have following query. I need to combine the result 2 by 2. Meaning I need to combine facet "1","2" as a result and facet "3","4" as another result. It's guaranteed that the number of facet will be even. Also, each pair of facet should get at most one record(it might not matter)
db.collection.aggregate([
{
"$facet": {
"1": [
{
$match: {
"ID": "2"
}
}
],
"2": [
{
$match: {
"array.ID": "2"
}
}
],
"3": [
{
$match: {
"array.ID": "4"
}
}
],
"4": [
{
$match: {
"ID": "4"
}
}
]
}
}
])
The expected result will be
[
{
"1": [
{
"ID": "1",
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
],
"attr1": "123"
}
],
"2": [
{
"ID": "4",
"array": [
{
"ID": "5",
"attr1": "456"
}
],
"attr1": "123"
}
]
}
]
I was able to figure this out using $concatArrays operator, along with $project.
Live demo here
Database
[
{
"ID": "1",
"attr1": "123",
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
]
},
{
"ID": "4",
"attr1": "123",
"array": [
{
"ID": "5",
"attr1": "456"
}
]
}
]
Query
db.collection.aggregate([
{
"$facet": {
"1": [
{
$match: {
"ID": "2"
}
}
],
"2": [
{
$match: {
"array.ID": "2"
}
}
],
"3": [
{
$match: {
"array.ID": "4"
}
}
],
"4": [
{
$match: {
"ID": "4"
}
}
]
}
},
{
"$project": {
_id: 0,
"1": {
"$concatArrays": [
"$1",
"$2"
]
},
"2": {
"$concatArrays": [
"$3",
"$4"
]
}
}
}
])
Result
[
{
"1": [
{
"ID": "1",
"_id": ObjectId("5a934e000102030405000000"),
"array": [
{
"ID": "2",
"attribute1": "456"
},
{
"ID": "3",
"attribute1": "567"
}
],
"attr1": "123"
}
],
"2": [
{
"ID": "4",
"_id": ObjectId("5a934e000102030405000001"),
"array": [
{
"ID": "5",
"attr1": "456"
}
],
"attr1": "123"
}
]
}
]

mongodb select all fields which is not null or empty string

I have mongodb document which has data like this:
{
"_id": "ObjectId(\"5e09db41d4ccee7f500f7326\")",
"time": "ISODate(\"2019-12-30T09:10:53Z\")",
"syslog_fac": 23,
"syslog_sever": 6,
"syslog_tag": "",
"procid": "",
"pid": "-",
"status": "Allowed",
"logtype": "Firewall Logs",
"date": "2019-12-30",
"Module": "01UM",
"Desc": "Theuserloggedout.(UserName",
"Severity_Level": "6",
"Vsys": "public",
"SourceIP": "10.10.0.57",
"ParentGroup": "/netgrup.com.tr",
"LogonTime": "2019/12/3014:07:20",
"LogoutTime": "2019/12/3014:10:53",
"ObversePackets": "0",
"ObverseBytes": "0",
"ReversePackets": "0",
"ReverseBytes": "0",
"LogoutReason": "AnIPaddressconflictoccurred",
"VSys": "",
"PolicyName": "",
"Country": "",
"DestinationIP": "",
"SourcePort": "",
"DestinationPort": "",
"SourceZone": "",
"DestinationZone": "",
"User": "",
"Protocol": "",
"ApplicationName": "",
"Profile": "",
"Type": "",
"Category": "",
"SubCategory": "",
"Page": "",
"Host": "",
"Referer": "",
"Item": "",
"Action": "",
"level": "",
"SourceNatIP": "",
"SourceNatPort": "",
"BeginTime": "",
"EndTime": "",
"SendPkts": 0,
"SendBytes": 0,
"RcvPkts": 0,
"RcvBytes": 0,
"SourceVpnID": "",
"DestinationVpnID": "",
"CloseReason": "",
"Task": "",
"Ip": "",
"VpnName": "",
"AuthenticationMethod": "",
"Command": "",
"user-name": "",
"attack-type": "",
"interface": "",
"packet-count": "",
"rate-number": "",
"file-name": "",
"file-type": "",
"direction": "",
"detect-type": "",
"virus-name": "",
"signature-id": "",
"event-number": "",
"target": "",
"role": "",
"user": ""
}
As you see there are a lot of fields which have an empty string or 0 , so what I want to do is to select only columns that have value except 0 or empty string.
this is my code that I am using to execute query from laravel but return nothings , when I apply that code in mongo directly I get correct data , and return exactly what I want , but return nothings from laravel ,
$data = Logss::raw(function($collection)use ($_id)
{
return $collection->aggregate([
[ '$match' => ['_id'=>'ObjectId(5e09dc63715d622be622c639)']],
[
'$replaceRoot'=> [
'newRoot'=>[
'$arrayToObject'=>[
'$filter'=> [
'input'=> [ '$objectToArray'=>'$$ROOT' ],
'cond'=> [
'$and'=> [
[ '$ne'=> [ '$$this.v', "" ] ],
[ '$ne'=>[ '$$this.v', 0 ] ]
]
]
]
]
]
]
]
]);
});
You can run below query:
db.collection.aggregate([
{ $match: { _id: ObjectId(...) } },
{
$replaceRoot: {
newRoot: {
$arrayToObject: {
$filter: {
input: { $objectToArray: "$$ROOT" },
cond: {
$and: [
{ $ne: [ "$$this.v", "" ] },
{ $ne: [ "$$this.v", 0 ] }
]
}
}
}
}
}
}
])
The idea is pretty simple: you start with $objectToArray to get all the fields as an array. Then you can run $filter to remove all zeros and whitespaces and convert that array back to an object $arrayToObject promoting it to the root level ($replaceRoot).
Mongo Playground

Aggregation collection in mongdb

How to populate in result of aggregated query in monogdb
Array of followedId
var followeduserId = ["abc","efg","xyz","pqr","acd","rts"];
Feeds Recommended
[
{
"feedsId": "feed1",
"userId": "abc"
},
{
"feedsId": "feed1",
"userId": "efg"
}
]
Feeds collection
[
{
"link": "www.yodo.com",
"recommended": [
"abc",
"efg"
],
"title": "This is my feed7",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
},
{
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"das",
"asd",
"eqw",
"weq"
],
"title": "This is my feed8",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
}
]
Ran aggregation query
feedsrecommended.aggregate([
{ $match: { userId: { $in: "followersId" }}},
{ $lookup: {
from: "feeds",
localField: "feedsId",
foreignField: "_id",
as: "feedsId"
}},
{ $group: {
"_id": { "feedsId": "$feedsId" },
"count": { "$sum": 1 }
}},
{ $sort: { count: -1 }}
])
result After aggregation
var resultfeeds = [
{
"count": 7,
"id": {
"_id": "feed1",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"xyz",
"pqr",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi8",
"topi6",
"topi5"
]
}
},
{
"count": 3,
"id": {
"_id": "feed5",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi2",
"topi3",
"topi4"
]
}
},
{
"count": 3,
"id": {
"_id": "feed6",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg",
"xyz",
"pqr"
],
"title": "This is my feed1",
"topics": [
"topi7",
"topi1",
"topi4",
"topi8"
]
}
},
{
"count": 2,
"id": {
"_id": "feed2",
"link": "www.yodo.com",
"recommended": [
"abc",
"acd",
"rts"
],
"title": "This is my feed1",
"topics": [
"topi7",
"topi6",
"topi8"
]
}
},
{
"count": 2,
"id": {
"_id": "feed7",
"link": "www.yodo.com",
"recommended": [
"abc",
"efg"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi5",
"topi6",
"topi4"
]
}
},
{
"count": 1,
"id": {
"_id": "feed3",
"link": "www.yodo.com",
"recommended": [
"abc",
"asd",
"eqw",
"weq"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi7",
"topi6",
"topi4"
]
}
},
{
"count": 1,
"id": {
"_id": "feed8",
"link": "www.yodo.com",
"recommended": [
"abc",
"das",
"asd",
"eqw",
"weq"
],
"title": "This is my feed1",
"topics": [
"topi1",
"topi2",
"topi5",
"topi4"
]
}
}
]
I want to populate topics and recommeded userName and image in the result
topic collection
[
{
"topic_name": "tiger"
},
{
"topic_name": "loin"
}
]
user collection
[
{
"name": "deepa",
"profileImg": "www.com/facebook.jpg"
},
{
"name": "nisa",
"profileImg": "www.com/facebook.jpg"
}
]
My last result should be like this
[
{
"count": 2,
"id": {
"_id": "feed2",
"link": "www.yodo.com",
"recommended": [
{
"_id": "abc",
"name": "deepa",
"profileImg": "www.com/facebook.jpg"
},
{
"_id": "acd",
"name": "sigger",
"profileImg": "www.com/facebook.jpg"
},
{
"_id": "rts",
"name": "buster",
"profileImg": "www.com/facebook.jpg"
}
],
"title": "This is my feed1",
"topics": [
{
"_id": "topi6",
"topic_name": "boolena"
},
{
"_id": "topi7",
"topic_name": "mika"
},
{
"_id": "topi8",
"topic_name": "tika"
}
]
}
}
]
You can try below aggregation in mongodb 3.6 and above
Feedsrecommended.aggregate([
{ "$match": { "userId":{ "$in": followersId }}},
{ "$group": {
"_id": "$feedsId",
"count": { "$sum": 1 }
}},
{ "$lookup": {
"from": "feeds",
"let": { "feedsId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$feedsId" ] }}},
{ "$lookup": {
"from": "topics",
"let": { "topics": "$topics" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$topics" ] } } }
],
"as": "topics"
}},
{ "$lookup": {
"from": "users",
"let": { "recommended": "$recommended" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$recommended" ] } } }
],
"as": "recommended"
}}
],
"as": "feedsId"
}}
])