Mongoose/Express CastError for 1 of 2 exact same routes? - node.js

I am attempting to Model.find({}) all documents within a collection on two exactly similar express routes. On one of my routes, this happens no problem and all documents are returned. Yet, on a different express route with the exact same logic, I receive the following error:
Cast to ObjectId failed for value "featured" at path "_id" for model "Location"
Utilizing the mongo shell db.locations.find() I receive the documents no problem.
Here is my Schema for locations:
var mongoose = require('mongoose');
var { pointSchema } = require('../Geoschema-Types/GeoSchemas');
const LocationSchema = new mongoose.Schema({
name: {type: String, required: true},
PATH: [{type: mongoose.Schema.Types.ObjectId, ref: 'SOME_COLLECTION'}],
PATH: [{type: mongoose.Schema.Types.ObjectId, ref: 'SOME_COLLECTION'}],
PATH: String,
location: {
type: pointSchema,
required: true
},
settings: {
private: Boolean
},
meta: {
created: { type : Date, default: Date.now },
view_count: {type: Number, default: 0},
tags: [String],
likes: {type: Number, default: 0},
numberOfComments: {type: Number, default: 0},
numberOfShares: {type: Number, default: 0}
},
comments: [{type: mongoose.Schema.Types.ObjectId, ref: 'Comment'}],
typeoflocation: String
});
LocationSchema.query.nearPoint = function(coordinates, maxDistance) {
return this.where('location')
.near({ center: { coordinates, type: 'Point' }, maxDistance, spherical: true })
};
LocationSchema.options.autoIndex = true;
LocationSchema.index({ location: "2dsphere" });
var Location = mongoose.model("Location", LocationSchema);
module.exports = Location;
In my root file app.js I have both of the routes placed in middleware each with different parent paths for their respective routers.
app.use('/test', require('./tests/routes/Test-Routes'))
app.use('/locations', require('./routes/Models/Locations/Location-Routes'));
~ /Location-Routes ------------ FULL FILE
const express = require('express');
const router = express.Router();
const { isLoggedIn } = require('../../../util/middleware/auth-util')
const {
addCommentUtil,
LikePostUtil,
deletePostUtil
} = require('../../../util/middleware/post-actions-util');
const {
newLocation,
findNear,
viewLocation
} = require('../../../controllers/Models/Locations/Location-Controllers')
// MODEL
const Location = require('../../../models/Location/LocationSchema')
router.route('/findnear')
.post(findNear)
router.route('/:id')
.get(isLoggedIn, viewLocation)
router.route('/featured')
.get((req, res, next) => {
Location.find({})
.then(docs => {
res.send(docs)
})
.catch(next)
})
router.route('/newlocation')
.post(isLoggedIn, newLocation)
router.route('/:id/addcomment')
.post(isLoggedIn, addCommentUtil(Location));
router.route('/:id/like')
.post(isLoggedIn,LikePostUtil(Location))
// DELETE ---------------------------------------------------------
router.route('/:id/delete')
.delete(isLoggedIn, deletePostUtil(Location))
module.exports = router;
~ /Test-Routes
router.route('/featured')
.get((req, res, next) => {
Location.find({})
.then(docs => {
res.send(docs)
})
.catch(next)
})
All other routes on the ~/Locations router work just fine, including adding documents and deleting them... yet this one query returns the above error:
Cast to ObjectId failed for value "featured" at path "_id" for model "Location"
Using the ~/Test-Routes route works just fine.

Because you put router.route('/:id') before router.route('/featured') so when you call to /featured, it will recognize featured as :id and go to your viewLocation function in your Location-Controllers.
Changing your route to put router.route('/featured') before router.route('/:id') may solve the problem.

Related

How to delete referenced collection id in my node.js mongodb based application?

