Express.js PUT Request not updating MongoDB - mongodb

I'm working on a project that is basically a super watered down social media website.
I have a chunk done already, but I'm having some issues creating a put request to my mongodb. Basically, I want to send a put request to update a numeric value to be able to have a like counter on each post.
What I'm trying do here is send a put request with a specific post id. I'm storing the post id in a hidden text box to reference it. This is pug formatted HTML:
input.form-control(type='hidden' value=item.id id='postId' placeholder='' name='postId' required='false')
form(method='PUT' action='/update/{{post._id}}')
button(type='submit') Like
Then in my router.js file I'm basically trying to take in that id and set the likes field in the Post schema to 1 (just for testing).
router.put('/update/:id', function (req, res, next) {
let id = {
_id: ObjectID(req.params.id)
};
Post.update({_id: id}, {$set:{'likes': 1}}, (err, result) => {
if(err) {
throw err;
}
res.send('user updated sucessfully');
});
});
Here is my post schema
var mongoose = require("mongoose");
var PostSchema = new mongoose.Schema({
postText: {
type: String,
unique: false,
required: true,
trim: true
},
usernameText: {
type: String,
unique: false,
required: true,
trim: true
},
likes:{
type: Number,
unique: false,
required: false
}
});
var Post = mongoose.model("Posts", PostSchema);
module.exports = Post;
Any and all help would be highly appreciated, thank you

You can't change the ObjectId. The ObjectId is generated by MongoDB and can't be changed by the user using query functions.
If you want to assign a unique id to each user for example, then create a separate field in your schema.

You cannot change the ID
'PUT' method is not supported directly as far as I know. You need method override

Related

MongoDB joining user and likes collection based on reference ids

I am trying to create a little social network using ExpressJS and MongoDB. I have a little problem relating to likes and posts collection. I know you can embed a likes inside a posts collection, but I have decided to separate both of the collection and use reference ids so I can join them later on. The main problem I have currently is this, how do I include the likes reference on the posts collection?
Let's say my posts schema looks something like this:
const PostSchema = new Schema({
content: { type: String, required: true },
isLiked: false,
}, { timestamps: true });
and my likes schema looks something like this:
const LikeSchema = new Schema(
{
// The user who is liking the post.
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
// The post that is being liked.
question: {
type: Schema.Types.ObjectId,
ref: 'Question',
required: true
},
},
{ timestamps: true }
);
I wanna make it so that whenever I try to query the posts collection, I can also get the likes embedded in it by referencing the collection and not modifying the schema to have embedded likes in it.
An example response:
{
_id: ObjectId("test"),
content: 'A post',
isLiked: false,
likes: ["A user object here based on the `likes collection`"]
}
You have to obtain them before sending the response:
Find all the likes of that post, something similar to Like.find({ question: <postId> })
Then you can resolve the users of that likes, in the command above you can concatenate .populate('user') with the mongoose populate feature
If you are interested only to the user object and not the entire like object, you can extract resolved user: const users = likes.map(x => x.user)
Then you can add the users array to the post object and sending the final object as response

Cannot set the reference to the user document in courses mongoose

I am currently working on a RESTful API, and I am trying to reference the users schema in the courses document such that, when a POST request gets sent to the route of the course, a course is created in the DB and has as one of its fields a reference to the user that created it. However, for the life of me, I cannot figure out why the "user" field is not appearing when I post. There seem to be quite a few of these questions here on Stack so I may just be adding to the pile, but I tried their solutions and they did not work for me
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
emailAddress: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
var CourseSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User'}, //FOR some reason this is not showing up on any courses created using the
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
estimatedTime: {
type: String
},
materialsNeeded: {
type: String
}
});
var User = mongoose.model('User', userSchema);
var Course = mongoose.model('Course', CourseSchema);
module.exports = {Course, User};
Do you see anything in here that would preclude the user field from appearing when a new course is created?
I have attached some screenshots to further explain.
This first image is a screen of the currently authenticated user credentials (fake data obviously). This is the user that is sending the POST request for the new course. I would expect his information to be attached to the course (see screenshot 3)
This image shows the body of the request that is sent. You can see that the key-value pairs match what is in the CourseSchema. I would expect that the "user" field would be created once the POST request is sent.
This last image is some dummy data that is the expected result.
Thanks all for taking a look at this!
User field will not be automatically added to the course document. You have to manually set the user field in the request body itself or while creating a course.
Example of the course body to be sent:-
{
user: "userId",
title: "test",
description: "test",
estimatedTime: "test",
materialsNeeded: 1
}
Also, the result of this will not include the whole user document as you have mentioned in the expected result. It will only return the userId. However, while accessing the course you can populate the user field to get the whole user document. Example for the same
Course.find({...query}).populate("user")

MongoDB Mongoose save object with nested objects

