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"
}
}
]);
Related
I have written a small query to get data from two collections,
here is my query as you can see:
Customer.aggregate([
{
$lookup: {
from: "Product",
localField: "product",
foreignField: "_id",
as: "productdata"
}
}
]).exec(function (err, res) {
if (err) {
console.log('Error', err)
}
else {
console.log(res);
}
});
I am always getting below output:
{
_id: 605412b063db104dfcb3d78a,
updated_at: 2021-03-19T02:55:44.098Z,
name: 'bhavesh',
product: '6052c356a76d435cf857aa3c',
city: 'surat',
__v: 0,
productdata: []
},
My collections are as follow:
// Customer collection
{
"_id": ObjectId("6052c3e50016c24b24a37f4d"),
"updated_at": ISODate("2021-03-18T08:37:17.659+05:30"),
"name": "bhavesh",
"product": "6052c356a76d435cf857aa3c",
"city": "surat"
},
{
"_id": ObjectId("6052c40202dce351e8448441"),
"updated_at": ISODate("2021-03-18T08:37:46.184+05:30"),
"name": "alex",
"product": "6052c369b9395c55042373e6",
"city": "ahmdabad"
},
{
"_id": ObjectId("6052c4157d1616563c707732"),
"updated_at": ISODate("2021-03-18T08:38:05.935+05:30"),
"name": "lexa",
"product": "6052c37321b64b4c40ac65a5",
"city": "mumbai"
},
{
"_id": ObjectId("6052c42efa94db04ccf19560"),
"updated_at": ISODate("2021-03-18T08:38:30.411+05:30"),
"name": "bhumi",
"product": "6052c37ef633e64cb8b65423",
"city": "surat"
}
// Product collection
{
"_id": ObjectId("6052c356a76d435cf857aa3c"),
"updated_at": ISODate("2021-03-18T08:34:54.187+05:30"),
"name": "laptop",
"company": "6052c217a1abc325b01ec87c",
"price": 66
},
{
"_id": ObjectId("6052c369b9395c55042373e6"),
"updated_at": ISODate("2021-03-18T08:35:13.939+05:30"),
"name": "charger",
"company": "6052c2311993dc433c0657d7",
"price": 50
},
{
"_id": ObjectId("6052c37321b64b4c40ac65a5"),
"updated_at": ISODate("2021-03-18T08:35:23.503+05:30"),
"name": "tablet",
"company": "6052c2311993dc433c0657d7",
"price": 50
},
{
"_id": ObjectId("6052c37ef633e64cb8b65423"),
"updated_at": ISODate("2021-03-18T08:35:34.979+05:30"),
"name": "bettery",
"company": "6052c217a1abc325b01ec87c",
"price": 500
}
// company collection
{
"_id": ObjectId("6052c217a1abc325b01ec87c"),
"updated_at": ISODate("2021-03-18T08:29:35.387+05:30"),
"name": "artoon",
"status": "active"
},
{
"_id": ObjectId("6052c2311993dc433c0657d7"),
"updated_at": ISODate("2021-03-18T08:30:01.508+05:30"),
"name": "identix",
"status": "active"
}
So basically I want data from 3 collections which have unique relation if you need more description please comment.
The $lookup doesn't match anything is because the localField 'product' of Customer collection is type String and the foreignField _id of Product collection is type ObjectId. The 2 values are not the same even though the look the same from your post. Notice that the _id value doesn't have the single quote around it while other fields (product and company) do.
You need to make them all the same type in order for the search to match. That is making
product field in Customer collection ObjectId type
company field in Product collection ObjectId type
Try this query for performing join between 3 collections:
db.Customer.aggregate([
{
$lookup: {
from: "Product",
let: {
product_id: { $toObjectId: "$product" }
},
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$product_id"] }
}
},
{
$lookup: {
from: "company",
let: {
company_id: { $toObjectId: "$company" },
},
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$company_id"] }
}
}
],
as: "company"
}
},
{ $unwind: "$company" }
],
as: "Product"
}
},
{ $unwind: "$Product" }
]);
Output:
/* 1 createdAt:3/18/2021, 8:37:17 AM*/
{
"_id" : ObjectId("6052c3e50016c24b24a37f4d"),
"updated_at" : ISODate("2021-03-18T08:37:17.659+05:30"),
"name" : "bhavesh",
"product" : "6052c356a76d435cf857aa3c",
"city" : "surat",
"Product" : {
"_id" : ObjectId("6052c356a76d435cf857aa3c"),
"updated_at" : ISODate("2021-03-18T08:34:54.187+05:30"),
"name" : "laptop",
"company" : {
"_id" : ObjectId("6052c217a1abc325b01ec87c"),
"updated_at" : ISODate("2021-03-18T08:29:35.387+05:30"),
"name" : "artoon",
"status" : "active"
},
"price" : 66
}
},
/* 2 createdAt:3/18/2021, 8:37:46 AM*/
{
"_id" : ObjectId("6052c40202dce351e8448441"),
"updated_at" : ISODate("2021-03-18T08:37:46.184+05:30"),
"name" : "alex",
"product" : "6052c369b9395c55042373e6",
"city" : "ahmdabad",
"Product" : {
"_id" : ObjectId("6052c369b9395c55042373e6"),
"updated_at" : ISODate("2021-03-18T08:35:13.939+05:30"),
"name" : "charger",
"company" : {
"_id" : ObjectId("6052c2311993dc433c0657d7"),
"updated_at" : ISODate("2021-03-18T08:30:01.508+05:30"),
"name" : "identix",
"status" : "active"
},
"price" : 50
}
},
/* 3 createdAt:3/18/2021, 8:38:05 AM*/
{
"_id" : ObjectId("6052c4157d1616563c707732"),
"updated_at" : ISODate("2021-03-18T08:38:05.935+05:30"),
"name" : "lexa",
"product" : "6052c37321b64b4c40ac65a5",
"city" : "mumbai",
"Product" : {
"_id" : ObjectId("6052c37321b64b4c40ac65a5"),
"updated_at" : ISODate("2021-03-18T08:35:23.503+05:30"),
"name" : "tablet",
"company" : {
"_id" : ObjectId("6052c2311993dc433c0657d7"),
"updated_at" : ISODate("2021-03-18T08:30:01.508+05:30"),
"name" : "identix",
"status" : "active"
},
"price" : 50
}
},
/* 4 createdAt:3/18/2021, 8:38:30 AM*/
{
"_id" : ObjectId("6052c42efa94db04ccf19560"),
"updated_at" : ISODate("2021-03-18T08:38:30.411+05:30"),
"name" : "bhumi",
"product" : "6052c37ef633e64cb8b65423",
"city" : "surat",
"Product" : {
"_id" : ObjectId("6052c37ef633e64cb8b65423"),
"updated_at" : ISODate("2021-03-18T08:35:34.979+05:30"),
"name" : "bettery",
"company" : {
"_id" : ObjectId("6052c217a1abc325b01ec87c"),
"updated_at" : ISODate("2021-03-18T08:29:35.387+05:30"),
"name" : "artoon",
"status" : "active"
},
"price" : 500
}
}
I am having certain issue with querying mongodb using mongoose, but unable to find my required output. Below is the data I am looking for the solution.
I am trying to find with two params here one is location and second is type
{
"_id" : ObjectId("5f04dcf8e123292518a863ae"),
"location" : "Australia",
"name" : "June Bill",
"filters" : [
{
"_id" : ObjectId("5f047efe9fc7ad000f44f990"),
"name" : "Q1",
"type" : "Platinum",
"conditions" : [
{
"And" : [
{
"_id" : "objectid",
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : "objectid",
"field" : "name",
"value" : "admin",
"operator" : "equal"
}
]
}
]
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6e"),
"name" : "Q2",
"type" : "Gold",
"conditions" : [
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6f"),
"field" : "ocation",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc70"),
"field" : "start_date",
"value" : "2020-06-01T00 3A00",
"operator" : "equal"
},
]
}
],
"__v" : 0
},
{
"_id" : ObjectId("5f04dcf8e123292518a863ae"),
"location" : "Brazil",
"name" : "June Bill",
"filters" : [
{
"_id" : ObjectId("5f047efe9fc7ad000f44f990"),
"name" : "Q1",
"type" : "Silver",
"conditions" : [
{
"And" : [
{
"_id" : "objectid",
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : "objectid",
"field" : "name",
"value" : "admin",
"operator" : "equal"
}
]
}
]
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6e"),
"name" : "Q2",
"type" : "Gold",
"conditions" : [
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6f"),
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc70"),
"field" : "start_date",
"value" : "2020-06-01T00 3A00",
"operator" : "equal"
},
]
}
],
"__v" : 0
}
here I am trying to find with location and in filters with its type
How Can i do this in mongoose?
I tried
Model.find({'location': 'Brazil', 'filters':{ "$in" : ["Silver"]} });
But it didn't work, Can any one help me achieving actual result?
You can use the . to query the embedded filed or if you want to match multiple fields you can use $elemMatch
db.collection.find({
"location": "Brazil",
"filters.type": {
"$in": [
"Silver"
]
}
},
{
"filters.$": 1
})
MongoDB Playground
If you want to filter out the matched result you can use $ operator in projection
Use this way:
db.collection.find({
"location": "Brazil",
"filters.type": "Silver"
})
MongoDb Playground
I have a question about lookup and filter array of objects in mongodb
I have structure: Person
{
"_id": "5cc3366c22c3767a2b114c6b",
"flags": [
"5cc30210fada5d7820d03aaf",
"5cc2c3924a94a575adbdc56a"
],
"key": "Animal",
"name": "name1",
"description": "description1",
"endpoints": [
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": "5cc1a8911b19026fd193506b"
},
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": "5ccaeef3312acb103730d4c5"
}
]
}
envs collection
{
"_id" : "5cc1a8911b19026fd193506b",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
{
"_id" : "5ccaeef3312acb103730d4c5",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
I should filter Document by endpoints.$.env
so, I have: accessKeys = ["PROD", "UAY"], and i should see result . with endpoints where env.key === "PROD" || env.key === "UAT"
Expected result:
{
"_id": "5cc3366c22c3767a2b114c6b",
"flags": [
"5cc30210fada5d7820d03aaf",
"5cc2c3924a94a575adbdc56a"
],
"key": "Animal",
"name": "name1",
"description": "description1",
"endpoints": [
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": {
"_id" : "5cc1a8911b19026fd193506b",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
},
]
}
Help me pls, how i can do that? I know about aggregate, but cant do it :(
Try this :
db.persons.aggregate([{
$unwind : "$endpoints"
},{
$lookup :{
from : "envs",
localField : "endpoints.env",
foreignField : "_id",
as : "endpoints.env"
}
},{
$unwind : "$endpoints.env"
},{
$match : {
"endpoints.env.key" : {$in : accessKeys}
}
},{
$group : {
_id : "$_id",
flags : {$first : "$flags"},
key : {$first : "$key"},
name : {$first : "$name"},
description : {$first : "$description"},
endpoints : {$push : "$endpoints"},
}
}])
Here I'll show you what exactly I want. Suppose I have the below two document for XYZ model.
[
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"email" : "one#one.com",
"mobileNumber" : "910123456989",
"verificationStatus" : true,
"activities" : [
{
"name" : "a1",
"_id" : ObjectId("59ef8786e8c7d60552139bae"),
"type" : 0,
"level" : null,
"verificationStatus" : true
},
{
"name" : "a2",
"_id" : ObjectId("59ef8786e8c7d60552139bad"),
"type" : 0,
"level" : null,
"verificationStatus" : false
}
],
"address" : {
"line1" : "asd",
"line2" : "asd",
"city" : "sd",
"state" : "sd",
"country" : "asd",
"landmark" : "sdsa",
"pincode" : "560090"
},
"__v" : 0
},
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"email" : "one#one.com",
"mobileNumber" : "919876543210",
"verificationStatus" : true,
"activities" : [
{
"name" : "b1",
"_id" : ObjectId("59ef8786e8c7d60552139bae"),
"level" : null,
"type" : 0,
"verificationStatus" : true
},
{
"name" : "b2",
"_id" : ObjectId("59ef8786e8c7d60552139bad"),
"level" : null,
"type" : 0,
"verificationStatus" : false
}
],
"address" : {
"line1" : "asd",
"line2" : "asd",
"city" : "sd",
"state" : "sd",
"country" : "asd",
"landmark" : "sdsa",
"pincode" : "560090"
},
"__v" : 0
}
]
Now I want only the name, mobileNumber and activities.name from the document where verificationStatus is true and I don't want all the activities I want activities.name only if activities.varificationStatus is true.
I can get the list of all document where varificationStatus is true and activities.varificationStatus is true but I'm not able to select only required fields (activities.name) from activities.
My current code is:
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
coverImage: 1,
location: 1,
address: 1,
dist: 1,
activities: {
$filter: {
input: "$activities",
as: "activity",
cond: {
$eq: ["$$activity.verificationStatus", true]
}
}
}
}
}], function (err, list) {
if (err) {
reject(err);
}
else {
resolve(list);
}
});
You actually need $map to "alter" the array elements returned, as $filter only "selects" the array elements that "match":
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
mobileNumber: 1,
activities: {
$map: {
input: {
$filter: {
input: "$activities",
as: "activity",
cond: "$$activity.verificationStatus"
}
},
"as": "a",
"in": "$$a.name"
}
}
}
}], function (err, list) {
...
Would return:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : ["a1"]
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : ["b1"]
}
Note also that the "cond" in $filter can be shortened since it's already a boolean value.
If you wanted the "object" with the property of "name" only, then return just that assigned key:
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
mobileNumber: 1,
activities: {
$map: {
input: {
$filter: {
input: "$activities",
as: "activity",
cond: "$$activity.verificationStatus"
}
},
"as": "a",
"in": {
"name": "$$a.name"
}
}
}
}
}], function (err, list) {
...
Returns as:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : [{ "name": "a1" }]
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : [{ "name": "b1" }]
}
If you knew for certain that you were matching "one" element in the array, then $indexOfArray with $arrayElemAt could be used instead if you have MongoDB 3.4
{ "$project": {
"name": 1,
"mobileNumber": 1,
"activities": {
"$arrayElemAt": [
"$activities.name",
{ "$indexOfArray": [ "$activities.verificationStatus", true ] }
]
}
}}
Which would come out a little differently since it's a singular value and not an array:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : "a1"
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : "b1"
}
I have sample json data in collections.
Sample data:
[{
"_id" : 1,
"username" : "abcd",
"createdDate" : ISODate("2016-06-03T08:52:32.434Z")
},
{
"_id" : 2,
"username" : "abcd",
"createdDate" : ISODate("2016-05-03T09:52:32.434Z")
},
{
"_id" : 3,
"username" : "abcd",
"createdDate" : ISODate("2016-04-03T10:52:32.434Z")
},
{
"_id" : 4,
"username" : "xyz",
"createdDate" : ISODate("2016-03-03T10:52:32.434Z")
},{
"_id" : 5,
"username" : "xyz",
"createdDate" : ISODate("2016-02-03T10:52:32.434Z")
},{
"_id" : 6,
"username" : "zzz",
"createdDate" : ISODate("2016-01-03T10:52:32.434Z")
}]
This data I need to retrieve data for following condtions.
Group by username.
username not equal "zzz"
Order by date desc order.
need date field also (which have lastest/last record).
get total count.
Expecting output:
[{
"username" : "abcd",
"createdDate" : "2016-06-03T08:52:32.434Z",
"total" : 3
},
{
"username" : "xyz",
"createdDate" : "2016-03-03T10:52:32.434Z",
"total" : 2
}]
Query:
db.logs.aggregate([
{ "$match": { "username": { "$ne": "zzz" } }},
{ "$group": {
"_id": {
"username": "$username",
"createdDate": "$createdDate"
},
"count": { "$sum": 1 }
}}])
try this :
db.logs.aggregate([
{
"$match":{
"username":{
"$ne":"zzz"
}
}
},
{
"$group":{
_id:"$username",
"count":{
"$sum":1
},
date:{
$max:"$createdDate"
}
}
},
{
$project:{
username:"$_id",
total:"$count",
createdDate:"$date"
}
}
])
output
{
"_id":"xyz",
"username":"xyz",
"total":2,
"createdDate": ISODate("2016-03-03T10:52:32.434 Z")
}{
"_id":"abcd",
"username":"abcd",
"total":3,
"createdDate": ISODate("2016-06-03T08:52:32.434 Z")
}
try it online: mongoplayground.net/p/3_-s2tUjPFi