Mongoose nested reference population - node.js

I'm having trouble understanding some of the concepts behind mongoose's populate methods. I had an embedded approach working first, although, as I feared a big overhead of data and out-of-sync documents going around I tried changing the paradigm to ref other documents.
My Schema is similar to the following (removed irrelevant properties):
var userSchema = mongoose.Schema({
name: {type: String, default:''},
favorites:{
users:[{type: Schema.Types.ObjectId, ref: this}],
places:[{type: Schema.Types.ObjectId, ref: PlaceSchema}]
}
});
module.exports = mongoose.model('User', userSchema);
Now I'm trying to get a User's favorites like this:
User.findOne({_id: currentUser}).exec(function(err, user){
console.log(user);
if (err)
throw err;
if(!user){
console.log("can't find user");
}else{ // user found
user.populate('favorites.users');
user.populate('favorites.places');
// do something to the 'user.favorites' object
}
});
Although this doesn't work as intended, as both user.favorites.users and user.favorites.places come up undefined.
I thought that I could populate as above, but apparently, that's not the case. From what I read, I must be missing something indicating (maybe) the model of the ref'ed document? This flow is very new to me and I'm kinda lost.
Is there anyway I can get an array of users and places by populating my query result as above? I tried populate chained with exec and it doesn't work either. Is there a better way to achieve this result?
EDIT: In case it's needed, in the DB, a User document shows up as:
{
"_id": "56c36b330fbc51ba19cc83ff",
"name": "John Doe",
"favorites": {
"places": [],
"users": [
"56b9d9a45f1ada8e0c0dee27"
]
}
}
EDIT: A couple more details... I'm currently storing/removing the reference ObjectIds like this (note targetID is a String):
user.favorites.users.push({ _id: mongoose.Types.ObjectId(targetID)});
user.favorites.users.pull({ _id: mongoose.Types.ObjectId(targetID)});
Also, I need to populate the users and places with their respective documents aswell, I think that might not be clear in my original question.

Well, I figured out what I needed by paying proper attention to the docs (and also with #DJeanCar 's (+1'd) help/pointers).
By Mongoose's docs regarding Populating across multiple levels, I've reached this solution:
User.findOne({_id: currentUser})
.populate({path:"favorites.users", populate: "name"})
.populate({path:"favorites.places", populate: "name"})
.exec(function(err, user){
if(err)
throw err;
if(!user){
console.log("couldnt find source user");
}else{
// here, user.favorites is populated and I can do what I need with the data
}
});
Also, from what I could tell, you can also pass select: "field field field" in populate()'s options, should you need to filter the document fields you require after population.
Hope this helps someone with similar issues!

Try:
User
.findOne({_id: currentUser})
.populate('favorites.users')
.populate('favorites.places')
.exec( function (err, user) {
// user.favorites.users
// user.favorites.places
});

Related

how add elements in collection with Mongoose

I try to learn databases, specially mongoDb.
I use Mongoose and i try to add elements in my collection. I know that's a basic question but i don't understand where can be my problem
there is my code:
mongoose.connect("mongodb://localhost:27017/hackathon", (error)=>{
if (!error){
console.log("YESSS")
}
else {
console.log("rip")
}
var friend = { firstName: 'Harry', lastName: 'Potter' };
mongoose.connection.db.listCollections().toArray(function (err, names) {
console.log(names); // [{ name: 'dbname.myCollection' }]
module.exports.Collection = names;
});
});
the mongoose.connect function correctly, and the console.log gives me
{ name: 'mycol',
type: 'collection',
I tried to do :
collection.push but he doesn't know it.
i tried something like that
var dbo = db.db("mycol");
dbo.collection("mycol").insertOne(friend, function(err, res) {
if (err) throw err;
console.log("1 document inserted");
db.close();
});
(i found it on stack overflow but that do nothing)
Is there something i miss?
I think it's an easy thing and i just miss something in my comprehension.
Sorry for my english and thanks !
I used an other technique, i don't know if its the best solution so i created a schema (with differents elements, name, number-phone etc), and with a new and save, i found my solution.
Thanks for your answers

How to use model.findbyid and query inside it?

This is a simple question for you guys...I am wondering how can I acess a specific Id that is inside the "followers", after querying the User by its id. In other words i am searching for the user by its Id, and then I want to check the ids that are inside the followers.
For example: I have an Id saved in one variable "x", and I want to check if this Id "x" is inside of the followers array.
The model looks like this:
var UserSchema = new mongoose.Schema({
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
]
});
module.exports = mongoose.model("User", UserSchema);
And the code that I am using is this one:
User.findById(req.params.id,'followers', function(err,name){
if(err){
console.log(err)
} else{
console.log(name);
}
});
It looks like I can print the user id and the id's that are inside the "followers", but i am not managing to see if the desired Id is inside of "followers" array or not.Can anyone help me with it?
Thank you for your attention!
I think you need to create nested for loop because you have an array of users according to user schema and each user has an array of followers and to solve this, you need to create for loop for user schema and inside that loop you will loop through all followers inside that user and check whether the id that you have equals to their id.
Since followers is an array of IDs (document references), if the query is for a single follower match you can use a shorthand of {followers: SOME_ID} or if it is an array then you can use {followers: {$all: [SOME_ID, SOME_OTHER_ID]}}. See the Mongo documentation for more information.
Using an array to match for example would look like the following.
User.findOne({_id: req.params.id, followers: {$all: [SOME_ID, SOME_OTHER_ID]}}, function(err, name) {
if (err) {
console.log(err)
} else {
console.log(name);
}
});

Mongoose Updating Certain Fields of a Document

I'm trying to implement an update method for our API and I'm kinda new to the Node so I didn't know what would be the best practice to carry out the task of updating some fields of a document. Let me elaborate, we have a user model which keeps basic info of a user like name, age, sex, school, bio, birthday etc. Our update method should work as this, the request of the method includes the new values of the fields provided such as {bio:'newBio'} or {school:'newSchool', name:'newName'} I must update the provided fields with the provided data and leave the rest as they are. I was wondering what the best approach to the problem at hand would be. Thanks in advance
The easiest approach what i can think of is to use $Set to perform the update operations.
an example would be :
var updatedUsers= function(db, callback) {
db.collection('users').updateMany(
{ "_id": "value"},
{
$set: { bio: "new bio" }
}
,
function(err, results) {
console.log(results);
callback();
});
};
and invoke your above function as :
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
updatedUsers(db, function() {
db.close();
});
});

