Mongoose query in an Object - node.js

Question, I got a schema using mongoose which created an Object. I don't know how to query the author id which will return the list of the object under the author Id
var mongoose = require("mongoose");
var bookedSchema = new mongoose.Schema({
origin: String,
destination: String,
author: { id: { type: mongoose.Schema.Types.ObjectId, ref: "User"}, username: String},
Date: {type: Date, default: Date.now}
});
module.exports = mongoose.model("Booked", bookedSchema);
On my route I have query find({}) i want to query the author id instead of {} and it will return the list of object under the author ID. i tried the findById(author:{id:req.user._id}) but it returned null answer. any ideas how to do that. Thank You!
router.get('/myAccount', function(req, res){
Booked.find({}).exec(function(err, booked){
if(err){
console.log(err);
// res.redirect('back');
}else{
console.log(booked)
res.render('accounts/myAccount', {booking:booked});
}
});
});

you should use .populate() for refered documents to populate them.
change this:
Booked.find({}).exec(function(err, booked)
to:
Booked.find({}).populate('author').exec(function(err, booked)
or if you need to find a document by the refered author id you can use this:
Booked.find({}).populate({
path: 'author',
match: { id: { $eq: req.user._id }}
}).exec(function(err, booked)

Related

How can I merge results into a single JSON while querying data from two different models in node.js and mongoose?

I have two mongoose models in my app:
var UsersSchema = new Schema({
username: {type: String},
facebook_username: {type: String},
display_name: {type: String}
}
and
var CommentsSchema = new Schema({
user_id: {type: String, required: true},
text_content: {type: String},
photo_content_url: {type: String}
comment_date: {type: Date, default: Date.now},
created_at: {type: Date, default: Date.now},
}
currently each single comment contains user_id - a direct link to its author.
I created a node.js endpoint that takes POST query and returns JSON with all details about comments:
commentsRoutes.post('/comments', function (req, res) {
var startDate = req.body.startDate;
var endDate = req.body.endDate;
var query= {};
query.$and = [];
// and condition on start date
if(startDate != undefined) {
var startDate = new Date(req.param('startDate'));
var endDate = new Date(req.param('endDate'));
query.$and.push({"comment_date":{$gte: startDate}});
query.$and.push({"comment_date":{$lte: endDate}});
}
var finalquery = Comments.find(query)
finalquery.exec(function(err, comments){
if(err) {
res.send(err);
return;
}
res.json(comments);
});
});
This endpoint returns me a JSON with all the comments, however - I need to attach to each JSON details about its author - username, facebook_username and display_name (fetched based on user_id from UsersSchema). Can you tell me how could I do it?
user_id in CommentsSchema is a mongoose id of a specific user from UsersSchema
====== EDIT
Like #Antonio suggested in his answer below, I used populate in my case, it worked well and I saw merged JSON at the end. All user details were added, it looked like:
{
"text_content": "blah",
"photo_content_url": "http://www...",
"comment_date": "some date",
"created_at": "some date",
"user_id": { "username": "someUsername",
"facebook_username": "fbUsername",
"display_name": "sth" }
}
however - is there a way to include POST parameters in my endpoint that will apply to the user_id json?
Currently I'm sending a POST parameters startDate and endDate. But if I send also facebook_username - how can I include it in a query and find only comments that were written by this author?
I tried adding a simple condition:
var fb_username = req.body.facebookUsername;
query.$and.push({"facebook_username": fb_username});
but it didn't work because there's no such field facebook_username in CommentsSchema...
So how can I include condition attached to fields from UsersSchema that will limit results from CommentsSchema?
Since you have a reference to the corresponding user you could use populate.
Taking into account that try this:
Comments
.find({
comment_date: {
$gte: startDate,
$lte: endDate
}
})
.populate('user_id')
.exec(function(err, comments) {
if(err) {
return res.send(err);
}
res.json(comments);
});
By the way, not related to the main question, but since you are not doing any change in the server I think a GET would be a better option.
I also abbreviated your query, $and is not necessary here.
EDIT
You can add filtering to populate, in your case:
.populate({
path: 'user_id',
match: {
facebook_username: fb_username
}
})

Retrieve Array in Subdocument MongoDB

I have a Users model structure somewhat like this:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [models.Do.schema],
}
And the child "Do" schema somewhat like this (in a different file):
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
And I'm trying to figure out how to retrieve the todosDo array for the signed in user. This is what I've got so far:
// Get all "Do" todos from DB
// Experimenting to find todos from certain user
User.findById(req.user.id, function(err, user){
if(err){
console.log(err);
} else {
doTodos = user.todosDo, // this obviously doesn't work, just an idea of what I was going for
console.log(doTodos);
finished();
}
});
Am I referencing the child/parent wrong or am I just not retrieving the array right? Any help is greatly appreciated!
As far I guess you may want to edit as raw js objects so you need to use lean() function. without using lean() function user is mongoose object so you can't modify it.
can try this one:
User.findById(req.user.id)
.lean()
.exec(function (err, user) {
if(err){
console.log(err);
return res.status(400).send({msg:'Error occurred'});
}
if(!user) {
return res.status(400).send({msg:'User Not found'});
}
doTodos = user.todosDo;
console.log(user.todosDo); // check original todos
console.log(doTodos);
return res.status(200).send({doTodos : doTodos }); // return doTodos
});
and to refer child schema in parent schema from different model you can access a Model's schema via its schema property.
say in doSchema.js file
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
module.exports = mongoose.model( 'DoSchema', doSchema );
in user.js file
var DoModel = require('./doSchema');// exact path
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [DoModel.schema],
}
Thanks for your help everybody! My problem was that I needed to push all the newly created todos in the post route to todosDo, so then I could retrieve them at the get route. Everything's working now!

Nested query with mongoose

I have three models: User, Post and Comment
var User = new Schema({
name: String,
email: String,
password: String // obviously encrypted
});
var Post = new Schema({
title: String,
author: { type: Schema.ObjectId, ref: 'User' }
});
var Comment = new Schema({
text: String,
post: { type: Schema.ObjectId, ref: 'Post' },
author: { type: Schema.ObjectId, ref: 'User' }
});
I need to get all posts in which the user has commented.
I know it should be a very simple and common use case, but right now I can't figure a way to make the query without multiple calls and manually iterating the results.
I've been thinking of adding a comments field to the Post schema (which I'd prefer to avoid) and make something like:
Post.find()
.populate({ path: 'comments', match: { author: user } })
.exec(function (err, posts) {
console.log(posts);
});
Any clues without modifying my original schemas?
Thanks
You have basically a couple of approaches to solving this.
1) Without populating. This uses promises with multiple calls. First query the Comment model for the particular user, then in the callback returned use the post ids in the comments to get the posts. You can use the promises like this:
var promise = Comment.find({ "author": userId }).select("post").exec();
promise.then(function (comments) {
var postIds = comments.map(function (c) {
return c.post;
});
return Post.find({ "_id": { "$in": postIds }).exec();
}).then(function (posts) {
// do something with the posts here
console.log(posts);
}).then(null, function (err) {
// handle error here
});
2) Using populate. Query the Comment model for a particular user using the given userId, select just the post field you want and populate it:
var query = Comment.find({ "author": userId });
query.select("post").populate("post");
query.exec(function(err, results){
console.log(results);
var posts = results.map(function (r) { return r.post; });
console.log(posts);
});

Use populate() for two different schemas in MongoDB

I have two MongoDB collections - comments
var mongoose = require('mongoose');
var CommentSchema = new mongoose.Schema({
body: String,
author: String,
upvotes: {type: Number, default: 0},
post: { type: mongoose.Schema.Types.ObjectId, ref: 'Profile' }
});
mongoose.model('Comment', CommentSchema);
and users
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
userName: String,
userJobRole: String,
userEmail: String,
userPhone: String,
userTimeZone: String,
post: { type: mongoose.Schema.Types.ObjectId, ref: 'Profile' }
});
mongoose.model('User', UserSchema);
I want to use populate for each of these schemas in my get request. One for users and one for comments from these models.
router.get('/profiles/:profile', function(req, res, next) {
req.post.populate('users', function(err, post) {
if(err) { return next(err) }
res.json(post);
});
});
I can only figure out how to call one.
Does Mongoose allow you to populate from two schemas?
In order to populate multiple paths, you can pass a space delimited string of path names to the populate method on any document as follows:
Story
.find(...)
.populate('fans _creator') // space delimited path names
.exec()
This is taken directly from the Mongoose docs http://mongoosejs.com/docs/populate.html

