How to do nested find in mongoose? - node.js

I have a user model which has todolists field, in the todolists field I want to get the specific todolist by id. my query is like this:
User.find({_id: user._id, _creator: user, todoList: todoList._id}, 'todoLists') // how do I query for todoList id here? I used _creator this on populate query.
Can I also do a search on a Usermodel field like this?
User.todoLists.find({todoList: todoList._id})
I haven't tested this yet because I am still modifying my Graphql schema and I am new in mongoose.I would really appreciate Links and suggestions. Help?

Assuming your models looks like this:
const todoListSchema = new Schema({
item: { type: String },
}, { collection: 'todolist' });
const userSchema = new Schema({
todoList: [todoListSchema],
}, { collection: 'user' });
mongoose.model('user', userSchema);
mongoose.model('todoList', todoListSchema);
Now you have multiple ways to do that:
1. Using the array filter() method
reference
User.findById(_id, (err, user) => {
const todoList = user.todoList.filter(id => id.equals(tdlId));
//your code..
})
2. Using mongoose id() method
reference
User.findById(_id, (err, user) => {
const todoList = user.todoList.id(tdlId);
//your code..
})
3. Using mongoose aggregate
reference
User.aggregate(
{ $match: { _id: userId} },
{ $unwind: '$todoList' },
{ $match: { todoList: tdlId } },
{ $project: { todoList: 1 } }
).then((user, err) => {
//your code..
}
});

Related

updating nested documents in mongoDB(node.js)

i am trying to update a value in the object of my embedded schema(comments schema) whose value i had previously stored 0 by default. i have tried all the ways to update but none of the stackoverflow answer worked.
my code is
var checkedBox = req.body.checkbox;
User.updateOne({_id: foundUser._id},{$set :{comments:{_id :checkedBox,cpermission:1,}}},function(err,updatec){
if(err){
console.log(err);
}
else{
console.log("successfull");
console.log(updatec);
}
});
i had comment schema nested in user schema,here foundUser._id is the particular users id,and checkedBox id is the embedded objects particular id. previously my cpermission was 0,set by default,but now i want to update it to 1. although this is updating my schema,but deleting the previous images and comments in the schema aswell.
where am i going wrong?
here is my schema
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
cpermission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
upermission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
First, you need to convert checkbox in the array, as it will be a string if you select a single element
Then wrap it with mongoose.Types.ObjectId as a precaution
Then you can use arrayFilters to update multiple matching array elements
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
checkedBox = checkedBox.map(id => mongoose.Types.ObjectId(id))
User.updateOne(
{ _id: foundUser._id }, // filter part
{ $set: { 'comments.$[comment].cpermission': 1 } }, // update part
{ arrayFilters: [{ 'comment._id': {$in: checkedBox }}] }, // options part
function (err, updatec) {
if (err) {
console.log(err);
}
else {
console.log("successfull");
console.log(updatec);
}
});
your comment is the array of documents. if you want to update an element of an array must be select it. for that must be added another condition to the first section of updateOne then in seconde section use $ for update selected element of the array.
User.updateOne(
{_id: foundUser._id, 'comments._id': checkedBox},
{
$set: {'comments.$.cpermission': 1}
}
, function (err, updatec) {
if (err) {
console.log(err)
}
else {
console.log('successfull')
console.log(updatec)
}
})
for more information, you can read this document form MongoDB official website.
Array Update Operators
var checkedBox = req.body.checkbox;
User.updateOne(
{ _id: foundUser._id, "comment._id": checkedBox },
{ $set: { "comment.$.cpermission": 1 } },
function (err, update) {
if (err) {
console.log(err);
} else {
console.log("successfull");
console.log(update);
}
}
);

push items to an array in a object using Mongoose

