How to insert a user's id into a separate collection in Mongodb? - node.js

I have two models, one that holds the user and one that holds their text(Blog). I simply want to append the user's id every time they post their text so I can later go ahead and query for my own use.
This is what I have tried doing but nothing happens. Do we need to use req.body.user_id? Why is req.session.user not working(not being added along with the new instance of Blog on save) when I intentionally made it carry the user's id
router.route("/blog/add").post((req, res) => {
// Retrieve the uid form the user
// Save uid to the db along with the whole Blog instance
let blog = new Blog({
user_blog: req.body.user_blog,
createdAt: req.body.createdAt,
date: req.body.date,
user_id: req.session.user //Not working even though it holds the id already
});
blog.save()
.then(blog => {
res.status(200).json({
message: "Blog saved succeccfully"
});
})
.catch(err => {
res.status(400).send("Failed to save users blog");
});
});
Schema Blog
let Blog = new Schema({
user_blog: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
date: {
type: String,
default: moment(new Date()).format("MMM Do YY, HH:mm")
},
user_id: {
type: mongoose.Schema.Types.ObjectId // This will be the users own id
}
}, { collection: "users_blogs" });
The user_id is not being appended, why?

Related

How to push unique data to an array in MongoDb document with Mongoose?

I want to write a logic for course enrollment for my app. When user click on Enroll button the api executes the code and add the course to the logged in user's document in MongoDB user collection.
The user can enroll to many courses but not the same one again and again. I want the course title and slug to be unique.
Here is the code of CourseSchema:
import mongoose from "mongoose";
const RegisterCourseSchema = new mongoose.Schema({
title: {
type: String,
required: true,
unique: true,
},
slug: {
type: String,
required: true,
unique: true,
},
});
export default mongoose.models.Course ||
mongoose.model("Course", RegisterCourseSchema);
I am doing as follow:
const { userId, title, slug } = req.body;
let course = new Course({
title: title,
slug: slug,
});
const result = await User.findByIdAndUpdate(
{ _id: userId },
{
$addToSet: {
courses: [{title: req.body.title, slug: req.body.slug}],
},
},
{ strict: false }
);
await result.save();
registerCourse();
res.status(200).json("success true");
This code runs and add the course to the logged in user document. See the picture below.
The problem in this code is that whenever this code is run this will add the duplicate the data. I actually want that If some one pass the same title and the same slug again and again then this will not be added to the MongoDB database.

Mongoose query to filter the collection based on nested field

We want to see if mongoose could do heavy lifting to get the user's role for a given Organization Name and UserId.
This can be done easily by first finding out the organization data and use javascript to filter out based on User's ID. But i think it would give better performance if the query can do it instead of doing outside the mongo collection.
What we want to try something like below, but it is not giving the role of the user correctly.
Query (not working)
public async getUserOrgRole(orgName: string, userId) {
const result = await this.organizationModel
.findOne({ name: orgName, 'orgMembers.userId': userId })
.select('orgMembers.role')
.exec();
if (result) {
const orgMember = _.get(result, 'orgMembers');
return orgMember[0].role;
}
return null;
}
Query (working but we want the above query to work instead of pulling entire document)
public async getUserOrgRole(orgName: string, userId) {
const org = await this.organizationModel.findOne({ name: orgName })
if (!org)
return null;
const userInOrg = org.orgMembers.find(om => om.userId === userId)
console.log('--getUserOrgRole', userInOrg)
if (userInOrg)
return userInOrg.role
return null;
}
Schema
const UserOrgSchema = new Schema({
role: { type: Schema.Types.String, enum: ['MEMBER', 'OWNER', 'ADMIN'], default: 'MEMBER' },
inactive: { type: Schema.Types.Boolean, default: false },
userId: { type: Schema.Types.String, required: true },
});
const OrganizationSchema = new Schema({
name: { type: Schema.Types.String, unique: true },
picture: { type: Schema.Types.String },
orgMembers: { type: [UserOrgSchema] },
createdAt: { type: Schema.Types.Date, default: Date.now },
updatedAt: { type: Schema.Types.Date, default: Date.now },
});
You almost got it right. The reason why your attempt does not quite work is explained by Sunil. No matter what filters you apply to .find(), it will always return the whole document. If you want to select specific subdocuments, you need to do that using an additional select operator. This ought to work:
const result = await this.organizationModel
.findOne({ name: orgName, "orgMembers.userId": userId })
.select({
orgMembers: {
$elemMatch: {
userId,
},
},
})
.select("orgMembers.role")
.exec();
Note the use of $elemMatch! It does exactly what you wanted - filters the subdocuments by selecting only the ones that match the provided filter.

How do I get the logged-in User ID when creating a post request for that user without using req.params.id?

