I have a mongoose schema having the structure of
const videoProjectsSchema = new Schema({
projectname:String,
projectmeta:String,
username:String,
createdat:{ type: Date, default: Date.now }
});
I need to retrieve a list of projectnames which belongs to a particular user. the array returned should only contain the names of the projects or else a list of projectname. This is my code(which returns all the projects objects)
videoProjects.find({ username: req.query.username }, function(err, proj) {
if (err) {
console.log(err);
}
res.json(proj);
});
You can use fields as second parameter to limit fields returned in object.
videoProjects.find({ username: req.query.username },{projectmeta:0,username:0,createdat:0,_id:0},
function(err, proj) {
if (err) {
console.log(err);
}else
res.json(proj);
});
OR
videoProjects.find({ username: req.query.username },{projectname:1,_id:0},
function(err, proj) {
if (err) {
console.log(err);
}else
res.json(proj);
});
Related
My User Schema is like this
{
_id:ObjectId("6e9465528a15ba6")
name: 'XYZ',
email: 'abc#gmail.com',
transactions: [
{
_id:ObjectId("5e946557a5128a15ba6"),
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
}
]
}
I want to update some fields of transaction with specific id. But not happening.
I tried the solution answered to
Mongoose, update values in array of objects this question.
May be my _id is of type ObjectId and id coming from my request is String?
So how can I solve this problem?
My code is like this but still getiing error user.transactions._id is not function
app.post('/api/update', function (req, res) {
const {
id,
email,
date,
type,
category,
description
} = req.body;
User.findOne({email}, function (err, user) {
if (err) {
console.error(err);
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!user) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
const objectId = mongoose.Types.ObjectId(id);
let transaction = user.transactions._id(objectId);
transaction.date = date;
transaction.type = type;
transaction.category = category;
transaction.description = description;
user.save((err, data) => {
if (err) return res.send(err);
return res.sendStatus(200);
});
}
});
});
fortunately I had to do something similar recently, so I suggest you to have a look at this page from Mongoose docs in the case I miss something, but basically you have to find the document first and then update its array of objects.
I'd try something like this:
User.findOne({ _id: your_user_id }, (err, data) => {
if (err) return console.log(err);
if (data) {
//Mongoose has a special method (id) for subdocuments
var transaction = data.transactions.id(your_transaction_id);
date: new Date(),
type: 'type2',
category: 'category2',
description: 'whatever',
//data.save() saves everything, document and subdocument
data.save((err, data) => {
if (err) return console.log(err);
done(null, data);
});
}
I have an API in which I am deleting 'course' documents in the following way:
module.exports.deleteCourse = function(req, res){
var courseid = req.params.courseid;
if(courseid){
Course.findByIdAndRemove(courseid, function(err, course){
if(err){
sendJSONResponse(res, 404, err);
return;
}
sendJSONResponse(res, 204, null)
});
} else{
sendJSONResponse(res, 404, {"message":"NoId"});
}
};
This is successful in deleting the course from the database, as is shown when attempting to find it by id.
The issue is that in user documents:
var instructorSchema = new mongoose.Schema({
name: {type: String,
unique: true,
required: true},
password: {type: String,
required: true},
courses: [course.schema]
});
If the document was pushed to the courses array it remains after the deletion method.
So my question. Is there a relatively painless way to keep this document updated after deletes?
Thanks.
Add a class method for course using statics, where you delete both the course and its dependencies.
Assuming you are storing ids in courses array:
var Instructor = require('./instructor');
courseSchema.statics = {
removeOneWithDependencies : function(id, done){
this.findByIdAndRemove(id, function(err, course){
if(err){
return done(err);
}
else{
//Removes the course id from courses array of all instructor docs
Instructor.update({courses: course._id}, { $pullAll: {courses: [course._id] } }, {multi: true}, function(err){ //http://stackoverflow.com/a/27917378/
if(err){
return done(err);
}
else{
return done();
}
})
}
});
}
}
In case you are storing course documents in courses array you need to change the update query to:
Instructor.update({"courses._id": course._id}, { $pull: {courses:{_id: course._id} } }, {multi: true}, function(err){ //http://stackoverflow.com/a/15122017/
Finally use the above method in your API:
module.exports.deleteCourse = function(req, res){
var courseid = req.params.courseid;
if(courseid){
Course.removeOneWithDependencies(courseid, function(err){
if(err){
return sendJSONResponse(res, 500, err);
}
else{
return sendJSONResponse(res, 204, null);
}
});
} else{
sendJSONResponse(res, 404, {"message":"NoId"});
}
};
I am in a bit of a pickle. Whenever I create a new resume as a logged in user it doesn't add the resume id as an array. I.e, ["20293", "2932392", "32903239"]
Instead, it overwrites the current resume id in the users schema. Here is the code
UserSchema
const UserSchema = new Schema({
_vId: {
type: String,
default: id.generate()
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
accountType: {
type: String,
enum: ['Alphaneer', 'Administrator', 'Support', 'PRO'],
default: 'Alphaneer'
},
email: {
type: String,
required: true,
trim: true
},
username: {
type: String,
required: true,
trim: true,
unique: true
},
bio: {
type: String,
default: "No bio provided."
},
// TODO: Hash the password before inserting as a document :)
password: {
type: String,
required: true
},
createdAt: {
type: String,
default: moment(new Date()).format("MMM DD, YYYY") // "Sun, 3PM 17"
},
resume: [ { type: mongoose.Schema.ObjectId, ref: "Resume" } ]
});
Where I post my resume
// POST /dashboard/resume/create
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//I am assuming that you have saved your resume and getting the saved object in `resume`, now update the logged in user in req.user
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findByIdAndUpdate(user, updateData, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
res.json(user);
}
})
}
})
});
gif of submitting new resumes
UPDATE:
error picture
UPDATED CODE:
// POST /dashboard/resume/create
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//I am assuming that you have saved your resume and getting the saved object in `resume`, now update the logged in user in req.user
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findById(user, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
user.resume.push(resume.id)
user.save(function(user) {
return res.json(user);
});
}
})
}
})
});
This is wrong:
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findByIdAndUpdate(user, updateData, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
res.json(user);
}
});
The resume field is an array and you are manipulating it as a string field. The method findOneAndUpdate do two things:
Find the document by it's id
Update it with the new data
The second argument is the new data to set. So, the second step is translated to:
User.upate({ _id: user }, { resume: resume._id });
Can you see what's wrong? resume must store an array of resume's id and your are setting a id as value. Obviously this will throw an MongooseError.
Your second shot is correct but has a typo error:
User.findById(user, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
user.resume.push(resume.id)
user.save(function(user) {
return res.json(user);
});
}
});
You must add the _id field since this is the ObjectID of the new created document (resume). So, you need to do user.resume.push(resume._id) instead.
Update
According with your last comment, you want to populate your User model, that is, through association id's retrieve all model data. In this case, is recommended that the resumes array change like this:
...
resumes: [
{
resume: {
type: Schema.Types.ObjectId,
ref: 'Resume'
}
}
]
To populate the User document with all Resume data you just need to reference the resume key in resumes field array.
User.findById(user, function(err, user) {
if (err) {
return res.json({ success: false, message: err.message });
}
user.resume.push(resume.id)
user.save(function(err, user) {
if (err) {
return res.json({ success: false, message: err.message });
}
// save was fine, finally return the user document populated
User.findById(user).populate('resumes.resume').exec(function(err, u) {
return res.json(u);
});
});
}
});
The populate method accepts a string with the fields that we want fill with it model data. In your case is an only field (resume). After run the query, you will get something like this:
{
_id: a939v0240mf0205jf48ut84sdfdjg4,
...,
resumes: [
resume: {
_id: f940tndfq4ut84jofgh03ut85dg9454g,
title: 'Some title'
},
...
]
}
Just to follow up on my comment regarding how I suggest you solve the issue:
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//Here, instead of creating a new key entry for resume, you rather push new resume-id into the resume property of the "found user".
//find, update and save the user
User.findOne({_id: req.user.sessionId}, function (err, userToUpdate) {
userToUpdate.toJSON().resume.push(resume.id);
userToUpdate.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
}
})
});
I left the rest of your code (saving new resume) untouched - I assume that part works. Give this a try and let me know if you encounter some problems.
I need to mention that I am totally aware of the fact that MongoDB is not a relational database in the first place. However it supports referencing other documents, hence some functionality should be supported, imo. Anyways, I have this relationship: a Company has many Departments and one Department belongs to one Company.
company.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var CompanySchema = new Schema({
name: {
type: String,
unique: true,
required: true
},
departments: [{
type: Schema.Types.ObjectId,
ref: 'Department'
}],
dateCreated: {
type: Date,
default: Date.now
},
dateUpdated: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Company', CompanySchema);
department.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var DepartmentSchema = new Schema({
name: {
type: String,
required: true
},
company: {
type: Schema.Types.ObjectId,
ref: 'Company'
},
dateCreated: {
type: Date,
default: Date.now
},
dateUpdated: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Department', DepartmentSchema);
Now, I am writing Node.js logic to manipulate this data using API. I get that if I create a new Department, I should add a reference to Company and I should create its reference in this Company's departments array. Simple. But what if a user changes the Company property of a Department? Say, the HR Department used to belong to Company A, but a user now moves it to Company B? We need to remove the reference to this department from Company A's array and push it to Company B. The same is when we want to delete a department. We need to find a company it belongs to and dis-associate it. My solution is working ATM, but seems rather clumsy.
routes.js
var Department = require('../../models/department'),
Company = require('../../models/company');
module.exports = function(express) {
var router = express.Router();
router.route('/')
.get(function(req, res) {
// ...
})
.post(function(req, res) {
// ...
});
router.route('/:id')
.get(function(req, res) {
// ...
})
.put(function(req, res) {
// First we need to find the department with the request parameter id
Department.findOne({ _id: req.params.id }, function(err, data) {
if (err) return res.send(err);
var department = data;
// department.name = req.body.name || department.name; Not relevant
// If the company to which the department belongs is changed
if (department.company != req.body.company._id) {
// We should find the previous company
Company.findOne({ _id: department.company }, function(err, data) {
if (err) return res.send(err);
var company = data;
// Loop through its departments
for (var i = 0; i < company.departments.length; i++) {
if (company.departments[i].equals(department._id)) {
// And splice this array to remove the outdated reference
company.departments.splice(i, 1);
break;
}
}
company.save(function(err) {
if (err) return res.send(err);
});
});
// Now we find this new company which now holds the department in question
// and add our department as a reference
Company.findOne({ _id: req.body.company._id }, function(err, data) {
if (err) return res.send(err);
var company = data;
company.departments.push(department._id);
company.save(function(err) {
if (err) return res.send(err);
});
});
}
// department.company = req.body.company._id || department.company; Not relevant
// department.dateUpdated = undefined; Not relevant
// And finally save the department
department.save(function(err) {
if (err) return res.send(err);
return res.json({ success: true, message: 'Department updated successfully.' });
});
});
})
.delete(function(req, res) {
// Since we only have id of the department being deleted, we need to find it first
Department.findOne({ _id: req.params.id}, function(err, data) {
if (err) return res.send(err);
var department = data;
// Now we know the company it belongs to and should dis-associate them
// by removing the company's reference to this department
Company.findOne({ _id: department.company }, function(err, data) {
if (err) return res.send(err);
var company = data;
// Again we loop through the company's departments array to remove the ref
for (var i = 0; i < company.departments.length; i++) {
if (company.departments[i].equals(department._id)) {
company.departments.splice(i, 1);
break;
}
}
company.save(function(err) {
if (err) return res.send(err);
});
// I guess it should be synchronously AFTER everything is done,
// since if it is done in parallel with Department.findOne(..)
// piece, the remove part can happen BEFORE the dep is found
Department.remove({ _id: req.params.id }, function(err, data) {
if (err) return res.send(err);
return res.json({ success: true, message: 'Department deleted successfully.' });
});
});
});
});
return router;
};
Is there any elegant solution to this case or it is just as it should be?
I see you have not yet captured the essence of the async nature of node.js ... for example you have a comment prior to department.save which says : and finally ... well the earlier logic may very will be still executing at that time ... also I strongly suggest you avoid your callback approach and learn how to do this using promises
I want to 2 model, one is user that can belongs to multiple groups, and another is group that can has multiple users.
This is my schemas and models, i don't know whether they the correct:
var Schema = mongoose.Schema;
var UserSchema = new Schema({
joinedGroups:[{type:Schema.Types.ObjectId, ref: 'Group'}]
}
);
var GroupSchema = new Schema({
createdBy: {type: Schema.Types.ObjectId, ref: 'User'},
joinedUsers:[{ type: Schema.Types.ObjectId, ref: 'User' }]
});
var User = mongoose.model('User',UserSchema);
var Group = mongoose.model('Group',GroupSchema);
when receive POST of url:/api/groups with the body of user._id, I want to join this user to new create group, besides, i want to join this new created group to user's joinedGroups and finally i want to response the client of the new group with users in it. Follow is my code of doing this:
app.post('/api/groups', function(req, res){
console.log(req.body);
var userId = req.body.user_id;
var group = {
createdBy : userId
};
Group.create(group, function(err,group){
if(err){
console.log('create group err');
res.send(err);
}
else{
console.log('create group success');
User.update({_id: userId},
{$push: {joinedGroups: group._id}},
function(err,user){
if(err){
console.log('update user err');
res.send(err);
}
else{
Group.update({_id: group._id},
{$push: {joinedUsers: user}},
function(err,group){
if(err){
console.log('update group err:' + err);
res.send(err);
}
else{
group.populate({path:'joinedUsers'},
function(err, group){
if(err){
console.log('populate group err');
res.send(err);
}
else{
console.log('populate group success');
res.json(group);
}
}
);
}
});
}
});
}
});
});
I feel it's really complex, and it occur error :
update group err:CastError: Cast to ObjectId failed for value "1" at path "joinedUsers"
So i want somebody help me with right solution to do this, thanks!
edit:
I also want to support join user in to existed group in PUT /api/group/:group_id like below:
var userId = req.body.user_id;
var groupId = req.params.group_id;
how to do that? thanks!
First of all, your realization is really complex and it can be simplified as this:
var userId = req.body.user_id; // this is an ObjectId
var group = {
createdBy: userId,
joinedUsers: userId
};
Group.create(group, function (err, group) {
if (err || !group) {
return res.send(err);
}
User.findById(userId, function (err, user) {
if (err || !user) {
return res.send(err);
}
user.joinedGroups.push(group._id);
user.save(function (err) {
if (err) {
return res.send(err);
}
group.populate('joinedUsers', function (err, group) {
if (err || group) {
return res.send(err);
}
res.json(group);
});
});
});
});
And the reason why you getting CastError error is: the update method returns 1 as second argument of callback if successfully updated. But your Group#joinedUsers filed expecting User reference.