I want to update the posts array in the data object.
It dosent work here is my schema:
const mongoose = require('mongoose');
const Post = require('./post');
const Review = require('./comment')
const User = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: {
type: String,
required: true
}
// necessary details needed for the user to initially working with
data: {
posts: [
Post
],
following: [String]
}
}
});
I have already tried:
User.update({_id: user}, {data:{$push: {posts: post
}}}).then(data=>{
console.log(data);
})
but it dosent work. Thank you.
Try this code:
User.update(
{ "_id": user },
{
"$push":
{
"data.posts": post
}
}
).then(data => {
console.log(data);
})
Read more about $push here.
Hope this solves your query!

get by id in mongoose

If I want to get by id a only subdocument that is an array into a document, how I need to call it? I have these models
/* Build */
var buildSchema = new Schema({
buildName: String,
package: [packageSchema],
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}
})
var Build = mongoose.model('build', buildSchema)
/* Project */
var projectSchema = new Schema({
projectName: String,
build: [buildSchema],
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}
})
var Project = mongoose.model('project', projectSchema)
my get function and route is this:
router.get('/build/:id', function (req, res, next) {
var token = helperJwt.decodeToken(req)
jwt.verify(token, helperJwt.secret, function (error, decoded) {
if (error) {
res.status(401)
} else {
Project.findOne({ _id: req.params.id}, 'build', function (err, build) {
if (err) {
res.status(401)
} else {
build.findOne({build: build._id}, 'postedBy', 'email', function (err, getBuild) {
if (err) {
res.status(401)
} else {
return status(200).json(build)
}
})
}
})
}
})
})
that is good or how can I do for get only one build?
I'm using vue as front end but it doesn't recognise only one build data.
I don't know how to findById a subdocument.
I want to bring something like:
{
"_id" : ObjectId("5a26fbfd1866893baf14b175"),
"postedBy" : "5a2021539f1d222bfc915fa2",
"projectName" : "El pipiripau",
"build" : [
{
"buildName" : "bdfbdgf",
"postedBy" : ObjectId("5a2021539f1d222bfc915fa2"),
"_id" : ObjectId("5a26fc0dbe4de0039cdae0c7"),
"package" : []
},
}
If you use buildSchema inside of projectSchema, the build array will contain subdocuments so they won’t have ids.
You could set a custom id field in your build schema and then reference that in your query, but you would have to take care of setting those ids when you save your project docs.
Alternatively, if your build docs are being stored in a separate collection (as you have a build model) then they will have an _id field and you could store an array of references to the build model in your projectSchema:
var projectSchema = new Schema({
projectName: String,
build: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'build'
}],
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}
})
And then, assuming req.params.id is the id of a build doc, you could find projects where the build array has the id and then use populate to replace the array of build ids with the corresponding subdocuments:
Project
.find({ build: req.params.id })
.populate('build')
.exec(function (err, projects) {
if (err) {
// Handle error
} else {
// For each project, filter builds for just the one matching the id param
projects.forEach(project => {
project.build = project.build.filter(item => item._id == req.params.id )
})
// Handle result
}
})
Here are the docs on population:
http://mongoosejs.com/docs/populate.html
I hope this helps.
https://mongoosejs.com/docs/api.html#model_Model.findById
// Find the adventure with the given `id`, or `null` if not found
await Adventure.findById(id).exec();
// using callback
Adventure.findById(id, function (err, adventure) {});
// select only the adventures name and length
await Adventure.findById(id, 'name length').exec();

Mongoose update is not working