Mongoose - Retrieving object from ref query

I've got the following schemas:
var userSchema = new Schema({
firstName: String,
lastName: String,
emailAddress: {type: String, set: toLower, index: {unique: true}},
});
var eventMemberSchema = new Schema ({
user: { type : Schema.ObjectId, ref : 'User' },
created: { type: Date, default: Date.now }
});
var eventSchema = new Schema({
id : String,
name : String,
startDate : Date,
endDate : Date,
venue : { type : Schema.ObjectId, ref : 'Venue' },
invitees : [eventMemberSchema],
});
What I'm trying to do, is query the events, with an invitation._id, and ultimately get back the user...
invitees->eventMember->user
So far i've got:
Event
.find({ "invitees._id": req.query.invitation_id })
.populate('user')
.run(function (err, myEvent) {
console.log('error: ' + err);
console.log('event: '+ myEvent);
})
This works, and console shows the output of myEvent...
(I realise I don't need the populate part of my mongoose query above for this... i'm just testing)
I'm struggling on how to get, what I'd basically describe as: myEvent.invitees.user
EDIT
As an update...
This works - however, it kind of sucks, as now i'll need to do another db operation to get the user (i realise ref in mongoose does this under the hood)
Event
.findOne({ "invitees._id": "4f8eea01e2030fd11700006b"}, ['invitees.user'], function(err, evnt){
console.log('err: '+ err);
console.log('user id: '+ evnt.invitees[0].user); //this shows the correct user id
});
Try
Event
.find({ "invitees._id": req.query.invitation_id })
.populate('invitees.user')
Update:
Here is a working gist.

Resources