MongoDB Unable to push to array with findOneAndUpdate() - javascript

Been working on a project recently where users subscribe to a class... however I am having trouble with pushing the class to the array... here is my code
const userSchema = new mongoose.Schema({
email: String,
password: String,
secret: String,
classes: [String]
});
const userModel = mongoose.model("Class", userSchema)
app.post("/subscribe", function (req, res) {
const newClass = req.body.subClass;
const id = req.user.id
const ObjectId = mongoose.Types.ObjectId;
userModel.findOneAndUpdate(
{ _id: new ObjectId(id) },
{ $addToSet: { letters: [newClass] } },
{ upsert: false }
);
});
I skipped through the program for useful info so please comment if more code is needed... looking to finally finish this up!

Related

why mongoose won't create an ObjectID

I have a JSON web token that once it's verified gives me a string that is the object id value of a user I want to find but it seems that every time I try to query with it being parsed into a mongoose object ID it never find my user, am I parsing it right ? I always get a 401 invalid token paylaod
logRoute.get('/user', (req, res) => {
let token = req.body;
User.findOne({_id: mongoose.Types.ObjectId(jwt.verify(token.token, 'secretkey').subject)}, (error, user) => {
if (error) {
console.log(error)
} else {
if (!user) {
res.status(401).send('invalid token payload')
} else {
let userData = {firstname: user.firstname, lastname: user.lastname, type: user.type}
res.status(200).send(userData)
}
}
})
})
my user data
{
"_id": {
"$oid": "5efc7d60ba7a8d3db08ca767"
},
"type": "teacher",
"firstname": "arandomfirstname",
"lastname": "arandomlastname",
"login": "random1",
"pwd": "arandompassword"
}
and
console.log(jwt.verify(req.body.token, 'P3GPROJECT'))
return me
{ subject: '5efc7d60ba7a8d3db08ca767', iat: 1593690718 }
EDIT:
User model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
_id: String,
pwd: String,
lastname: String,
firstname: String,
type: String
});
module.exports = mongoose.model('user', userSchema, 'user');
You've to remove _id: String from model schema, since You've ObjectId as a value (not a string) and it makes mongoose to convert Your input to string, instead of ObjectId.
so:
const userSchema = new Schema({
_id: String,
pwd: String,
lastname: String,
firstname: String,
type: String
});
becomes:
const userSchema = new Schema({
pwd: String,
lastname: String,
firstname: String,
type: String
});
or You can keep it as string but have to define the logic of generation of proper _id:
const uuid = require('short-uuid');
const userSchema = new Schema({
_id: {
type: Schema.Types.String,
default: uuid.generate(), // this will generate unique string
},
pwd: String,
lastname: String,
firstname: String,
type: String
});
P.S. Keep in mind if You want to use _id: String, You've to remove old documents or convert them manually to be {_id: "id here"} instead of {_id: ObjectId("id here")}.
Bonus:
Also I recommend You to create middlewares and separate token handling, from user authorization check.
It will allow You to not repeat access token checking in every request and will help You to understand in which step You're doing it wrong.
And then to do what You want.
jwt.verify throws error if it's invalid, that's why I put try catch arount.
I'm extracting subject as userId and if it does not exist it also means that token invalid, otherwise I pass handling to next handler in routing.
authorizeUser gets req.accessToken.userId from previous middleware and tries to check if user exists. If yes - so it stores complete user object in req.user, otherwise will end with 401 also.
and at last route handling goes last step which does logic or it can be empty as res.status(200).send(req.user)
const checkAccessToken = (req, res, next) => {
try {
const {subject: userId} = jwt.verify(req.body.token, 'secretkey');
if (!userId) throw new Error('Token data does not contain user id');
req.accessToken = {userId};
next();
}
catch (error) {
console.error(error);
res.status(401).send({message: 'Invalid access token'});
}
};
const authorizeUser = async (req, res, next) => {
try {
const user =
await User.findById(req.accessToken.userId)
.select('_id firstname lastname type')
.lean();
if (!user) {
return res.status(401).send({message: 'Unauthorized'});
}
req.user = user;
req.user.id = req.accessToken.userId;
next();
}
catch (error) {
console.error(error);
res.status(500).end();
}
};
logRoute.get(
'/user',
checkAccessToken, // checking token and putting user id to `req.user.id`
authorizeUser, // checking if user exists in database and putting it as `req.user` object
async (req, res) => { // handling some logic or simply returning `req.user` object
// some extra logic here...
res.status(200).send(req.user);
}
);

How can I create a post, i have two mongoose models ref'ing each other

Basically I wanna create a Post that has its author to the users name, the one who created it. I also want the Post to be pushed into the array of posts, which the user model has, that is ref'ing to "Post".
I have been googling and watching youtube videos but still i do not understand how i would go about to do this, also i read about populate, but i wanna create a new post and have the author to be the users name, also i want the post to be pushed into the array of posts that the user has.
How would I go about doing this ?
This is the post create controller
exports.postCreatePost = (req, res, ) => {
const {
title,
description,
context
} = req.body;
const post = new Post({
title,
description,
context,
author:
})
}
This is the model.js
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
bcrypt = require("bcryptjs");
const postSchema = new Schema({
title: String,
description: String,
context: String,
author: {
type: Schema.Types.ObjectId,
ref: "User"
}
});
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
posts: [{
type: Schema.Types.ObjectId,
ref: "Post"
}]
});
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
next();
});
const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
const userId = new mongoose.Types.ObjectId();
Either let client send you username/id and get it from req.body or when you are authenticating user simply pass the reference to the user to the body of request.
For example when client gives you id/username you can do something like this
const post = new Post({
title,
description,
context,
author: req.body.username or req.body.id
})
If you want to push use this
await findOneAndUpdate({_id: req.body.id}, {
"$push":
{
"posts": post._id
}
})