I have a Comment Model, User Model and Post model. In Post Model, there is a field called 'comment' and I referenced Comment model there. That way, every comment made on that post will be populated.
Now, if a user deletes any comment, that comments get deleted but the id referenced in the Post Model still remains. Though it is not active but it remains there. In a situation where you have many comments that get deleted, that Post collection with the Comment referenced field will look messy. Is there a way around this? I want once a comment is deleted, it should also delete anywhere it is referenced. Here are my codes:
Post Model
//creating the user models for the database
const mongoose = require("mongoose"); //import mongoose
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema(
{
title:{
type: String,
required: true,
unique: true,
},
description:{
type: String,
required: true,
},
postPhoto:{
type: String,
required:false,
},
username:{
type: Schema.Types.ObjectId,
ref: 'User'
},
categories:{
type: Array,
},
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
unique: true,
}]
}, {timestamps: true},
);
//exporting this schema
module.exports = mongoose.model("Post", PostSchema); //the module name is "Post"
Comment Model
const mongoose = require("mongoose"); //import mongoose to be used
const Schema = mongoose.Schema;
const CommentSchema = new mongoose.Schema(
{
commentdescription:{
type: String,
required: true,
},
author:{
type: Schema.Types.ObjectId,
ref: 'User',
},
}, {timestamps: true}
);
//exporting this schema
module.exports = mongoose.model("Comment", CommentSchema); //the module name is "Post"
Codes that delete a comment
//comment delete
router.delete("/posts/:id/comment/:id", async (req, res) =>{
try{
const comment = await Comment.findById(req.params.id)
if(comment.author == req.body.author){
try{
await comment.delete()
res.status(200).json("Comment has been deleted")
}catch(err){
console.log(err)
}
}
else{
res.status(401).json("you can only delete your comment")
}
}catch(err){
console.log(err)
}
})
codes that populates comment in Post
//Get Post
router.get("/:id", async(req, res)=>{
try{
const post = await Post.findById(req.params.id).populate('username').populate({
path: "comments",
populate: {
path: "author",
}
})
See the attached image. You can see that comment field in Post collection is still with a comment ref that has been deleted. The comment is deleted from the Comment Collection. But I will also like to delete all places it is referenced.
my main language is not English, so I apologize for that
Some things need to be corrected
Post Model
const mongoose = require("mongoose"); //import mongoose
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
unique: true,
},
description: {
type: String,
required: true,
},
postPhoto: {
type: String,
required: false,
},
// Commented for testing
// username: {
// type: Schema.Types.ObjectId,
// ref: "User",
// },
categories: {
type: Array,
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment",
unique: true,
},
],
},
{ timestamps: true }
);
//exporting this schema
module.exports = mongoose.model("Post", PostSchema);
Comment Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CommentSchema = new mongoose.Schema(
{
commentdescription: {
type: String,
required: true,
},
// Commented for testing
// author: {
// type: Schema.Types.ObjectId,
// ref: "User",
// },
postId: {
type: Schema.Types.ObjectId,
ref: "Post",
},
},
{ timestamps: true }
);
module.exports = mongoose.model("Comment", CommentSchema);
index.js
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true }));
const port = 3000;
var mongoDB = "mongodb://127.0.0.1/my_database";
mongoose.connect(mongoDB);
const Post = require("./Model/Post");
const Comment = require("./Model/Comment");
// add post
app.post("/posts/add", async (req, res) => {
console.log(req.body);
const post = await Post.create(req.body);
res.status(200).json({
success: true,
data: post,
});
});
// add comment
app.post("/comment/add", async (req, res) => {
const comment = await Comment.create(req.body);
const post = await Post.findByIdAndUpdate(
{ _id: comment.postId },
{
$addToSet: { comments: comment._id },
}
);
res.status(200).json({
success: true,
data: comment,
});
});
// get all post
app.get("/posts", async (req, res) => {
const post = await Post.find({});
res.status(200).json({
post,
});
});
// delete comment
app.delete("/comment/:id", async (req, res) => {
const com = await Comment.findById(req.params.id);
console.log("postid", com.postId);
await Post.findByIdAndUpdate(
{ _id: com.postId },
{
$pull: { comments: com._id },
},
{ new: true }
);
await com.delete();
res.status(200).json({
success: true,
});
});
app.listen(port, () => {
console.log("server connect");
});
Models are not smart to understand your intention that because you
deleted the comment, they should be deleted from everywhere.
Computers are stupid, you have to explain to them face to face.

