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();
Related
I try to call a related list of logs for a certain user via Mongoose populate. Who can help me with finishing the response?
These are the schemes:
const logSchema = new Schema({
logTitle: String,
createdOn:
{ type: Date, 'default': Date.now },
postedBy: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'}
});
const userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
logs: { type: mongoose.Schema.Types.ObjectId, ref: 'logs' }
});
mongoose.model('User', userSchema);
mongoose.model('logs', logSchema);
Inspired by the Mongoose documentary (see above) and other questions in relation to this subject I think I got pretty far in making a nice get. request for this user. I miss the expierence to 'translate it' to Express.
const userReadLogs = function (req, res) {
if (req.params && req.params.userid) {
User1
.findById(req.params.userid)
.populate('logs')
.exec((err, user) => {
if (!user) { }); // shortened
return;
} else if (err) {
return; // shortened
}
response = { //question
log: {
user: user.logs
}
};
res
.status(200)
.json(response);
});
} else { }); //
}
};
The response in Postman etc would be something like this:
{
"log": {5a57b2e6f633ce1148350e29: logTitle1,
6a57b2e6f633ce1148350e32: newsPaper44,
51757b2e6f633ce1148350e29: logTitle3
}
First off, logs will not be a list of logs; it will be an object. If you want multiple logs for each user, you will need to store is as an array: logs: [{ type: mongoose.Schema.Types.ObjectId, ref: 'logs' }]
From the Mongoose docs: "Populated paths are no longer set to their original _id , their value is replaced with the mongoose document returned from the database by performing a separate query before returning the results." In other words, in your query user.logs will be the logs document for each user. It will contain all the properties, in your case logTitle, createdOn, and postedBy.
Sending user.logs as json from the server is as easy as: res.json(user.logs). So your query can look like this:
const userReadLogs = function (req, res) {
if (req.params && req.params.userid) {
User1
.findById(req.params.userid)
.populate('logs')
.exec((err, user) => {
if (!user) { }); // shortened
return;
} else if (err) {
return; // shortened
}
res.status(200).json(user.logs)
});
} else { }); //
}
};
I hope this makes it a little bit clearer!
I am using a pretty simple Node/Mongo/Express setup and am trying to populate referenced documents. Consider my schemas for "Courses" which contain "Weeks":
// define the schema for our user model
var courseSchema = mongoose.Schema({
teachers : { type: [String], required: true },
description : { type: String },
previous_course : { type: Schema.Types.ObjectId, ref: 'Course'},
next_course : { type: Schema.Types.ObjectId, ref: 'Course'},
weeks : { type: [Schema.Types.ObjectId], ref: 'Week'},
title : { type: String }
});
// create the model for Course and expose it to our app
module.exports = mongoose.model('Course', courseSchema);
I specifically want to populate my array of weeks (though when I changed the schema to be a single week, populate() still didn't work).
Here is my schema for a Week (which a Course has multiple of):
var weekSchema = mongoose.Schema({
ordinal_number : { type: Number, required: true },
description : { type: String },
course : { type: Schema.Types.ObjectId, ref: 'Course', required: true},
title : { type: String }
});
// create the model for Week and expose it to our app
module.exports = mongoose.model('Week', weekSchema);
Here is my controller where I am trying to populate the array of weeks inside of a course. I have followed this documentation:
// Get a single course
exports.show = function(req, res) {
// look up the course for the given id
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// my code works until here, I get a valid course which in my DB has weeks (I can confirm in my DB and I can console.log the referenced _id(s))
// populate the document, return it
course.populate('weeks', function(err, course){
// NOTE when this object is returned, the array of weeks is empty
return res.status(200).json(course);
});
};
};
I find it strange that if I remove the .populate() portion from the code, I get the correct array of _ids back. But when I add the .populate() the returned array is suddenly empty. I am very confused!
I have also tried Model population (from: http://mongoosejs.com/docs/api.html#model_Model.populate) but I get the same results.
Thanks for any advice to get my population to work!
below should return course with populated weeks array
exports.show = function(req, res) {
// look up the course for the given id
Course.findById(req.params.id)
.populate({
path:"weeks",
model:"Week"
})
.exec(function (err, course) {
console.log(course);
});
};
### update: you can populate from instance also ###
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// populate the document, return it
Course.populate(course, { path:"weeks", model:"Weeks" }, function(err, course){
console.log(course);
});
});
### Update2: Perhaps even more cleanly, this worked: ###
Course.findById(req.params.id, function (err, course) {
// error checks
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// populate the document, return it
console.log(course);
}).populate(course, { path:"weeks", model:"Weeks" });
here it seems like you are using course.populate() instead of Course.populate()
Use this code instead of yours,I change only one single word course.populate() to Course.populate()
In your case "course" is instance but you need to use Course(Model)
Course.findById(req.params.id, function (err, course) {
if (err) { return res.status(500).json({ error: err }); }
if (!course) { return res.sendStatus(404); }
// Guys in some case below three-line does not work in that case you must comment these lines and uncomments the last three-line
Course.populate('weeks', function(err, course){
return res.status(200).json(course);
});
// Course.populate({ path:"weeks", model:"Weeks" }, function(err, course){
// return res.status(200).json(course);
// });
};
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..
}
});
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.
I have an existing document that contains a nested array of elements (I'm not exactly sure of the terminology here). I have no problem creating the document. The problem arises when I need to insert a new element into the existing document. The code below may clarify what I'm trying to do:
Controller:
var Post = require('./models/post');
app.post('/post/:id/comment', function(req, res) {
var updateData = {
comments.comment: req.body.comment
comments.name: req.body.name,
};
Post.update({_id: req.params.id},updateData, function(err,affected) {
console.log('affected rows %d', affected);
});
});
Model:
var mongoose = require('mongoose');
var postSchema = mongoose.Schema({
post : String,
name : String,
created : {
type: Date,
default: Date.now
},
comments : [{
comment : String,
name : String,
created : {
type: Date,
default: Date.now
}
}]
});
module.exports = mongoose.model('Posts', postSchema);
So, each post can contain multiple comments. I'm just not sure how to insert a new comment into an existing post.
Since comments is declared as array, try to use
Post.update({_id:yourid}, { $push : { comments: { comment: '', name: '' } } }, ...
You can convert the object returned from mongodb in to an js object, and push new comment into the comments array. See the following:
var postSchema = require('./postSchema'); // your postSchema model file
postSchema.findOne({name: 'name-of-the-post'}, function (err, doc) { //find the post base on post name or whatever criteria
if (err)
console.log(err);
else {
if (!doc) { //if not found, create new post and insert into db
var obj = new postSchema({
post: '...'
name: '...'
...
});
obj.save(function (err) {
if (err)
console.log(err);
});
} else {
// if found, convert the post into an object, delete the _id field, and add new comment to this post
var obj = doc.toObject();
delete obj._id;
obj.comments.push(req.body.comment); // push new comment to comments array
postSchema.update(
{
'_id': doc._id
}, obj, {upsert: true}, function (err) { // upsert: true
if (err)
console.log(err);
});
}
console.log('Done');
}
});