This is my appointment collection:
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
I used aggregate to get the following result
{date: ISODate("2013-05-13T22:00:00Z"),
patients:[ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002")] }
like this:
Appointments.aggregate([
{$group: {_id: '$date', patients: {$push: '$patient'}}},
{$project: {date: '$_id', patients: 1, _id: 0}}
], ...)
How can I populate the patient document
I trued this but it doesn't work ... Appointments.find({}).populate("patient").aggregate....
In other words, can i use populate and aggregate at the same statement
any help please
With the latest version of mongoose (mongoose >= 3.6), you can but it requires a second query, and using populate differently. After your aggregation, do this:
Patients.populate(result, {path: "patient"}, callback);
See more at the Mongoose API and the Mongoose docs.
Edit: Looks like there's a new way to do it in the latest Mongoose API (see the above answer here: https://stackoverflow.com/a/23142503/293492)
Old answer below
You can use $lookup which is similar to populate.
In an unrelated example, I use $match to query for records and $lookup to populate a foreign model as a sub-property of these records:
Invite.aggregate(
{ $match: {interview: req.params.interview}},
{ $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
).exec( function (err, invites) {
if (err) {
next(err);
}
res.json(invites);
}
);
You have to do it in two, not in one statement.
In async await scenario, make sure await until populate.
const appointments = await Appointments.aggregate([...]);
await Patients.populate(appointments, {path: "patient"});
return appointments;
or (if you want to limit)
await Patients.populate(appointments, {path: "patient", select: {_id: 1, fullname: 1}});
You can do it in one query like this:
Appointments.aggregate([{
$group: {
_id: '$date',
patients: {
$push: '$patient'
}
}
},
{
$project: {
date: '$_id',
patients: 1,
_id: 0
}
},
{
$lookup: {
from: "patients",
localField: "patient",
foreignField: "_id",
as: "patient_doc"
}
}
])
populate basically uses $lookup under the hood.
in this case no need for a second query.
for more details check MongoDB aggregation lookup
Perform a Join with $lookup
A collection orders contains the following documents:
{ "_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2 }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1 }
{ "_id" : 3 }
Another collection inventory contains the following documents:
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
{ "_id" : 2, "sku" : "def", description: "product 2", "instock" : 80 }
{ "_id" : 3, "sku" : "ijk", description: "product 3", "instock" : 60 }
{ "_id" : 4, "sku" : "jkl", description: "product 4", "instock" : 70 }
{ "_id" : 5, "sku": null, description: "Incomplete" }
{ "_id" : 6 }
The following aggregation operation on the orders collection joins the documents from orders with the documents from the inventory collection using the fields item from the orders collection and the sku field from the inventory collection:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
The operation returns the following documents:
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "jkl", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
Reference $lookup
Short answer:
You can't.
Long answer:
In the Aggregation Framework, the returned fields are built by you, and you're able to "rename" document properties.
What this means is that Mongoose can't identify that your referenced documents will be available in the final result.
The best thing you can do in such a situation is populate the field you want after the query has returned. Yes, that would result in two DB calls, but it's what MongoDB allows us to do.
Somewhat like this:
Appointments.aggregate([ ... ], function( e, result ) {
if ( e ) return;
// You would probably have to do some loop here, as probably 'result' is array
Patients.findOneById( result.patient, function( e, patient ) {
if ( e ) return;
result.patient = patient;
});
});
domain.Farm.aggregate({
$match: {
"_id": mongoose.Types.ObjectId(farmId)
}
}, {
$unwind: "$SelfAssessment"
}, {
$match: {
"SelfAssessment.questionCategoryID": QuesCategoryId,
"SelfAssessment.questionID": quesId
}
},function(err, docs) {
var options = {
path: 'SelfAssessment.actions',
model: 'FarmAction'
};
domain.Farm.populate(docs, options, function (err, projects) {
callback(err,projects);
});
});
results i got action model populate
{ "error": false, "object": [
{
"_id": "57750cf6197f0b5137d259a0",
"createdAt": "2016-06-30T12:13:42.299Z",
"updatedAt": "2016-06-30T12:13:42.299Z",
"farmName": "abb",
"userId": "57750ce2197f0b5137d2599e",
"SelfAssessment": {
"questionName": "Aquatic biodiversity",
"questionID": "3kGTBsESPeYQoA8ae2Ocoy",
"questionCategoryID": "5aBe7kuYWIEoyqWCWcAEe0",
"question": "Waterways protected from nutrient runoff and stock access through fencing, buffer strips and off stream watering points",
"questionImage": "http://images.contentful.com/vkfoa0gk73be/4pGLv16BziYYSe2ageCK04/6a04041ab3344ec18fb2ecaba3bb26d5/thumb1_home.png",
"_id": "57750cf6197f0b5137d259a1",
"actions": [
{
"_id": "577512c6af3a87543932e675",
"createdAt": "2016-06-30T12:38:30.314Z",
"updatedAt": "2016-06-30T12:38:30.314Z",
"__v": 0,
"Evidence": [],
"setReminder": "",
"description": "sdsdsd",
"priority": "High",
"created": "2016-06-30T12:38:30.312Z",
"actionTitle": "sdsd"
}
],
"answer": "Relevant"
},
"locations": []
} ], "message": "", "extendedMessage": "", "timeStamp": 1467351827979 }
I see that there are many answers, I am new to mongoldb and I would like to share my answer too.
I am using aggregate function along with lookup to populate the patients.
To make it easy to read I have changed the names of the collections and fields.
Hope it's helpful.
DB:
db={
"appointmentCol": [
{
_id: ObjectId("518ee0bc9be1909012000001"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000001")
},
{
_id: ObjectId("518ee0bc9be1909012000002"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000002")
},
{
_id: ObjectId("518ee0bc9be1909012000003"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000003")
}
],
"patientCol": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
},
]
}
Aggregate Query using lookup:
db.appointmentCol.aggregate([
{
"$lookup": {
"from": "patientCol",
"localField": "patientId",
"foreignField": "_id",
"as": "patient"
}
}
])
Output:
[
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
}
],
"patientId": ObjectId("518ee0bc9be1909012000001")
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
}
],
"patientId": ObjectId("518ee0bc9be1909012000002")
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
}
],
"patientId": ObjectId("518ee0bc9be1909012000003")
}
]
Playground:
mongoplayground.net
I used lookup instead, and it worked well. See the code snipped below.
Post.aggregate([
{
$group: {
// Each `_id` must be unique, so if there are multiple
// posts with the same category, MongoDB will increment `count`.
_id: '$category',
count: { $sum: 1 }
}
},
//from: is collection name in MongoDB, localField are primary and foreign keys in Model.
{$lookup: {from: 'categories', localField: '_id', foreignField:'_id', as: 'category'}}
]).then(categoryCount => {
console.log(categoryCount);
let json = [];
categoryCount.forEach(cat => {
console.log(json);
});
Related
Here is my code, I have two collections called cart and product, First I want to take the id of the product from the cart and then from the product collection get the product details
collection cart
{
"_id" : ObjectId("62f8086e29c549f34ab89df7"),
"user" : ObjectId("62f3f8600e93c17d1c25c2ed"),
"product" : [
ObjectId("62f391b9482a375c4f83de8e"),
ObjectId("62f39121482a375c4f83de8d"),
ObjectId("62f39200482a375c4f83de8f")
]
}
collection product
{
"_id" : ObjectId("62f39121482a375c4f83de8d"),
"name" : "iphone 15",
"category" : "mobiles",
"price" : "125",
"description" : "fastest iphone"
}
{
"_id" : ObjectId("62f391b9482a375c4f83de8e"),
"name" : "OnePlus Nord",
"category" : "mobile",
"price" : "40000",
"description" : "budget phone of OnePlus"
}
{
"_id" : ObjectId("62f39200482a375c4f83de8f"),
"name" : "Samsung M33",
"category" : "mobile",
"price" : "25000",
"description" : "mid range mobile"
}
Here is the function for getting the details
getcartProducts:(userId)=>{
return new Promise(async(resolve,reject)=>{
let cartItem=await db.get().collection(collection.CART_COLLECTION).aggregate([
{
"$match":{user:objectId(userId)}
},
{
"$lookup":{
"from":collection.PRODUCT_COLLECTIONS,
"let":{"prodList":'$product'},
pipeline:[
{
"$match":{ "$expr":{ "$in":["_id","$$prodList"],
},
},
},
],
"as":"cartItems"
}
}
]
).toArray()
resolve(cartItem)
})
}
Finally the function is called
router.get('/cart',verifyLogin,async (req,res)=>{
let products=await userHelpers.getcartProducts(req.session.user._id)
console.log(products)
res.render('user/cart')
})
The output is: Showing null array in cartItems: []
[
{
_id: new ObjectId("62f8086e29c549f34ab89df7"),
user: new ObjectId("62f3f8600e93c17d1c25c2ed"),
product: [
new ObjectId("62f391b9482a375c4f83de8e"),
new ObjectId("62f39121482a375c4f83de8d"),
new ObjectId("62f39200482a375c4f83de8f"),
new ObjectId("62f39121482a375c4f83de8d")
],
cartItems: []
}
]
Use $_id instead of _id in $in operator. Mongo Playground
// This is the raw query
db.carts.aggregate([
{
"$match": {
"user": ObjectId("62f3f8600e93c17d1c25c2ed")
}
},
{
"$lookup": {
"from": "products",
"as": "cartItems",
"let": {
"prodList": "$product"
},
pipeline: [
{
"$match": {
"$expr": {
"$in": [
"$_id", // note the change here, use $ for matching field in lookup collection
"$$prodList" // use $$ for matching variable
],
},
},
},
],
}
}
])
Change the following line in your example code:
"$match":{ "$expr":{ "$in":["_id","$$prodList"],
To:
"$match":{ "$expr":{ "$in":["$_id","$$prodList"],
This aggregation pipeline cane be simplified to: Mongo Playground
[
{
"$match": {
"user": ObjectId("62f3f8600e93c17d1c25c2ed")
}
},
{
"$lookup": {
"from": "products",
"as": "cartItems",
"localField": "product",
"foreignField": "_id"
}
}
]
This question already has answers here:
Renaming Field Within an Array in MongoDB
(1 answer)
MongoDB rename database field within array
(8 answers)
Closed 10 months ago.
Below is the JSON file and I am trying to change the key name in offers from '_id' to 'id'
[{
"offers": [{
"i": "",
"a":
"e": 25.3,
"c": "A new concept in the ed-tech market. I can relate with the importance of the Learn By Doing philosophy. Keep up the Good Work! Definitely interested to work with you to scale the vision of the company!",
"_id": "62565340aa2519d6cc33e791"
}],
"id": "62565340aa2519d6cc33e790"
},
{
"er": "#3",
"p": "Title #3",
"p": "Idea #3",
"at": ,
"equity": 25.3,
"offers": [],
"id": "6256533baa2519d6cc33e78f"
}
]
I am new to Node js
I am able to change the 'id' key outside of every pitch but not inside 'offers' key.
Can someone please help me in this.
Try to change your code like this:
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("mydb");
var coll = dbo.collection("pitches");
coll
.find({})
.sort({
_id: -1,
})
.toArray(function (err, result) {
if (err) {
response.send(err);
} else {
for (const value of res) {
for (const offer of value.offers) {
offer.id = offer._id
delete offer._id
}
}
response.statusCode = 201;
response.send(result);
}
});
});
Your answer is almost right. Use double for loop.
let result = [
{
"entrepreneur": "Yakshit#4",
"pitchTitle": "Sample Title #4",
"pitchIdea": "Sample Idea #4",
"askAmount": 1000000000,
"equity": 25.3,
"offers": [{
"investor": "Anupam Mittal",
"amount": 1000000000,
"equity": 25.3,
"comment": "A new concept in the ed-tech market. I can relate with the importance of the Learn By Doing philosophy. Keep up the Good Work! Definitely interested to work with you to scale the vision of the company!",
"_id": "62565340aa2519d6cc33e791"
}],
"_id": "62565340aa2519d6cc33e790"
},
{
"entrepreneur": "Yakshit#3",
"pitchTitle": "Sample Title #3",
"pitchIdea": "Sample Idea #3",
"askAmount": 1000000000,
"equity": 25.3,
"offers": [],
"_id": "6256533baa2519d6cc33e78f"
}
]
for (const val of result) {
val["id"] = val["_id"];
delete val["_id"];
for (const val2 of val["offers"]) {
val2["id"] = val2["_id"];
delete val2["_id"];
}
}
console.log(JSON.stringify(result));
do it in mongo query
db.collection.aggregate([
{
$match: {}
},
{
$set: {
id: "$_id",
offers: {
$map: {
input: "$offers",
as: "o",
in: { $mergeObjects: [ "$$o", { id: "$$o._id" } ] }
}
}
}
},
{
$unset: [ "_id", "offers._id" ]
}
])
mongoplayground
OPTION 1as explained here You can change key name while returning query result from mongodb using aggregation.
orginal data:
{ "_id" : 2, "name" : "Sarah", "salary" : 128000 }
{ "_id" : 3, "name" : "Fritz", "salary" : 25000 }
{ "_id" : 4, "name" : "Chris", "salary" : 45000 }
{ "_id" : 5, "name" : "Beck", "salary" : 82000 }
query:
db.employees.aggregate([
{ "$project": { "employee": "$name", "salary": 1 }}
])
query result:
{ "_id" : 2, "salary" : 128000, "employee" : "Sarah" }
{ "_id" : 3, "salary" : 25000, "employee" : "Fritz" }
{ "_id" : 4, "salary" : 45000, "employee" : "Chris" }
{ "_id" : 5, "salary" : 82000, "employee" : "Beck" }
OPTION 2
you can use lodash and _.transform as explained here
I want to make the groups from this using mongodb aggregate. I want to immplement this on my project but stuck in this. not finding a better way to do this.
{
"_id" : ObjectId("5e9a21868ed974259c0da402"),
"shopId" : "5e975cc7be7c1b546b7abb17",
"shopType" : "Medium Store",
"products": [{
"isPackedProduct" : true,
"_id" : ObjectId("5e92ff706af877294d63098e"),
"brand" : "ABC",
"category" : "CAT1",
"productName" : "P1",
"subCategory" : "SUB1",
},
{
"isPackedProduct" : true,
"_id" : ObjectId("5e92ff706af877294d63098f"),
"brand" : "EFG",
"category" : "CAT1",
"productName" : "P2",
"subCategory" : "SUB2",
},
{
"isPackedProduct" : true,
"_id" : ObjectId("5e92ff706af84d630977298f"),
"brand" : "EFG",
"category" : "CAT2",
"productName" : "P3",
"subCategory" : "SUB1",
}
....
]
}
From this set of json i want to show the data as:
{
"_id" : ObjectId("5e9a21868ed974259c0da402"),
"shopId" : "5e975cc7be7c1b546b7abb17",
"CAT1":{
"SUB1":{
"products": [{
...ALL the Products which have CAT1 and SUB1
}]
}
},
"CAT2":{
"SUB1":{
"products": [{
...ALL the Products which have CAT2 and SUB1
}]
}
}
...
}
i tried so far but not getting close to solution:
db.shopproducts.aggregate([{$unwind: {path: '$products'}}, {$group: {_id: 'products.category'}}, {$project: {'products.category': 1, 'products.productName': 1}}])
Also, if there is a better way to do this without using aggregate then suggestions are welcome.
Thanks in advance.
We need to apply several $group stages. To transform products.category and products.subCategory into object field, we need to use $arrayToObject operator.
[ {
{ "k" : "CAT1", "v" : "SUB1" }, ----\ "CAT1" : "SUB1",
{ "k" : "CAT2", "v" : "SUB1" } ----/ "CAT2" : "SUB1"
] }
Try this one:
db.shopproducts.aggregate([
{
$unwind: "$products"
},
{
$group: {
_id: {
_id: "$_id",
shopId: "$shopId",
category: "$products.category",
subCategory: "$products.subCategory"
},
products: {$push: "$products"}
}
},
{
$group: {
_id: {
_id: "$_id._id",
shopId: "$_id.shopId",
category: "$_id.category"
},
products: {
$push: {
k: "$_id.subCategory",
v: {products: "$products"}
}
}
}
},
{
$group: {
_id: {
_id: "$_id._id",
shopId: "$_id.shopId"
},
products: {
$push: {
k: "$_id.category",
v: {$arrayToObject: "$products"}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
{
_id: "$_id._id",
shopId: "$_id.shopId"
},
{
$arrayToObject: "$products"
}
]
}
}
}
])
MongoPlayground
db.shopproducts.aggregate([
{
$unwind: {
path: "$products"
}
},
{
$group: {
_id: {
cat: "$products.category",
sub_cat: "$products.subCategory"
},
products: {
$addToSet: "$products"
}
}
}
])
I guess this is what you want, isn't it?
Mongoplayground
I am trying to display unique records grouped by the particular slug passed in.
My output in postman looks like this though:
"subcats": [
{
"_id": {
"subcategory": {
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts"
}
}
}
]
when my desired output is:
"subcats": [
{
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts"
}
]
An example of a product in the database:
"_id": "5d39eff7a48e6e30ace831dc",
"name": "A colourful shirt",
"description": "A nice colourful t-shirt",
"category": {
"_id": "5d35faa67b19e32ab3dc91ec",
"name": "clothing",
"catSlug": "clothing"
},
"subcategory": {
"_id": "5d2b42c47b454712f4db7c37",
"name": "shirts",
"catSlug": "shirts"
},
"price": 19
}
I don't want that top level _id there with everything nested inside of it.
I tried using $project but then I just end up with an empty array.
const products = await Product.find({ "category.catSlug": catslug }).select({
name: 1,
description: 1,
price: 1,
category: 1
});
const subcats = await Product.aggregate([
{ $match: { "category.catSlug": catslug } },
{ $group: { _id: { subcategory: "$subcategory" } } }
{ $project: { _id: 0, name: 1 } }
]);
Promise.all([products, subcats]);
res.status(200).json({
products,
subcats
});
What I have understood from your question is you need product by category and all sub categories as a list.
Below is the aggregation:
db.getCollection('test').aggregate([
{ $match: { "category.catSlug": "clothing" } },
{ $group: { _id: '$subcategory.name', subCategory: { $first : "$subcategory" }}},
{ $group: { _id: null, subcats: { $push: {_id: "$subCategory._id", name: "$subCategory.name" }}}},
{ $project: {_id: 0, subcats: 1}}
]);
Note: You can directly push "$subcategory" but it will have catSlug inside object
Output:
{
"_id" : null,
"subcats" : [{
"_id" : "5d2b42c47b454712f4db7c37",
"name" : "shirts"
},
{
"_id" : "5d2b42c47b454712f4db7c37",
"name" : "pents"
}]
}
Hope this help!
Hi you need to assign the required data in other fields while projection then define the _id to 0 as given below
{$project:{subcat_data:'$_id.subcategory'},_id:0}
This question already has answers here:
How do I perform the SQL Join equivalent in MongoDB?
(19 answers)
Closed 4 years ago.
I have a mongodb document as
{
"_id": 10001,
"uid": 1413430,
"loginType": "student"
}
the _id is bookId. This book Id is primary key in "books" collection which contains isbn number. The isbn number in "books" is primary key in "bookDetails" collection. I want bookName and author from the above document using join (aggregate in mongodb). The "books" and "bookDetails" collection are as follows :
"books"
{
"_id": 10001,
"issued": true,
"isbn": 1177009,
"issuedIds": []
}
"bookDetails"
{
"_id": 1177009,
"quantity": 5,
"available": 5,
"tags": [
"cse",
"ece",
"me",
"ce",
"ee",
"sems 1"
],
"bookIds": [
10001,
10002,
10003,
10004,
10005
],
"bookName": "book 1",
"author": "author 1"
}
I am working with nodejs and mongodb.
Thanks all.
I got the answer.Please tell me if something is wrong because I got the required output.
database
.collection('issueCart')
.aggregate([
{
$match: {uid: parseInt(id)}
},
{
$lookup: {
from: "bookDetails",
localField: "_id",
foreignField: "bookIds",
as: "book"
},
},
{
$unwind: "$book"
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: ["$book", "$$ROOT"]
}
}
},
{
$project: {"bookId": "$_id", author: "$author", name: "$bookName", _id: 0}
}
])