How to pass an ObjectId into a POST request?

I am trying to make a One-To-Many relationship between two tables(Group and Movement tables) using node js (Express) and mongo DB. I already have a group Id coming from the Group table on my side, my question is, how can I save a movement( see point 3 ) with that group Id I have. I tried passing groupId: req.body.group._id and
groupId: req.body.group but I am never able to populate that variable
This are the two entities I've created:
1) GROUP ENTITY
const mongoose = require("mongoose")
const GroupSchema = mongoose.Schema({
name: {
type: String,
required: true
},
limit: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
movement: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Movement' }],
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
})
module.exports = mongoose.model("Group", GroupSchema)
2) MOVEMENT ENTITY
const mongoose = require("mongoose")
const MovementSchema = mongoose.Schema({
description: {
type: String,
required: true
},
value: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
group: { type: mongoose.Schema.Types.ObjectId, ref: 'Group' }
})
module.exports = mongoose.model("Movement", MovementSchema)
This is my movement router where I make the endpoints (Actual problem is here)
3) MovementRoute
const router = require('express').Router();
const verify = require('./verifyToken');
const User = require('../model/User');
const Group = require('../model/Group');
const Movement = require('../model/Movement');
// Create Movement
router.post('/', verify, async (req, res) => {
const post = new Movement({
description: req.body.description,
value: req.body.value,
groupId: req.body.group._id //**tried this**
});
try {
const savedMovement = await post.save()
res.status(200).send(res.json({ data: savedMovement }));
} catch (error) {
res.status(400).send(res.json({ message: error }));
}
});
module.exports = router;
Request sent
{
"description":"group1",
"value":"233",
"group":"5e506f3c56233d08f79bc8f3"
}
If console.log(req.body) gives you this:
{
description: 'group1',
value: '233',
group: '5e506f3c56233d08f79bc8f3'
}
..you should be able to do this:
router.post('/', verify, async (req, res) => {
console.log(req.body) // --> { description: 'group1', value: '233', group: '5e506f3c56233d08f79bc8f3' }
const post = new Movement({
description: req.body.description,
value: req.body.value,
groupId: req.body.group
// groupId: req.body.group._id //**tried this**
});
try {
const savedMovement = await post.save()
res.status(200).send(res.json({ data: savedMovement }));
} catch (error) {
res.status(400).send(res.json({ message: error }));
}
});

Mongoose Populate() doesn't return anything, it just stays stuck

I have 2 Schemas, one is for Questions and the other is for Themes
It's a one to many relationship, where theme_id references the id for the Theme
It works great but thene I try to use populate to get the theme info from the id it just doesn't do anything, literally
I am making an API so when I hit /questions/:id with the respective id of the question, nothing happens Postman just freezes and the server does nothing
This is the Question Schema:
const questionSchema = mongoose.Schema({
question: {
type: String,
required: true,
index: { unique: true }
},
answers: [{
name: String,
is_true: Boolean
}],
theme_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'themes'
}
});
const Question = module.exports = mongoose.model('Question', questionSchema);
This is the Themes Schema:
const themeSchema = mongoose.Schema({
name: {
type: String,
required: true,
index: { unique: true }
},
relation: {
type: String,
required: true
}
});
const Theme = module.exports = mongoose.model('Theme', themeSchema);
This is how my get question method:
exports.getQuestion = (req, res, next) => {
Question.findById(req.params.id)
.populate('theme_id')
.exec((err, question) => {
if(err) return err;
console.log(question);
res.json(question);
})
}
When I do
populate('theme_id')
Nothing happens as described above
When I do
populate('theme') //or any other string, it doesn't matter
I get the theme_id field from MongoDB but it's not populated, it's just the ID of the theme
Been stuck here for a while now, what am I doing wrong?
mongoose give reference using ref:"ModelName"
const questionSchema = mongoose.Schema({
...
theme_id: { //<-- use this key in populate
type: mongoose.Schema.Types.ObjectId,
ref: 'Theme' // <-- model name here
}
});
const Question = module.exports = mongoose.model('Question', questionSchema);
and populate using field name in schema using : populate('field_name')
in your case : populate('theme_id')
I think the error is in your ObjectId reference .
Change theme_id reference to
theme_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Theme'
}
Then
populate('theme_id')
You can read more at Mongoose v5.6.13: Query Population

