const userSchema = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
posts: [{ type: Schema.Types.ObjectId, ref: "Post" }],
following: [{ type: Schema.Types.ObjectId, ref: "User" }]
}
};
I want to extract all the posts from all the Users in the 'following' array, put them into one single array, sort them and then display the first 20. I was wondering if that is possible within the cursor or if I have to load it into memory.
function createFeed(user) {
User.findOne({ name: user })
.populate({
path: "following",
populate: {
path: "posts"
}
})
//put all the posts into one array
.sort(...) //sort by time created
.limit(...) //only get the newest n posts
.exec((err, result) => {
if (err) console.log("error", err);
console.log("result", //sorted tweets array);
});
};
(I don't want to filter all the posts in my 'Posts' collection to check if they are made by the user since that would be a lot more expensive)
You can use distinct query in mongoDB
db.User.distinct('following',{})
If you are trying to filter your populate with a condition, then you should be doing this:
User.findOne({ name: user })
.populate({
path: 'posts',
match: { user: 'XXX' }
})
Even more better would be to query the posts with the user filter condition and then populate user details.
Related
const UserSchema = new Schema(
{
referrals: {
ref: 'User',
type: [mongoose.Schema.Types.ObjectId],
},
referredBy: {
ref: 'User',
type: mongoose.Schema.Types.ObjectId,
},
}
);
I want Mongoose to find users who have current user _id in referredBy reference.
In other words, eg: find all users who have '_IDOfSpecificUser' in their referredBy field and put all the found users in the array of referrals where user's _id is '_IDOfSpecificUser'.
How can I handle that in mongoose?
Simplest is using find
User.
find({ "referredBy" : "xxxxxxxxxxxx" }).
exec(function (err, users) {
if (err) return handleError(err);
console.log('The users are an array: ', users);
});
Refer to https://mongoosejs.com/docs/populate.html
If you want to convert bellow function to static method inside UserSchema, please refer to this https://mongoosejs.com/docs/api.html#schema_Schema-static and https://mongoosejs.com/docs/2.7.x/docs/methods-statics.html
I'm writing a forum module using Mongoose & Nodejs.
I have a collection of ForumPost objects which have a property "comments" which is a mongoose schema containing a reference property "author", which references a User model. I'm having trouble populating the "author" property for each of the items in the "comments" array.
Here's the ForumPost object definition:
var ForumPost = db.model("ForumPost", {
author: { type: Schema.Types.ObjectId, ref: "User", required: true, default: null, select: true},
comments: [new db.Schema({
author: { type: Schema.Types.ObjectId, ref: "User" },
comment: { type: String, default: ""},
})]
});
When I pull these from the database, i'm populating the "author" field of the forum post, which works fine as it's a basic populate operation.
I've been trying to populate the "author" field of the comments array this morning to no avail, my latest attempt is posted below.
I use the following query:
ForumPost.findOne({alliance_id: alliance._id, _id: post_id})
.populate("author")
.populate({
path: 'comments',
populate: {
path: 'comments.author',
model: 'User'
}
})
.select("comments")
.exec(function(error, post) {
return res.json(post);
});
Is this possible to populate the "author" field of the objects in the comments array of ForumPost in this single query?
Because you're not doing any nested population, you can include both paths in a single populate call:
ForumPost.findOne({alliance_id: alliance._id, _id: post_id})
.populate('author comments.author')
.select('author comments')
.exec(function(error, post) {
return res.json(post);
});
I have a mongodb running (MEAN environment) with two collections (users and books). One of those collections (myusers) contains an array of Objectids (references to documents of books collection) as such:
var UserSchema = new Schema({
[...],
externalids: [{type: Schema.Types.ObjectId, ref: 'Books', required: false}]
}, {collection: 'myusers'});
At runtime, I'd like to constantly fill that array (externalids) with ids of new book documents. This is how I do it:
var newBook = new Book({ ... });
newBook.save(function (err){
if (err){
//whatever
}else{
User.update({ _id: req.user._id }, { $set: { externalids: newBook._id }}, function(err){
//whatever
});
}
});
Unfortunately, I can't use something like:
externalids: externalids.push(newBook._id)
I even tried:
User.update({ _id: req.user._id }, { $push: { externalids: newBook._id }}
But it wouldn't help either.
Thus, the array always only contains one value (the latest) and won't be filled up. Is there a quick way to append more values? Would be nice if there was a quicker way than reading the array content first, storing it to a local temporary array, append the new value and write the entire array back...
Cheers
Igor
Try $addToSet instead of $push to avoid duplicate id.
User.update({ _id: req.user._id }, { $addToSet : { externalids: newBook._id }}
What can be your issue is that you previously use $set to update an user. This will initially create externalids as string, not array, hence you cannot use $push/$addToSet on externalids afterwards. To correct that, try to update your user externalids as an array first:
User.update({ _id: req.user._id }, { $set : { externalids: [] }}
I have this setup in my MongoDB
Items:
title: String
comments: [] // of objectId's
Comments:
user: ObjectId()
item: ObjectId()
comment: String
Here's my Mongoose schema:
itemSchema = mongoose.Schema({
title: String,
comments: [{ type: Schema.Types.ObjectId, ref: 'comments' }],
});
Item = mongoose.model('items', itemSchema);
commentSchema = mongoose.Schema({
comment: String,
user: { type: Schema.Types.ObjectId, ref: 'users' },
});
Comment = mongoose.model('comments', commentSchema);
This is where I get my items along with the comments:
Item.find({}).populate('comments').exec(function(err, data){
if (err) return handleError(err);
res.json(data);
});
How do I populate the comments array with it's respective user? Since each comment has a user ObjectId()?
One more way (easier) to do this:
Item
.find({})
.populate({
path: 'comments',
populate: { path: 'user',
model: 'users' }
})
.exec(function(err, data){
if (err) return handleError(err);
res.json(data);
});
As a complete example calling populate on the result objects:
Item.find({}).populate("comments").exec(function(err,data) {
if (err) return handleError(err);
async.forEach(data,function(item,callback) {
User.populate(item.comments,{ "path": "user" },function(err,output) {
if (err) throw err; // or do something
callback();
});
}, function(err) {
res.json(data);
});
});
The call to .populate() in the form invoked from the model takes either a document or an array as it's first argument. So you loop through the returned results for each item and call populate this way on each "comments" array. The "path" tells the function what it is matching.
This is done using the "async" version of forEach so it is non-blocking, but generally after all the manipulation all of the items in the response are not only populated with comments but the comments themselves have the related "user" details.
Simpler
Item
.find({})
.populate({
path: 'comments.user',
model: 'users' }
})
.exec(function(err, data){
if (err) return handleError(err);
res.json(data);
});
To add one final method that people may want to use to select only particular fields from sub-documents, you can use the following 'select' syntax:
Model.findOne({ _id: 'example' })
.populate({
path: "comments", // 1st level subdoc (get comments)
populate: { // 2nd level subdoc (get users in comments)
path: "user",
select: 'avatar name _id'// space separated (selected fields only)
}
})
.exec((err, res) => {
// etc
});
I use this:
.populate({
path: 'pathName',
populate: [
{
path: 'FirstSubPathName',
model: 'CorrespondingModel',
},
{
path: 'OtherSubPathName',
model: 'CorrespondingModel',
},
{
path: 'AnotherSubPathName',
model: 'CorrespondingModel',
},
]
});
it's the more easier way that i find to do this.I expect to help. :)
To populate sub-sub document and populate from multiple schemas
ProjectMetadata.findOne({id:req.params.prjId})
.populate({
path:'tasks',
model:'task_metadata',
populate:{
path:'assigned_to',
model:'users',
select:'name employee_id -_id' // to select fields and remove _id field
}
})
.populate({
path:'client',
model:'client'
})
.populate({
path:'prjct_mgr',
model:'users'
})
.populate({
path:'acc_exec',
model:'users'
})
.populate({
path:'prj_type',
model:'project_type'
}).then ( // .. your thing
or you can do it in following manner ..
ProjectMetadata.findOne({id:req.params.prjId})
.populate(
[{
path:'tasks',
model:TaskMetadata,
populate:[{
path:'assigned_to',
model:User,
select:'name employee_id'
},
{
path:'priority',
model:Priority,
select:'id title'
}],
select:"task_name id code assign_to stage priority_id"
},
{
path:'client',
model:Client,
select:"client_name"
},
{
path:'prjct_mgr',
model:User,
select:"name"
},
{
path:'acc_exec',
model:User,
select:'name employee_id'
},
{
path:'poc',
model:User,
select:'name employee_id'
},
{
path:'prj_type',
model:ProjectType,
select:"type -_id"
}
])
I found the second method (of using array) more useful when I had to get multiple sub-sub documents of same parent.
do it try it`s working
find Project and get project related populate Task and Perticular Task User find
db.Project.find()
.populate({
path: 'task',
populate: { path: 'user_id'}
})
.exec(async(error,results)=>{
})
You can also populate subdocument by this in mongoose -
Item.find({}).populate("comments.user")
Check the screenshot below. This thing works like charm!!!
This worked for me:
i.e. no need for model
.populate({
path: 'filters',
populate: {
path: 'tags',
populate: {
path: 'votes.user'
}
}
})
.populate({
path: 'members'
})
Just a simple query, for example with a double ref in the model.
Schema / Model
var OrderSchema = new Schema({
user: {
type : Schema.Types.ObjectId,
ref : 'User',
required: true
},
meal: {
type : Schema.Types.ObjectId,
ref : 'Meal',
required: true
},
});
var OrderModel = db.model('Order', OrderSchema);
Query
OrderModel.find()
.populate('user') // works
.populate('meal') // dont works
.exec(function (err, results) {
// callback
});
I already tried something like
.populate('user meal')
.populate(['user', 'meal'])
In fact only one of the populates works.
So, how do is get two populates working ?
You're already using the correct syntax of:
OrderModel.find()
.populate('user')
.populate('meal')
.exec(function (err, results) {
// callback
});
Perhaps the meal ObjectId from the order isn't in the Meals collection?
UPDATE:
This solution remains for the version 3.x of Mongoose http://mongoosejs.com/docs/3.8.x/docs/populate.html but is no longer documented for >= 4.x versions of Mongoose and so the answer from #JohnnyHK is the only valid one for now on.
ORIGINAL POST
If you're using Mongoose >= 3.6, you can pass a space delimited string of the path names to populate:
OrderModel.find()
.populate('user meal')
.exec(function (err, results) {
// callback
});
http://mongoosejs.com/docs/populate.html
This has probably been resolved already, but this is my take on multiple & deep population in Mongodb > 3.6:
OrderModel.find().populate([{
path: 'user',
model: 'User'
}, {
path: 'meal',
model: 'Meal'
}]).exec(function(err, order) {
if(err) throw err;
if(order) {
// execute on order
console.log(order.user.username); // prints user's username
console.log(order.meal.value); // you get the idea
}
});
There are probably other ways to do this, but this makes very readable code for beginners (like me)
The best solution in my opinion is arrays when you are populating more than one foreign field on the same level. My code shows that I have multiple populates for different levels.
const patients = await Patient.find({})
.populate([{
path: 'files',
populate: {
path: 'authorizations',
model: 'Authorization'
},
populate: {
path: 'claims',
model: 'Claim',
options: {
sort: { startDate: 1 }
}
}
}, {
path: 'policies',
model: 'Policy',
populate: {
path: 'vobs',
populate: [{
path: 'benefits'
}, {
path: 'eligibility',
model: 'Eligibility'
}]
}
}]);
As you can see, wherever I needed more than one field of a document populated, I encased the populate key in an array and provided an array of objects, each object having a different path. Most robust and concise way to do it, in my opinion.
You can use array syntax:
let results = await OrderModel.find().populate(['user', 'meal']);
You can also select which properties you want from each populate:
let results = await OrderModel.find().populate([{path: 'user', select: 'firstname'}, {path: 'meal', select: 'name'}]);
Latest mongoose v5.9.15
has ability to take array of populate fields
so you can do,
.populate([ 'field1', 'field2' ])
You can try:
OrderModel.find()
.populate('user')
.populate('meal')
.exec(function (err, results) {
// callback
});
or with array options
OrderModel.find()
.populate([
{
path: "path1",
select: "field",
model: Model1
},
{
path: "path2",
select: "field2",
model: Model2
}
])
.exec(function (err, results) {
// callback
});
In model file do something like:-
doctorid:{
type:Schema.Types.ObjectId, ref:'doctor'
},
clinicid:{
type:Schema.Types.ObjectId, ref:'baseClinic'
}
In js file for adding operator use Something like:-
const clinicObj = await BaseClinic.findOne({clinicId:req.body.clinicid})
const doctorObj = await Doctor.findOne({ doctorId : req.body.doctorid}) ;
**and add data as:-**
const newOperator = new Operator({
clinicid:clinicObj._id,
doctorid: doctorObj._id
});
Now, while populating
apiRoutes.post("/operator-by-id", async (req, res) => {
const id = req.body.id;
const isExist = await Operator.find({ _id: id }).populate(['doctorid','clinicid'])
if (isExist.length > 0) {
res.send(isExist)
} else {
res.send("No operator found");
}
});
i have same problem , but my mistake not in populate , i have an error in Model
if you do this
uncorrected
user: {
type: [Schema.Types.ObjectId],
ref: 'User'
}
correct
user: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
you must put array around of object like this
To populate multiple fields with array of objects in controller/action function, model of both is already referred in schema of post
post.find({}).populate('user').populate('comments').exec(function (err,posts)
{
if(err)
{
console.log("error in post");
}
return res.render('home',{
h1:"home Page",
posts:posts,
});
});
I think you are trying to the nested population you can visit official docs
User.
findOne({ name: 'Val' }).
populate({
path: 'friends',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'friends' }
});