Mongoose / Express Middleware update reference documents at insert time - node.js

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}
);
});

Related

Save _id current logged inuser to another mongodb collection

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).

How do I prevent Schema fields from being inserted into subdocument?

I'm making a dating app in node js and vue, and everything works however I wish to exclude password from being inserted into subdocument upon creation of a conversation. Right now I know that i can say .select('-password') when using User.findOne() but it doesn't work, when adding the user schema as a subdoc to my Conversations schema, which has user_one and user_two, each referring to a User schema. I need the password field, so I can't ommit it when creating a schema. Right Now my code looks like this:
User.findOne({ _id: fromUserId }, (errUserOne, userOne) => {
User.findOne({ _id: toUserId }, (errUserTwo, userTwo) => {
conversation = new Conversation({
_id: mongoose.Types.ObjectId(),
user_one: userOne,
user_two: userTwo
});
conversation.save();
const message = new Message({
_id: mongoose.Types.ObjectId(),
conversation_id: conversation._id,
message: req.body.message,
user_id: fromUserId
});
message.save();
res.sendStatus(201);
});
});
However this code saves the password to the Conversation collection, which I don't want.
User.find({ _id: :{ $in : [fromUserId,toUserId] }, { password:0 } , (err, userArray) => {
//your code goes here
});
Two things, You are querying two time for getting users. You can merge it into single query and for excluding the password field you can pass {password:0}. Which will exclude it in the documents.
also while you define Conversation schema don't make user_one and user_two type of user. Instead define only whatever properties of user you want to save like:
var Conversation = new Schema({
_id : ObjectId,
user_one : {
_id: ObjectId,
//all other fields
},
user_two : {
_id: ObjectId,
//all other fields
},
});

Upserting with Mongoose

I have a schema that is defined like
var UserSchema = mongoose.Schema({
user: {
ipAddress: String,
pollIDs: [{
id: String
}]
}
});
var User = module.exports = mongoose.model('User', UserSchema);
What I want to create is a route that checks the requests ip address, see if it exists in the database, if it doesn't create a new document with the ipAddress property set accordingly and the current req.body.poll_id to be an element in the pollIDs array.
However, if there is a document with that ip address I want the req.body.poll_id to be pushed into the pollIDs array.
I would demonstrate my first attempt, but I know that I've messed up the parameters on the findOneAndUpdate call.
Should be as simple as:
User.findOneAndUpdate(
{'user.ipAddress': req.body.ipAddress},
{$push: {'user.pollIDs': {id: req.body.poll_id}}},
{upsert: true, new: true},
(err, doc) => {...});
The upsert will take the query object and apply the update operation to it in the case where it needs to insert a new document.

(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