My question here is why isn't the watchlistSchema.update(function (error) { { $push: { watchlist: req.body.stockAdded } }}); line updating the existing schema for the watchlist attribute? When I use this update nothing happens and it returns null. When I change it to watchlistSchema.save it work but it creates and entirely different document. I would like to basically check for the user and watchlist and if they both exist together I would like to push a string into the watchlist array. I am very new to mongoose so it is a bit confusing.
var Schema = mongoose.Schema;
var watchlistSchema = new Schema({
watchlist: [{ }],
user: String
});
var Watchlist = mongoose.model('Watchlist', watchlistSchema, "watchlist");
app.post('/watchlistPost', function (req, res) {
var watchlistSchema = Watchlist({
'watchlist': req.body.stockAdded,
'user': req.user.username
});
Watchlist.findOne({
$and: [{
'watchlist': req.body.stockAdded,
}, {
'user': req.user.username
}]
}, function (err, list) {
if (list) {
res.status(200).send({ "success": "Updated Successfully", "status": 200 });
} else {
if (req.user) {
watchlistSchema.update(function (error) {
{ $push: { watchlist: req.body.stockAdded } }
});
} else {
}
}
})
});
Your update statement needs to contain the "find" query. So it can appy the update condition to all documents matching the specified query.
change your code to something like:
var Schema = mongoose.Schema;
var watchlistSchema = new Schema({
watchlist: [{ }],
user: String
});
var Watchlist = mongoose.model('Watchlist', watchlistSchema, "watchlist");
app.post('/watchlistPost', function (req, res) {
var watchlistSchema = Watchlist({
'watchlist': req.body.stockAdded,
'user': req.user.username
});
var query = {
$and: [{
'watchlist': req.body.stockAdded,
}, {
'user': req.user.username
}]};
Watchlist.update(query, { $push: { watchlist: req.body.stockAdded } }, ==your callback to check stuff==);

Mongoose slice array, in populated field

I have the following mongoose schemas:
The main one is userSchema which contains an array of friends,
friendSchema. Each friendSchema is an object that contains an array of messageSchema. The messageSchema is the deepest object, containing the body of the message.
var messageSchema = new mongoose.Schema({
...
body: String
});
var conversationsSchema = new mongoose.Schema({
...
messages: [messageSchema]
});
var friendSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
conversation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Conversation',
},
}, { _id : false });
var userSchema = new mongoose.Schema({
...
friends: [friendSchema]
});
When retrieving specific user's friend, I populate its friends profiles, and if a conversation exist, I populate the conversation too.
How can I slice conversations.messages array, which resides in the population of the conversationobject ? I don't want to return the whole messages.
var userId = req.userid;
var populateQuery = [{ path:'friends.user',
select: queries.overviewConversationFields },
{ path:'friends.conversation' }];
User
.find({ _id: userId }, { friends: 1 })
.populate(populateQuery)
.exec(function(err, result){
if (err) { next(err); }
console.log(result);
}
EDIT(1) : I tried
.slice('friends.conversation.messages', -3)
EDIT(2) : I tried in populate query
{ path:'friends.conversation', options: { 'friends.conversation.messages': { $slice: -2 } }
EDIT(3) : For now, I can achieve what I want, slicing the array after the query is executed. This isn't optimized at all.
A little workaround that works.
I didn't found how to $slice an array that resides in a populated field.
However, the $slice operator works perfecly on any array, as long as its parent document has'nt been populated.
1) I decided to update the conversationSchema by adding an array containing both user's Id involved in the conversation :
var conversationsSchema = new mongoose.Schema({
users: [type: mongoose.Schema.Types.ObjectId],
messages: [messageSchema]
});
2) Then, I can easily find every conversation my user participates to.
As I said, I can properly slice the messages array, because nothing has to be populated.
Conversation.find({ users: userId },
{ 'messages': { $slice: -1 }}, function(err, conversation) {
});
3) Finally all I have to do, is to query all friends and conversations separately, and put back everything together, with a simple loop and a _find.
That would do more or less the same procedure of a Mongo population
Using async.parallel for more efficiency :
async.parallel({
friends: function(done){
User
.find({ _id: userId }, { friends: 1 })
.populate(populateQuery)
.exec(function(err, result){
if (err) { return done(err);}
done(null, result[0].friends);
});
},
conversations: function(done){
Conversation.find({ users: userId }, { 'messages': { $slice: -1 }}, function(err, conversation) {
if (err) { return done(err); }
done(null, conversation)
});
}}, function(err, results) {
if (err) { return next(err); }
var friends = results.friends;
var conversations = results.conversations;
for (var i = 0; i < friends.length; i++) {
if (friends[i].conversation) {
friends[i].conversation = _.find(conversations, function(conv){
return conv._id.equals(new ObjectId(friends[i].conversation));
});
}
}
});
// Friends contains now every conversation, with the last sent message.

Resources