Let's say I have an API with a /posts endpoint. Each post in my Mongoose schema has a reference to the user that created it, and each user has an array of references to the posts they created.
When posting as a logged-in user, in order to save that reference I need to send the current logged-in user ID along with the content of the post to the /posts endpoint. I would prefer not to do so through some nested query like /users/:id/posts and then send req.params.id. I would like to post directly to /posts but send the user.id in the request somehow.
User model:
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
});
Posts model:
const PostSchema = new Schema({
content: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
Create new post (need a way to get the user ID since it won't actually be in req.params)
exports.createPost = async function(req, res, next) {
try {
const { content } = req.body; // ideally would get user ID from here
const post = await db.Post.create({
content,
user: req.params.id
});
const postUser = await db.User.findById(req.params.id);
postUser.posts.push(post.id);
await postUser.save();
const newPost = await db.Post.findById(post.id);
const {id, created} = newPost;
return res.status(201).json({
id,
content,
created
})
}
catch(err) {
return next(err);
}
}
I know I'm probably missing something obvious, but I appreciate any suggestions.

MongoDB Creating document and on success create another document

I am working on application using Node.js and MongoDB. I have a particular use case wherein I create a new user and on success add the user's ObjectId into another collection called 'cities' by fetching the user's city if not existing or create a new one and append User's ObjectId to Subscriber's List field of the city document.
The Schemas look like below:
CitiesSchema:
var CitiesSchema = new Schema({
City:{
type: String
},
SubscribersList: [{type: Schema.ObjectId}]
});
User Schema:
var UsersSchema = new Schema({
emailId: {
type: String,
required: 'Mandatory field'
},
city: {
type: String
},
subscribedOn: {
type: Date,
default: Date.now
},
lastEmailSent: {
type: Date,
default: null
},
isActive: {
type: Boolean,
default: true
}
});
Please let me know how I can tackle this in the cleanest way possible or is there any design pattern I need to follow ?
You can use the then notation to continue processing after you have created your User. Like this
UserSchema.create({
emailId: 'email#exmaple.com',
city: 'Rome'
})
.then(user => {
// For example if you want to push to the city of the user
CityScema.update({
City: user.city
}, {
$push: {
SubscribersList: user._id
}
}).then(() => {
res.sendStatus(200);
}).catch(err => res.status(500).send(err));
}).catch(err => {
// Handle your errors here
console.error(err);
});
Make sure you check the Mongoose docs on Models for more information

add a item inside a nested schema mongoose with addToSet

I know populating schemas is not a new question but I am having a little trouble following the logic on this in regards to multiple schemas. I am working with
"mongoose": "^4.8.5",
"express": "^4.15.0",
I have a schema with a collection of caffeine drinks. When a user selects a drink i would like for that drink to be assigned to the user.
** If at any point I am missing something simple in the architecture please let me know. This project has been my intro to mongodb.
I am reading through populating on the mongoose documentation http://mongoosejs.com/docs/populate.html.
Essentially, if I am to assign the drinks to the list it looks like I want to add them as a reference in an array. This was my approach with caffeine_list
const SelectedDrinks = require('./userDrinks');
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
caffeine_list: caffeine_list: [ // attempting to reference selected drinks
{
type: mongoose.Schema.Types.ObjectId,
ref: 'SelectedDrinks'
}
]
})
SelectedDrinks comes from the schema below. I added a reference to the user as the creator below
const User = require('./user');
let userDrinkSchema = new mongoose.Schema({
creator : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
caffeine: Number,
mgFloz: Number,
name: String,
size: Number,
updated_at: {
type: Date,
default: Date.now()
}
});
This is where I start to get confused. I initially tried populate but could not get it going. If that was correct please let me know.
In regards to my task of adding a selected drink to the user I used addToSet. I was hoping that this would give me the drink info. I did my set up like so....
const User = require('../../models/user');
const UserDrinks = require('../../models/userDrinks');
router.post('/addDrink', (req, res, next) => {
let newDrink = new UserDrinks({
creator: req.body.creator,
caffeine: req.body.caffeine,
mgFloz: req.body.mgFloz,
name: req.body.name,
size: req.body.size,
updated_at: req.body.updated_at
});
newDrink.save( (err) => {
if(err) {
res.send(err);
} else {
User.findOne({ _id: newDrink.creator}, (err, user) => {
user.caffeine_list.addToSet(newDrink)
user.save( function (err) {
if(err) {
console.log(err);
}else {
res.status(201).json(newDrink);
}
})
})
}
})
});
However, after i do a post in postman I check caffeine_list and the result is
"caffeine_list" : [
ObjectId("58d82a5ff2f85e3f21822ab5"),
ObjectId("58d82c15bfdaf03f853f3864")
],
Ideally I would like to have an array of objects being passed with the caffeine info like so
"caffeine_list" : [
{
"creator": "58d6245cc02b0a0e6db8d257",
"caffeine": 412,
"mgFloz": 218.7,
"name": "1.95 Perfect Drink!",
"size": 42.93,
"updated_at": "2017-03-24T18:04:06.357Z"
}
]
Change your else part with below code instead of findOne and save use update
User.update(
{ _id: newDrink.creator},
{ $addToSet:{
caffeine_list: newDrink
}}).exec(function (err, updatedrink){
if(err) {
console.log(err);
}else {
res.status(201).json(updatedrink);
}
})
Although I am not sure this is the best approach I did find this to be give me the result that I was desiring. I had to make two small changes and I was able to get the caffeine_list to give me the desired response
I had to access the schema for selected drinks
const SelectedDrinks = require('./userDrinks').schema; //** need schema
Afterwards I was able to change
caffeine_list: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'UserDrinks' // name of the file
}
]
to
caffeine_list: [SelectedDrinks]
Now that I have the schema I am able to add the drinks directly into the caffeine_list on the UserSchema.

Resources