Related
I have one query where I want to group subdocument. i have try some example but it is not working properly
Query
db.getCollection("checklistCombination").aggregate([
{
"$lookup": {
"from": "Users",
"localField": "userId",
"foreignField": "_id",
"as": "user"
}
},
{
"$lookup": {
"from": "checklist",
"localField": "checklistId",
"foreignField": "_id",
"as": "linkChecklist"
}
},
{ "$unwind": "$linkChecklist" },
{
"$lookup": {
"from": "orderDetail",
"localField": "linkChecklist.product",
"foreignField": "productRangeId",
"as": "orderProduct"
}
},
{
"$unwind": { "path": "$orderProduct", "preserveNullAndEmptyArrays": true }
},
{
"$lookup": {
"from": "companysuppliers",
"localField": "orderProduct.supplierId",
"foreignField": "_id",
"as": "comapnySupplier"
}
},
{
"$unwind": {
"path": "$comapnySupplier",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"from": "suppliers",
"localField": "comapnySupplier.supplierId",
"foreignField": "_id",
"as": "supplier"
}
},
{ "$unwind": { "path": "$supplier", "preserveNullAndEmptyArrays": true } },
{
"$project": {
"_id": 1,
"name": 1,
"user": 1,
"linkChecklist": 1,
"orderProduct": 1,
"orderProductStatusIndex": {
"$ifNull": ["$orderProduct.statusIndex", "0"]
},
"comapnySupplier": 1,
"supplier": 1
}
},
{ "$match": { "orderProductStatusIndex": "0" } },
{
"$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"supplier": {
"$push": {
"supplierId": "$supplier._id",
"supplierName": "$supplier.name",
"items": { "$sum": "$orderProduct.quantity" }
}
}
}
}
])
This query return below result
[{
"_id" : ObjectId("5cee224b97e765079c8c2839"),
"name" : "Dairy",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
},
{
"_id" : ObjectId("5cee1a19a01ad50f5c2229f2"),
"name" : "dairy/fruit",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 55
},
{
"supplierId" : ObjectId("5cee11f7a01ad50f5c2229a2"),
"supplierName" : "Agron India PVT. LTD",
"items" : 55
},
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
}]
In result you can see there are two different results for Bhagwandas Bherumal in dairy/fruit (in array index 1 ). I want to group by this field and sum its items.
Expected Result
[
{
"_id" : ObjectId("5cee224b97e765079c8c2839"),
"name" : "Dairy",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
},
{
"_id" : ObjectId("5cee1a19a01ad50f5c2229f2"),
"name" : "dairy/fruit",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 65
},
{
"supplierId" : ObjectId("5cee11f7a01ad50f5c2229a2"),
"supplierName" : "Agron India PVT. LTD",
"items" : 55
}
]
}]
Hope this solves your problem :
db.getCollection("checklistCombination").aggregate([
{
"$lookup": {
"from": "Users",
"localField": "userId",
"foreignField": "_id",
"as": "user"
}
},
{
"$lookup": {
"from": "checklist",
"localField": "checklistId",
"foreignField": "_id",
"as": "linkChecklist"
}
},
{ "$unwind": "$linkChecklist" },
{
"$lookup": {
"from": "orderDetail",
"localField": "linkChecklist.product",
"foreignField": "productRangeId",
"as": "orderProduct"
}
},
{
"$unwind": { "path": "$orderProduct", "preserveNullAndEmptyArrays": true }
},
{
"$lookup": {
"from": "companysuppliers",
"localField": "orderProduct.supplierId",
"foreignField": "_id",
"as": "comapnySupplier"
}
},
{
"$unwind": {
"path": "$comapnySupplier",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"from": "suppliers",
"localField": "comapnySupplier.supplierId",
"foreignField": "_id",
"as": "supplier"
}
},
{ "$unwind": { "path": "$supplier", "preserveNullAndEmptyArrays": true } },
{
"$project": {
"_id": 1,
"name": 1,
"user": 1,
"linkChecklist": 1,
"orderProduct": 1,
"orderProductStatusIndex": {
"$ifNull": ["$orderProduct.statusIndex", "0"]
},
"comapnySupplier": 1,
"supplier": 1
}
},
{ "$match": { "orderProductStatusIndex": "0" } },
{ $group : {
_id : {
_id : '$_id',
name : '$name',
supplierId : "$supplier._id",
supplierName : "$supplier.name"
},
items : { "$sum": "$orderProduct.quantity"}
}
},
{
$group : {
_id : '$_id._id',
name : { "$first": "$_id.name" },
supplier : {
"$push": {
"supplierId": "$_id.supplierId",
"supplierName": "$_id.supplierName",
"items" : '$items'
}
}
}
}
])
How can i perform $lookup in aggregate (mongodb) for an array
{
messages: [{
"_id" : ObjectId("5bfc43f2bbc4176ecc1c5f83"),
"text" : "text1",
"sender" : {
"id" : "36046fc2e70dd508a0bf1f36fd2daa20"
}
}, {
"_id" : ObjectId("5bfc43f2bbc4176ecc1c5f83"),
"text" : "text2",
"sender" : {
"id" : "36046fc2e70dd508a0bf1f36fd2daa22"
}
}],
"filed1": { ... },
"filed2": { ... }
}
how can i do a $lookup for sender id from accounts collection?
tried:
...
{
$lookup: {
from: "accounts",
localField: "messages.sender.id",
foreignField: "id",
as: "messages.sender.user"
}
}
...
You can use below aggregation
db.collection.aggregate([
{ "$unwind": "$messages" },
{ "$lookup": {
"from": "accounts",
"localField": "messages.sender.id",
"foreignField": "id",
"as": "messages.sender.user"
}},
{ "$unwind": "$messages.sender.user" }
{ "$group": {
"_id": "$_id",
"messages": { "$push": "$messages" },
"filed1": { "$first": "$filed1" },
"filed2": { "$first": "$filed2" }
}}
])
I have following users collection
[{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [ ObjectId("5afd1c42af18d985a06ac306"),ObjectId("5afd257daf18d985a06ac6ac") ]
},
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [],
}]
and have friends collection
[{
"_id" : ObjectId("5afd1c42af18d985a06ac306"),
"recipient" : ObjectId("5afaab572c4ec049aeb0bcba"),
"requester" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"status" : 2,
},
{
"_id" : ObjectId("5afd257daf18d985a06ac6ac"),
"recipient" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"requester" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"status" : 1,
}]
suppose I have an user logged in with _id: "5afaab572c4ec049aeb0bcba" and this _id matches the recipient of the friends
Now I have to add a field friendsStatus which contains the status from friends collection... And if does not matches the any recipient from the array then its status should be 0
So when I get all users then my output should be
[{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [ ObjectId("5afd1c42af18d985a06ac306") ],
"friendStatus": 2
},
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [],
"friendStatus": 0
}]
Thanks in advance!!!
If you have MongoDB 3.6 then you can use $lookup with a "sub-pipeline"
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"let": { "friends": "$friends" },
"pipeline": [
{ "$match": {
"recipient": ObjectId("5afaab572c4ec049aeb0bcba"),
"$expr": { "$in": [ "$_id", "$$friends" ] }
}},
{ "$project": { "status": 1 } }
],
"as": "friends"
}},
{ "$addFields": {
"friends": {
"$map": {
"input": "$friends",
"in": "$$this._id"
}
},
"friendsStatus": {
"$ifNull": [ { "$min": "$friends.status" }, 0 ]
}
}}
])
For earlier versions, it's ideal to actually use $unwind in order to ensure you don't breach the BSON Limit:
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"localField": "friends",
"foreignField": "_id",
"as": "friends"
}},
{ "$unwind": { "path": "$friends", "preserveNullAndEmptyArrays": true } },
{ "$match": {
"$or": [
{ "friends.recipient": ObjectId("5afaab572c4ec049aeb0bcba") },
{ "friends": null }
]
}},
{ "$group": {
"_id": "$_id",
"firstName": { "$first": "$firstName" },
"friends": { "$push": "$friends._id" },
"friendsStatus": {
"$min": {
"$ifNull": ["$friends.status",0]
}
}
}}
])
There is "one difference" from the most optimal form here in that the pipeline optimization does not actually "roll-up" the $match condition into the $lookup itself:
{
"$lookup" : {
"from" : "friends",
"as" : "friends",
"localField" : "friends",
"foreignField" : "_id",
"unwinding" : {
"preserveNullAndEmptyArrays" : true
}
}
},
{
"$match" : { // <-- outside will preserved array
Because of the preserveNullAndEmptyArrays option being true then the "fully optimized" action where the condition would actually be applied to the foreign collection "before" results are returned does not happen.
So the only purpose of unwinding here is purely to avoid what would normally be a target "array" from the $lookup result causing the parent document to grow beyond the BSON Limit. Additional conditions of the $match are then applied "after" this stage. The default $unwind without the option presumes false for the preservation and a matching condition is added instead to do this. This of course would result in the documents with no foreign matches being excluded.
And not really advisable because of that BSON Limit, but there is also applying $filter to the resulting array of $lookup:
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"localField": "friends",
"foreignField": "_id",
"as": "friends"
}},
{ "$addFields": {
"friends": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"cond": {
"$eq": [
"$$this.recipient",
ObjectId("5afaab572c4ec049aeb0bcba")
]
}
}
},
"in": "$$this._id"
}
},
"friendsStatus": {
"$ifNull": [
{ "$min": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"cond": {
"$eq": [
"$$this.recipient",
ObjectId("5afaab572c4ec049aeb0bcba")
]
}
}
},
"in": "$$this.status"
}
}},
0
]
}
}}
])
In either case we're basically adding the "additional condition" to the join being not just on the directly related field but also with the additional constraint of the queried ObjectId value for "recipient".
Not really sure what you are expecting for "friendsStatus" since the result is an array and there can possibly be more than one ( as far as I know ) and therefore just applying $min here to extract one value from the array in either case.
The governing condition in each case is $ifNull which is applied where there isn't anything in the "friends" output array to extract from and then you simply return the result of 0 where that is the case.
All output the same thing:
{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [
ObjectId("5afd1c42af18d985a06ac306")
],
"friendsStatus" : 2
}
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [ ],
"friendsStatus" : 0
}
I'm using npm "csv-express" package to export data to CSV. I'm having trouble getting the nested vendor name to display in the exported file. I used the $lookup (aggregation) to get the vendor name from another collection.
Here what the exported file look like:
Here is my query:
db.getCollection('systems').aggregate([
{ "$sort": { "sponsor": 1 } },
{ "$lookup": {
"from": "vendors",
"localField": "vendorID",
"foreignField": "_id",
"as": "vendor"
}
},
{ "$project":{
"sponsor":1,
"address":1,
"city":1,
"state":1,
"vendor.name":1
}
}])
Here's what the query returns:
/* 1 */
{
"_id" : ObjectId("5ab55fee294f2366c054d5eb"),
"sponsor" : "John Doe",
"address" : "123 Western Ave",
"city" : "Los Angeles",
"state" : "CA",
"vendor" : [
{
"name" : "West Interactive"
}
]
}
I tried the following but had no luck.
db.getCollection('systems').aggregate([
{ "$sort": { "sponsor": 1 } },
{ "$lookup": {
"from": "vendors",
"localField": "vendorID",
"foreignField": "_id",
"as": "vendor"
}
},
{ "$project":{
"sponsor":1,
"address":1,
"city":1,
"state":1,
"vendor.[0].name":1
}
}])
Any suggestion would be greatly appreciated!
I found a great article on how to accomplish what I needed.
Here is update query:
db.getCollection('systems').aggregate([
{ "$lookup": {
"from": "vendors",
"localField": "vendorID",
"foreignField": "_id",
"as": "vendor"
}
},
**{ $unwind: "$vendor" },**
{ "$project":{
"sponsor":1,
"address":1,
"city":1,
"state":1,
**"vendorName":"$vendor.name"**
}
}])
This returned the data need for my export.
/* 1 */
{
"_id" : ObjectId("5ab55fee294f2366c054d5eb"),
"sponsor" : "John Doe",
"address" : "123 Western Ave",
"city" : "Los Angeles",
"state" : "CA",
"vendorName" : "Acme Inc."
}
I need to retrieve the entire single object hierarchy from the database as a JSON. I decided to use MongoDB with its $lookup support.
So I have three collections:
1. Restaurants
{
"_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"name" : "KFC",
"city" : "Lahore",
"area" : "Johar town",
"min_order" : "500",
"del_time" : "1 hour",
"del_fees" : "5",
"pre_order" : "no",
"rating_star" : "0",
"rating_no" : 0,
"status" : "1",
"working_hours" : {
"monday" : "2pm 6am",
"tuesday" : "6am 8pm",
"wednesday" : "9pm 5pm",
"thursday" : "3pm 6pm",
"friday" : "1pm 9pm",
"saturday" : "4pm 5pm",
"sunday" : "4pm 8pm"
},
"cuisines" : [
"fast food, pizza"
],
"payments" : [
"cash,credit card,paypal"
]
}
2. categories
"categories" : [
{
"_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"name" : "Breakfast",
"category_id" : "1",
"__v" : 0
},
{
"_id" : ObjectId("57c6a06021c8b8d04443aa7f"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"name" : "Special",
"category_id" : "2",
"__v" : 0
}
]
3. items
"items" : [
{
"_id" : ObjectId("57c6da8cc8d053e8310dfe65"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"category_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"name" : "Water",
"price" : "55",
"rating" : "0",
"__v" : 0
},
{
"_id" : ObjectId("57c6da96c8d053e8310dfe66"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"category_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"name" : "Milk",
"price" : "55",
"rating" : "0",
"__v" : 0
}
]
I want an output in Json response like this
{
"_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"name" : "KFC",
"city" : "Lahore",
"area" : "Johar town",
"min_order" : "500",
"del_time" : "1 hour",
"del_fees" : "5",
"pre_order" : "no",
"rating_star" : "0",
"rating_no" : 0,
"status" : "1",
"working_hours" : {
"monday" : "2pm 6am",
"tuesday" : "6am 8pm",
"wednesday" : "9pm 5pm",
"thursday" : "3pm 6pm",
"friday" : "1pm 9pm",
"saturday" : "4pm 5pm",
"sunday" : "4pm 8pm"
},
"cuisines" : [
"fast food, pizza"
],
"payments" : [
"cash,credit card,paypal"
],
"__v" : 0,
"categories" : [{
"_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"name" : "Breakfast",
"category_id" : "1",
"__v" : 0,
"items" : [
{
"_id" : ObjectId("57c6da8cc8d053e8310dfe65"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"category_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"name" : "Water",
"price" : "55",
"rating" : "0",
"__v" : 0
},
{
"_id" : ObjectId("57c6da96c8d053e8310dfe66"),
"restaurant_id" : ObjectId("57c6a02021c8b8d04443aa7c"),
"category_id" : ObjectId("57c6a05421c8b8d04443aa7e"),
"name" : "Milk",
"price" : "55",
"rating" : "0",
"__v" : 0
}
]
}]
}
Still, I'm doing like this
db.restaurants.aggregate([{
$lookup: {
from: "categories",
localField: "_id",
foreignField: "restaurant_id",
as: "categories"
}
}])
It's working fine for Restaurants and categories but how I do it for each categories item that I have in categories?
You could try the following aggregation pipeline, bearing in mind that you'd need to do an $unwind operation on the created lookup array in order for the next $lookup operation to apply:
db.restaurants.aggregate([
{
"$lookup": {
"from": "categories",
"localField": "_id",
"foreignField": "restaurant_id",
"as": "categories"
}
},
{ "$unwind": "$categories" },
{
"$lookup": {
"from": "items",
"localField": "categories._id",
"foreignField": "category_id",
"as": "categories.items"
}
}
])
I don't think that's directly possible. Per the documentation on $lookup:
If your localField is an array and you’d like to match the elements
inside it against a foreignField which is a single element, you’ll
need to $unwind the array as one stage of the aggregation pipeline.
So, basically you need to $unwind your categories array in order to look up items. But then you can get creative as far as piecing it back together.
For example, this gets close to what you want:
db.restaurants.aggregate([
{
$lookup: {
from: "categories",
localField: "_id",
foreignField: "restaurant_id",
as: "categories"
}
},
{
$unwind: "$categories"
},
{
$lookup: {
from: "items",
localField: "categories._id",
foreignField: "category_id",
as: "items"
}
},
{
$project:{
category: {
elem: "$categories",
items: "$items"
}
}
},
{
$group: {
_id: "$_id",
categories: {$addToSet: "$category"}
}
},
{
$lookup: {
from: "restaurants",
localField: "_id",
foreignField: "_id",
as: "restaurant"
}
},
{
$unwind: "$restaurant"
},
{
$project: {
_id:false,
restaurant_id:"$_id",
categories:true,
restaurant_data:"$restaurant"
}
}
]);