Mongoose & Express: POST data from subdocument

I'm new to Express/Mongoose and backend development. I am attempting to use a Mongoose subdocument in my Schema and POST data from a form to an MLab database.
I am successfully POSTing to the database when only using the parent Schema, but when I attempt to also POST data from the subdocument I am getting an undefined error. How do I properly POST data from a subdocument?
Here is my Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bookSchema = new Schema({
bookTitle: {
type: String,
required: true
},
author: {
type: String,
required: true
},
genre: {
type: String
}
});
const userSchema = new Schema({
name: String,
username: String,
githubID: String,
profileUrl: String,
avatar: String,
// I've tried this with bookSchema inside array brackets and also
//without brackets, neither works
books: [bookSchema]
});
const User = mongoose.model('user', userSchema);
module.exports = User;
Here is my route where I attempt to POST to the database:
router.post('/', urlencodedParser, (req, res) => {
console.log(req.body);
const newUser = new User({
name: req.body.name,
username: req.body.username,
githubID: req.body.githubID,
profileUrl: req.body.profileUrl,
avatar: req.body.avatar,
books: {
// All of these nested objects in the subdocument are undefined.
//How do I properly access the subdocument objects?
bookTitle: req.body.books.bookTitle,
author: req.body.books.author,
genre: req.body.books.genre
}
});
newUser.save()
.then(data => {
res.json(data)
})
.catch(err => {
res.send("Error posting to DB")
});
});
Figured it out. I wasn't properly accessing the values using dot notation.
books: {
// All of these nested objects in the subdocument are undefined.
//How do I properly access the subdocument objects?
bookTitle: req.body.books.bookTitle,
author: req.body.books.author,
genre: req.body.books.genre
}
No need to access .books inside of the books object. req.body.books.bookTitle should be req.body.bookTitle and so forth. Leaving this post up in case it helps someone else.

GET request returns empty array instead of array contents

I'm a newbie, but trying to figure out why my GET request returns an empty array even though I know that the Mongo database collection isn't empty. Each word form in the WordForm collection has a "lexicalform" key whose value is a reference to a LexiconEntry object in that collection. When I submit a GET request with a LexiconEntry ObjectId as a parameter, it returns an empty array in stead of the array contents. Here are my files:
The GET route in my controller:
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexiconentry: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});
The LexiconEntry model:
import mongoose from 'mongoose';
import WordForm from './wordform';
let Schema = mongoose.Schema;
let LexiconEntrySchema = new Schema({
lexicalform: String,
pos: String,
gender: String,
genderfull: String,
decl: String,
gloss: [String],
meaning: String,
pparts: [String],
tags: [String],
occurrences: Number,
wordforms: [{type: Schema.Types.ObjectId, ref: 'Form'}]
});
module.exports = mongoose.model('LexiconEntry', LexiconEntrySchema);
The WordForms model:
import mongoose from 'mongoose';
import LexiconEntry from './lexiconentry';
let Schema = mongoose.Schema;
let WordFormSchema = new Schema({
form: String,
gender: String,
case: String,
number: String,
lexicalform: {
type: Schema.Types.ObjectId,
ref: 'LexicalForm',
required: true
}
});
module.exports = mongoose.model('WordForm', WordFormSchema);
Based on your WordForm schema given above, there is no such property lexiconentry exists in WordForm schema as in your query below.
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexiconentry: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});
The property called lexicalform in WordForm schema resembled the one you are trying above. So, you can change your code as below with this.
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexicalform: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});

How to save nested mongoDB properties with mongoose?

Let say that i want to make a user schema with nested properties:
var userSchema = new mongoose.Schema({
username: { type: String, unique: true, lowercase: true },
/* ... some other properties ... */
profile: {
firstName: { type: String, default: '' },
/* ... some other properties ... */
},
});
module.exports = mongoose.model('User', userSchema);
Now if i want to save a user in a nodejs framework like ExpressJs, i will save it like so:
var user = new User({
username: req.body.username,
profile.firstName: req.body.firstName /* this is what i want to achive here */
});
user.save(function(err) {
if (!err) {
console.log('User created');
}
});
And i want to know if my Schema is good, or it's best practice to make all the properties in the root, as this:
var userSchema = new mongoose.Schema({
username: { type: String, unique: true, lowercase: true },
firstName: { type: String },
/* ... some other properties ... */
},
});
Your schema is good, but you cant define nested properties on the root of a new object as you did in your second code sample without quoting them. It should look like this:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var User = mongoose.model('User', {
username: String,
profile: {
firstName: String
}
});
var user1 = new User(
{
username: 'Zildjian',
'profile.firstName':'test'
});
user1.save(function (err) {
if (err) // ...
console.log('meow');
process.exit(0);
});
Although I would recommend nesting it properly like this
var user1 = new User(
{
username: 'Zildjian',
profile: {
firstName:'test'
}
});

Categories