I'm new to MongoDB and I'm creating a simple db with Mongoose with the following models: User, Game and Players.
So, one user contains none or many games. Every game has to players, and each player refers to a user. Like this (I simplified the schemas for clarity):
const UserSchema = new Schema({
name: String,
games: [{
type: Schema.Types.ObjectId,
ref: 'game'
}]
});
const GameSchema = new Schema({
mode: Number,
players: {
type: [{
type: Schema.Types.ObjectId,
ref: 'player'
}],
required: true
}
});
const PlayerSchema = new Schema({
order: Number,
isWinner: Boolean,
user: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
}
});
So, now in the frontend I want to send a petition to the backend to create a new game for users Joe (_id:11111) and Bob (_id:22222) and so I send a POST to /api/games with the body { users: [ 11111, 22222 ] }
Now my question is, for the backend to create a new game, it also has to create 2 players. What's the best way to achieve this?
In the Game.create() method, shall I retrieve the data, create and save the players, create the game, assign the players, save the game, and also update the users and add the game ids?
I also read about Mongoose middleware, where you can set certain functions to be executed before or after some operations. So maybe it's better:
pre function before Game.create, to create the players
post function before Game.create, to update the users
This last one seems cleaner.
What's the best way? Maybe another one I have not considered?
Thanks
I would suggest you using the post and pre functions defined in the mongoose middleware. They're pretty straightforward and neat to use. It will probably solve your problem.
Here is a personal example of a problem we had; In our case, we had to assign a userId from a sequence in the database. We used the following code:
var UserSchema = new Schema({
username: { type: String, required: true, unique: true },
id: { type: String },
...
});
UserSchema.pre('save', function(next) {
let doc = this;
let id = 'userSeq'
Sequence.findByIdAndUpdate(id, { $inc : {nextSId : 1} }, function(error,data) {
if(error)
next(error)
doc.id = data.nextSId-1;
next();
})
});
My suggestion is that before you create the game, you can search for the users and add a reference to the game. If I were you, I would use the findAndModify query of mongodb to find the users or create if they do not exist yet.

Clean up dead references with Mongoose populate()

If a user has an array called "tags":
var User = new Schema({
email: {
type: String,
unique: true,
required: true
},
tags: [{
type: mongoose.Schema.Types.ObjectId,
ref:'Tag',
required: true
}],
created: {
type: Date,
default: Date.now
}
});
and I do a populate('tags') on a query:
User.findById(req.params.id)
.populate("tags")
.exec(function(err, user) { ... });
If one of the tags in the list has actually been deleted, is there a way to remove this dead reference in "tags"?
Currently, the returned user object IS returning the desired result -- ie. only tags that actually exist are in the tags array... however, if I look at the underlying document in mongodb, it still contains the dead tag id in the array.
Ideally, I would like to clean these references up lazily. Does anyone know of a good strategy to do this?
I've tried to find some built-in way to do that but seems that mongoose doesn't provide such functionality.
So I did something like this
User.findById(userId)
.populate('tags')
.exec((err, user) => {
user.tags = user.tags.filter(tag => tag != null);
res.send(user); // Return result as soon as you can
user.save(); // Save user without dead refs to database
})
This way every time you fetch user you also delete dead refs from the document. Also, you can create isUpdated boolean variable to not call user.save if there was no deleted refs.
const lengthBeforeFilter = user.tags.length;
let isUpdated = user.tags.length;
user.tags = user.tags.filter(tag => tag != null);
isUpdated = lengthBeforeFilter > user.tags.length;
res.send(user);
if (isUpdated) {
user.save();
}
Assuming you delete these tags via mongoose, you can use the post middleware.
This will be executed after you've deleted a tag.
tagSchema.post('remove', function(doc) {
//find all users with referenced tag
//remove doc._id from array
});
its sample retainNullValues: true
Example:
User.findById(req.params.id)
.populate({
path: "tag",
options: {
retainNullValues: true
}
})

inserting in mongo using express

i am trying to put data in the mongodb using express but it is storing blank always ...also it is not printing any console logs :
the url i am hitting after starting the sever is
http://localhost:3000/posts?title=test&link=http://test.com
and it is showing the below output:
{"__v":0,"_id":"562717b064002b1c2e697b33","comments":[],"upvotes":0}
router.get('/posts', function(req, res, next) {
console.log('reached ere '+req);
var post = new Post(req.body);
post.save(function(err, post){
if(err){ return next(err); }
res.json(post);
});
});
Post Scheme:
var mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
title: String,
link: String,
upvotes: {type: Number, default: 0},
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
mongoose.model('Post', PostSchema);
You are calling your API as
http://localhost:3000/posts?title=test&link=http://test.com
which will send title and link to server as query parameters and not body parameters. So your req.body in this case would be an empty object. That is the reason no data in being saved in your posts collection.
You have two options here:
Change your API to save req.query in posts collection which you can do as follows:
Replace
var post = new Post(req.body);
with
var post = new Post(req.query);
Pass link and title as body parameters instead of query parameters.