Save _id current logged inuser to another mongodb collection - node.js

I want to save the user collection _id from the currently logged in user to the projects collection when a new project is saved to the database. The project gets saved but the createdby field isn't in the db after saving. I've followed this example: https://mongoosejs.com/docs/2.7.x/docs/populate.html
I found this to be the right way for referencing in several examples .
What am I missing?
project model
const mongoose = require('mongoose');
const projectSchema = new mongoose.Schema({
projectTitle:{
type:String,
required: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Users' ,
},
createdAt: {
type: Date,
default: Date.now,
required: true
},
});
module.exports = mongoose.model('Projects', projectSchema)
route post project form
// #desc Process project add form
// #route POST /project
router.post('/', ensureAuth, async (req, res) => {
req.body.user = req.user.id
await Project.create(req.body)
res.redirect('/')
})

Though not fully clear from your code snippets, my guess is you lack this piece of code in the controller:
// #desc Process project add form
// #route POST /project
router.post('/', ensureAuth, async (req, res) => {
req.body.user = req.user.id // not sure if this is necessary?
req.body.createdBy = req.user._id
await Project.create(req.body)
res.redirect('/')
})
When using mongoose.Schema.Types.ObjectId, .id is just a getter method that returns a string representation of the ObjectId (so basically ._id.toString()). To properly save the reference, you should save the ._id of the User document (i'm assuming in my code that req.user holds the full document so you can access the ObjectId).

Related

Problem with a Cast using Router.put with mongoDB in MERN app

I want to attach the router.put which will update the Boolean(isOn) in toggle button but firstly I wanted to try how it works and now I am facing the problem.
const express = require("express");
const router = express.Router();
const Buttons = require('../../models/Buttons');
// GET buttons
// This request works perfect
router.get('/', (req,res) => {
Buttons.find()
.sort({name: 1})
.then(buttons => res.json(buttons))
});
// PUT buttons
// This one doesnt work at all
router.put('/:name', function(req,res,next) {
Buttons.findByIdAndUpdate({name: req.params.name},
req.body).then(function(){
Buttons.findOne({name: req.params.name}).then(function(buttons){
res.send(buttons);
});
});
});
module.exports = router;
Model of buttons has only name: String, required: true and isOn: Boolean, required: true and data in db looks like that:
Can you tell me what did I do wrong here?
Code of Buttons modal :
const mongoose = require('mongoose');
const Schema = mongoose.Schema
const buttonSchema = new Schema ({
name: {
type: String,
required: true
},
isOn: {
type: Boolean,
required: true
}
});
module.exports = Buttons = mongoose.model("buttons", buttonSchema);
You ca only use findByIdAndUpdate when you want to update the document by matching the _id of the document
If you want to match the document by any other property (such as name in your case), you can use findOneAndUpdate
Write your query like this
router.put('/:name', function(req,res,next) {
Buttons.findOneAndUpdate({name: req.params.name},
req.body).then(function(){
Buttons.findOne({name: req.params.name}).then(function(buttons){
res.send(buttons);
});
});
});
Hope this helps
Please add your id as well which you have to update in your database
Model.findByIdAndUpdate(id, updateObj, {new: true}, function(err, model) {...
This error occur because findByIdAndUpdate need id of an Object which we want to update so it shows ObjectId error. so pass your id from front end and use it in your back-end to update particulate data.
step 1 : you can create new endpoint for update-name
router.put('/update-name', function(req,res,next) {
//here you can access req.body data comes from front-end
// id = req.body.id and name = req.body.name then use it in your
Buttons.findByIdAndUpdate(id, { name : name }, {new: true}, function(err, model) {...
}
step 2 : try this endpoint /update-name and pass your data in Body from postman

Saving data to array in mongoose

Users are able to post items which other users can request. So, a user creates one item and many users can request it. So, I thought the best way would be to put an array of users into the product schema for who has requested it. And for now I just want to store that users ID and first name. Here is the schema:
const Schema = mongoose.Schema;
const productSchema = new Schema({
title: {
type: String,
required: true
},
category: {
type: String,
required: true
},
description: {
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
requests: [
{
userId: {type: Object},
firstName: {type: String}
}
],
});
module.exports = mongoose.model('Product', productSchema);
In my controller I am first finding the item and then calling save().
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findById(productId).then(product => {
product.requests.push(data);
return product
.save()
.then(() => {
res.status(200).json({ message: "success" });
})
.catch(err => {
res.status(500).json({message: 'Something went wrong'});
});
});
};
Firstly, is it okay to do it like this? I found a few posts about this but they don't find and call save, they use findByIdAndUpdate() and $push. Is it 'wrong' to do it how I have done it? This is the second way I tried it and I get the same result in the database:
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findByIdAndUpdate(productId, {
$push: {requests: data}
})
.then(() => {
console.log('succes');
})
.catch(err => {
console.log(err);
})
};
And secondly, if you look at the screen shot is the data in the correct format and structure? I don't know why there is _id in there as well instead of just the user ID and first name.
Normally, Developers will save only the reference of other collection(users) in the collection(product). In addition, you had saved username also. Thats fine.
Both of your methods work. But, second method has been added in MongoDB exactly for your specific need. So, no harm in using second method.
There is nothing wrong doing it the way you have done it. using save after querying gives you the chance to validate some things in the data as well for one.
and you can add additional fields as well (if included in the Schema). for an example if your current json return doesn't have a field called last_name then you can add that and save the doc as well so that's a benefit..
When using findById() you don't actually have the power to make a change other than what you program it to do
One thing I noticed.. In your Schema, after you compile it using mongoose.modal()
export the compiled model so that you can use it everywhere it's required using import. like this..
const Product = module.exports = mongoose.model('Product', productSchema);

Mongoose / Express Middleware update reference documents at insert time

Given the below code, and the fact that I'm using mongoose populate() in my API, how can I update the user reference document with the checkin _id, at the same time?
I feel it's like the chicken/problem. Thanks!
router.post('/', (req, res, err) => {
var checkin = new Checkin(req.body);
var user = new User(checkin.user);
checkin.save({
'user': checkin.user,
'checkin_comment': checkin.checkin_comment,
'rating_score': checkin.rating_score
});
});
The Checkin model has the following:
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
.. and the User one has this:
checkins: [{
type: Schema.Types.ObjectId,
ref: 'Checkin'
}],
Regarding your model definition, it seems that attribute checkins on the User model is optional. The user can exists without checkin reference.
If that's the case then create the user first, then the checkin and update the user with its id.
Edit: Given your comments, we assume that the user already exists in DB and its id available in userId.
So something like:
checkin.save()
.then((checkinDoc) => {
return User.findOneAndUpdate(
{_id: userId},
{$push:{checkins: checkinDoc._id}},
{new: true}
);
});

(Mongoose) How to get user._id from session in order to POST data including this user._id

I am new in Mongoose.
I'm developing a MEAN stack To do list with user authentification.
(In other words, a user can register login and create, get, update and delete the to do's).
It means 2 schemas: 'users' and 'tasks'
With a relationship one to many: a user can have many tasks, many tasks belongs to a user.
This is how it looks the 'tasks' Schema:
const TaskSchema = new Schema({
title:{
type: String,
required: true
},
owner:{
type: Schema.Types.ObjectId,
ref:'User'
}
});
In order to build the CRUD methods I will need the user._id as a 'owner' attribute, otherwhise any user could have access to the tasks list, create update or delete a task,
To get the user._id it I was thinking two options:
Angular2 at the front end would get the user._id from the localStorage of the browser where was stored previously to keep the user logged in.
const user = localStorage.getItem('user');
And then send it in the same object as I send the 'title' attribute.
I think this option is too insecure as anyone from the front-end could send any id.
Get the current user._id at the back-end from the sessions. (I would't know how to do it though). And include it in the new task object at the POST method, something like this:
.post('/task', function(req, res, next){ function(req, res, next){
var task = new Task({
title: req.body.title,
owner : req.user._id /// Does not do nothing
});
if(!task.title){
res.status(400);
res.json({
"error":"Bad Data"
});
} else{
task.save(task, function(err, task){
if(err){
res.send(err);
}
res.json(task);
});
}
});
Taking the second option (unless the former is better), how would you build the POST method?
Concretely, how can I get the current user._id from the session and include it the new Task object?
I look forward of receiving your feedback soon.
Thank you.
A bit different but:
User Model:
var UserSchema = new mongoose.Schema({
username: String,
password: String
});
Tasks Model:
var taskSchema = mongoose.schema({
text: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Task", taskSchema);
Create a task with post route:
var text = req.body.text;
var author = {
id: req.user._id,
username: req.user.username
};
var newTask = {text: text, author: author};
Task.create(newTask, function(err, addedTask){
// what you wanna do
});
Similarly with edit/update you can use a put route (edit) and delete route (method override for delete) with a 'checkTaskOwnership' middleware and then
Task.findByIdAndUpdate / Task.findByIdAndRemove
I think you should store user's _id in session. To store _id in the session use passport. It handles Authentication really well, and on successful authentication it stores users credentials in req.user. This req.user is present in all the requests. So for any Route, you can get the user's _id from req.user object. you wont need to send user's _id from the Frontend.
While saving Task use this:
var task = new Task({
title: req.body.title,
owner : req.user._id
});
task.save(function(err){...});
Read PassportJS docmentation to get more detailed information about Session and Authentication.

MongooseJS using find to find by reference

I have this Mongoose Schema.
var mongoose = require('mongoose')
, dev = require('../db').dev();
var schema = new mongoose.Schema({
date: {
type: Date,
default: Date.now()
},
company: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company'
},
questionnaire: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Questionnaire'
}
});
module.exports = dev.model('Survey', schema);
I want to find only the surveys which have a specific company id. How do I do that? I tried (with my Express handler):
app.get('/survey', function(req, res) {
Survey.find({ company: req.query.company })
.populate('questionnaire')
.exec(function(err, surveys) {
return res.json(surveys);
});
});
In your latest comment you say that the company field of the Surveys collection is actually a string and not and ObjectId and that's why this isn't working. Because your schema definition declares company as an ObjectId, Mongoose will cast your req.query.company value to an ObjectId and then query for documents in Surveys where their company property is an ObjectId with the same value. So if company is a string in the database it won't match.
If you update the company values in Surveys to be ObjectIds instead of strings then this will work.
have you tried 'company._id
app.get ('/survey', function (req, res) {
Survey.find ({ 'company._id': req.query.company }).populate ('questionnaire').exec (function (err, surveys) {
return res.json (surveys);
});
});
What worked for me (and it is hinted in the accepted answer) was to make sure that the documents are created through mongoose (i.e through your express server) because this will force the schema onto them, and thus the ids will be stored as objectIds.
Initially I had created a document manually through the database using the objectIds as simple strings, and this causes the query not to work because mongo casts the id (from your server) as an ObjectId.

Resources