GET request is not working to retrieve data from the db

I am quite new to node.js and I am writing a GET request to retrieve documents from my database. Here is the definition of my request (in users.js) -
router.get('/', function (req, res, next) {
Booking.find({} ,(err,prevBookings)=>{
if(err) {
res.status(400).send('No previous bookings found.');
}
if(prevBookings[0]) {
res.status(200).send(prevBookings);
}
else{
console.log("no records")
res.status(200).send("No previous bookings found.")
}
});
});
And I have defined these in the index.js file as follows:
router.use('/users', require('./users'))
router.use('/prev', require('./users'))
//and there are other routers here too but totally unrelated to users.js
But, I am getting the following error on Postman:
NotFoundError: Not Found
I know that this error indicates that I am not routing it correctly. But I am not able to identify where I am going wrong. Other routes that I have defined, are working. Could anyone please help me out with this? Thank you :)
I think you're specifying '/prev' twice. If you change your route in users.js to "/", see what happens. I think at the moment the route "/prev/prev" will return a result.
I'm guessing your users.js should look like so:
const express = require("express");
const router = express.Router();
router.get('/', function (req, res, next) {
Booking.find({} ,(err,prevBookings)=>{
if(err) {
res.status(400).send('No previous bookingd found.');
}
if(prevBookings[0]) {
res.status(200).send(prevBookings);
}
else{
console.log("no records")
res.status(200).send("No previous bookings found.")
}
});
});
module.exports = router;
And then at the top level (index.js) or whatever:
app.use("/", router);
router.use('/prev', require('./users'))
This should return the correct result on "/prev"
For the booking model, could you try modifying the mongoose schema like so (adding the { collection: } entry):
var bookingSchema = mongoose.Schema({
_id : {
type: mongoose.Schema.Types.ObjectId,
required: true
},
user_id : {
type: mongoose.Schema.Types.Mixed,
ref: 'User',
required: true
},
places_id : {
type: mongoose.Schema.Types.ObjectId,
ref: 'Place',
required: true
},
fromDate : {
type: Date,
required: true,
default: Date.now()
},
toDate : {
type: Date,
required: true,
default: Date.now()
},
people_count : {
type: String,
required: true
},
package_details : {
type: mongoose.Schema.Types.Mixed
},
total_cost : {
type : Number,
required : true
},
cost : {
type: Number,
required: true
},
// confirmation: {
// type : String,
// required: true
// },
transaction_no : {
type : String,
required : true
}
}, {
collection: 'booking'
}
)

Problems with Mongoose populate

Game schema
const { Schema, model } = require('mongoose');
const gameSchema = Schema({
_id: Schema.Types.ObjectId,
name: { type: String, required: true },
description: String,
calc: [{ type: Schema.Types.ObjectId, ref: 'Calc' }]
});
module.exports = model('Game', gameSchema);
Calc schema
const { Schema, model } = require('mongoose');
const calcSchema = Schema({
_id: Schema.Types.ObjectId,
preset: { type: String, required: true },
datasets: [{ type: Schema.Types.ObjectId, ref: 'Dataset' }],
model: String,
});
module.exports = model('Calc', calcSchema, 'calc');
GET Games route
router.get('/', passport.authenticate('jwt', { session: false }), (req, res) => {
Game.find()
.select('_id name calc')
.populate('calc')
.then(games => res.status(200).json(games))
.catch(err => res.status(500).json({ error: err }));
});
Instead of populating calc property with Calc objects, replacing the ids, calc property turns into an empty array. How do I use populate correctly? Is there an obvious mistake I made in my code?
In short: populate() results in calc: [] instead of calc: [{Calc object}, ...]
In your case you are trying to populate an array of document (and not only one document) so you should use the Model.populate() method instead.
Game.find()
.select('_id name calc')
.then(games => Game.populate(games, { path: 'calc' }))
.then(games => res.status(200).json(games))
.catch(err => res.status(500).json({ error: err }));

Resources