Expressjs rest api how to deal with chaining functionality

I am building a restful API using express, mongoose and mongodb. It works all fine but I have a question about how to deal with requests that contain more functionality than just one find, delete or update in the database. My user model looks as follows:
var UserSchema = new Schema({
emailaddress: {type: String, unique: true},
firstname: String,
lastname: String,
password: String,
friends: [{type: Schema.Types.ObjectId, unique: true}]
});
As you can see is the friends array just an array of ObjectIds. These ObjectIds refer to specific users in the database. If I want to retrieve an array of a user's friends I now have to look up the user that makes the request, then find all the users that have the same id as in the friends array.
Now it looks like this:
methods.get_friends = function(req, res) {
//find user.
User.findOne({_id: req.params.id}, function(err, user, next) {
if(err) next(err);
if(user) {
console.log(user);
//find friends
User.find({_id: {$in: user.friends}}, {password: 0}).exec(function (err,
friends, next) {
if(err) next(err);
if(friends) {
res.send(friends);
};
});
}
Would it be possible to seperate the lookup of the user in a certain method and chain the methods? I saw something about middleware chaining i.e. app.get('/friends', getUser, getFriend)but would that mean that I have to alter the req object in my middleware (getUser) method and then pass it on? How would you solve this issue? Would you perhaps change the mongoose model and save all friend data (means that it could become outdated) or would you create a method getUser that returns a promise on which you would collect the friend data?
I will be grateful for all the help I can get!
Thank you in advance.
Mongoose has a feature called population which exists to help in these kinds of situations. Basically, Mongoose will perform the extra query/queries that are required to load the friends documents from the database:
User.findOne({_id: req.params.id})
.populate('friends')
.exec(function(err, user) {
...
});
This will load any related friends into user.friends (as an array).
If you want to add additional constraints (in your example, password : 0), you can do that too:
User.findOne({_id: req.params.id})
.populate({
path : 'friends'
match : { password : 0 },
})
.exec(function(err, user) {
...
});
See also this documentation.

Querying nested documents using Mongoose (MongoDB)

I am starting out with mongodb and having hard time trying to query nested documents. I have two schemas:
var LinkSchema = new mongoose.Schema({
url: String,
name: String
});
var UserSchema = new mongoose.Schema({
name: String,
links: [LinkSchema]
});
As you can see, I am just tying to build a simple bookmarking tool. Each user has a name and a collection of links. Each link has a name and a url.
Now, what I am trying to do is for example, see if a link already exists in someone's links array. I would like to be able to do something like this (Trying to get vlad's link collection and then see if the query link already belongs to the collection or not):
app.get("/:query", function(req, res){
User.findOne({"name":"vlad"}, function(err, user){
user.links.find({"url":req.params.query}, function(err, foundLinks){
if(foundLinks){
res.send("link already exists!");
} else {
res.send("link doesn't exist!");
}
});
});
});
Of course, this code doesn't work, because apparently I can't do a "user.links.find()". I guess I can just do a user.links.map to extract only urls and then run a membership query against it. But I think this would be far from the right solution. There's gotta be a way to do something like this natively using DB queries. Can someone help? Thank you!
You can query an embedded document in mongoose like this
User.find({'links.url':req.params.query}, function(err, foundUsers){
// ---
});
and to find the links that belong to the user "vlad", you can write
User.find({name:'vlad','links.url':req.params.query}, function(err, foundUsers){
// ---
});
This will do the trick.
To find a specific link that belongs to a specific user you can do this
User.find({name:'vlad','links.url':req.params.query}, { 'links.$': 1 }, function(err, foundUsers){
// ---
});

Resources