node.js why dose mongodb throw casting error - node.js

//this error appear
{
"error": {
"message": "Cast to ObjectId failed for value \"events\" at path \"_id\" for model \"user\"",
"name": "CastError",
"stringValue": "\"events\"",
"kind": "ObjectId",
"value": "events",
"path": "_id"
}
}
//when execute this code
exports.get_all_events = (req, res, next) => {
Event.find({})
.populate("creator","name _id",user) // must define model reference
.then(result => {
console.log(result);
res.status(200).json({ result });
}).catch(err => {
console.log(err);
res.status(500).json({ error: err });
});
}
Event schema
const mongoose = require('mongoose');
// creat event schema
const eventSchema = mongoose.Schema({
name: {
type: String,
required: [true, 'name is required']
},
location: {
type: String,
required: [true, 'location is required']
},
date: {
type: String,
required: [true, 'data is required']
},
description: {
type: String,
required: [true, 'description is required']
},
creator: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "users"
}
}
});
module.exports = mongoose.model("events", eventSchema);
Userschema
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
match: /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
post: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "posts"
}
],
event: [
{
type: mongoose.Schema.Types.ObjectId,
// it point to collection
ref: "events"
}
]
});
module.exports = mongoose.model('users', userSchema);
it works great adding event to database and get single event it work but when i get all events from database throw casting error and can't make any updating on exist event

I think you are populating the events document little bit wrong.
Try this:
Event.find({})
.populate("creator._id","name _id")
.then(result => {
console.log(result);
res.status(200).json({ result });
}).catch(err => {
console.log(err);
res.status(500).json({ error: err });
});
I dont think you need any third argument in the .populate() function, You have already defined in your schema, where it should be populated from:
//look here, you have already defined it in your schema
creator: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "users" //this says which collection it should be populated from
}
}
I hope it helps you out.

Related

MongoDB relation between two collections by ID with the Express

I am facing a problem while making a relation between two collections (I am using MEAN stack)
I have two collections: Books and Authors
In frontend I want to make a CRUD menu, where I add a new book in the table and then from there i insert a few data about book and then I choose author from the dropdown menu (fetchin data from Authors collection)
So at the end my Book collection needs to have a few data about the book and then inside the object i need an array of data about those author.
Book schema:
const BookSchema = new mongoose.Schema({
owner: { type: String, required: true },
pagesNo: { type: String, required: true },
releaseDate: { type: String, required: true },
country: { type: String, required: true },
authorID: { type: String, required: true }, <-- HERE I NEED DATA ABOUT AUTHOR
});
Author schema:
const AuthorSchema = new mongoose.Schema({
name: { type: String, required: true },
surname: { type: String, required: true },
dateOfBirth: { type: String, required: true },
countryOfBirth: { type: String, required: true },
});
Book route: book.ts
router.get("/", async (req, res) => {
try {
const books= await Book.find();
let Author = await Author.find({
books: { $elemMatch: { _id: books.bookID } },
});
res.status(200).json(books);
} catch (err) {
res.status(404).json({ success: false, msg: "Booknot found" });
}
});
The problem is somewhere inside the find() function.. Is it even a good practice? I want that it can handle a lot of data.
Thanks to everyone!
Greetings.
Your Book schema would be like this:
const MongooseSchema = new mongoose.Schema({
owner: {
type: String,
required: true,
},
pagesNo: {
type: String,
required: true,
},
releaseDate: {
type: String,
required: true,
},
country: {
type: String,
required: true,
},
authorId: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
});
And your Author Schema would remain the same (in order to link both schemas).
Your route would be like this (if you want to search all books along with their author names):
router.get('/', async (req, res) => {
try {
const books = await Book.find().populate('authorId');
res.status(200).json(books);
} catch (err) {
res.status(404).json({ success: false, msg: 'Booknot found' });
}
});
And in case you want to search for books with a specific author id then your route would be like this:
router.get('/', async (req, res) => {
try {
const books = await Book.find({ authorId }).populate('authorId');
res.status(200).json(books);
} catch (err) {
res.status(404).json({ success: false, msg: 'Booknot found' });
}
});
AuthorID should be type ObjectId, not string.
To join data from other table, you have to use an aggregate with a lookup.
let author = await Author.aggregate([
{
$lookup:
{
from: "books",
localField: "_id",
foreignField: "authorID",
as: "books"
}
}
]);

POPULATION ISSUE: Mongoose/Express

I'm trying to have each record attached to a user who created it,
and every user have their records attached.
Here are my schemas:
1.The Records schema:
const mongoose = require('mongoose')
const RecordsSchema = new mongoose.Schema(
{
Title: { type: String, required: true },
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}],
Author: { type: String, required: true},
ISBN: { type: String, required: true },
Review: { type: String },
SelectedFile: { type: String },
Likes: { type: Number, default:0},
Date: { type: Date, default: Date.now()}
});
module.exports = Records = mongoose.model('record', RecordsSchema', 'record');`
Here is the The user Schema:
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username: { type: String},
email: { type: String, required: true ,unique:true},
records:[{
type: [mongoose.Schema.Types.ObjectId],
ref: 'record' }],
password: { type: String, required: true},
Date: { type: Date, default: Date.now(), immutable: true }
});
module.exports = User = mongoose.model('user', userSchema,'user');
The express route for getting a record:
router.get('/postedby/', (req, res) => {
Records.find(req.params.id)
.populate('postedby')
.exec()
.then(post =>{
if (!post) {
return res.status(400).json({ msg: 'Add Posts' });
}
else return res.json(post);
}).catch (err => console.error(err))
});
Result of the route:
{
"postedby": [],
"Likes": 0,
"_id": "5fed8c12a4fb2c1e98ef09f6",
"Title": "New Age",
"Author": "Situma Prisco",
"ISBN": "23422",
"SelectedFile": "",
"Review": "",
"Date": "2020-12-31T08:30:10.321Z",
"__v": 0
},
I'm getting a blank Array on the populated user field(posteddby) .
Please help, What am I doing wrong? And yes, i do have a User Logged in
You are too close.
In your schema definition, postedby field itself is an array. Hence you can simply define it like :
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}]
Make sure that the ObjectID is stored properly in the postedby array.
Also, you are trying to find a single record based on the ID, hence you can use findById(req.params.id) or findOne({_id:req.params.id}) which would return a single document.

How to properly use references in mongoose

I am trying to use references in mongoose but somehow am not being able to do it properly.
What I am trying to achieve
I would like to query the examModel and get all information about a particular exam including the questions related to that exam.
What I achieved
New questions get saved into the questionModel with the object id of the exam am saving the question for, BUT, the questions array of the examModel doesnt take notice of it.
I have two different models:
examModel
const examSchema = new mongoose.Schema({
examId: {
type: String,
unique: true,
index: true,
required: true,
default: _generateAlphanumericId(18),
},
questions: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Questions",
}],
}, {
timestamps: true,
});
module.exports = mongoose.model("Exams", examSchema);
questionModel
const questionSchema = new mongoose.Schema({
_refExamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exams",
},
questionId: {
type: String,
unique: true,
index: true,
required: true,
default: _generateAlphanumericId(26),
},
title: {
type: String,
trim: true,
required: [true, "Question is missing"],
},
}, {
timestamps: true,
});
module.exports = mongoose.model("Questions", questionSchema);
Now, when I save a new question into the question model, I am sending the _id of an exam from the exam model but still the questions array of exam model doesnt save the object ids of newly created questions.
How I am creating a new question
try {
const question = new questionModel({ _refExamId: req.body._refExamId, title: req.body.title });
await question.save();
return res.status(200).json({ type: "SUCCESS" });
}
catch (error) {
return res.status(500).json({
type: "ERROR",
message: "Some unknown error occurred",
});
}
try {
const question = new questionModel({ _refExamId: req.body._refExamId, title: req.body.title });
const saved_question = await question.save();
const getExam = await examModel.find({_id: saved_question._refExamId});
getExam.questions.push(saved_question._id);
const result = await getExam.save();
return res.status(200).json({ type: "SUCCESS" });
}catch (error) {
return res.status(500).json({
type: "ERROR",
message: "Some unknown error occurred",
});
}
This should get it working.
First of all,
Exam Model
const examSchema = new mongoose.Schema({
examId: {
type: String,
unique: true,
index: true,
required: true,
default: _generateAlphanumericId(18),
},
questions: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Question'
}],
}, {
timestamps: true,
});
module.exports = mongoose.model("Exam", examSchema);
Note: I turned Exams to Exam, Mongodb will change it to plural on your DB
Question Model
const questionSchema = new mongoose.Schema({
_refExamId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exam",
},
questionId: {
type: String,
unique: true,
index: true,
required: true,
default: _generateAlphanumericId(26),
},
title: {
type: String,
trim: true,
required: [true, "Question is missing"],
},
}, {
timestamps: true,
});
module.exports = mongoose.model("Question", questionSchema);
Note: I turned Questions to Question, Mongodb will change it to plural on your DB
Creating a new Question
try {
const question = new questionModel({ _refExamId: req.body._refExamId, title: req.body.title });
await question.save();
return res.status(200).json({ type: "SUCCESS" });
}
catch (error) {
return res.status(500).json({
type: "ERROR",
message: "Some unknown error occurred",
});
}
Try that and lets see
So, I was not reading the refs-to-children part of the docs where it states that, there will be no references in the parent schema unless we explicitly push the reference to the parent.
As a result the following solved my issue:
try {
const exam = await examModel.findOne({ examId: req.body.exam_id });
const question = new questionModel({ title: req.body.title, positiveMarks: req.body.positiveMarks });
exam.questions.push(question);
await exam.save(question.save());
return res.status(200).json({ type: "SUCCESS" });
}
catch (error) {
return res.status(500).json({
type: "ERROR",
message: "Some unknown error occurred",
});
}

How can I populate with mongoose the following code?

I have the following code for the models
// rooms model
const mongoose = require('mongoose');
const ObjectId = mongoose.Schema.Types.ObjectId;
// Model
module.exports = mongoose.model('rooms', {
type_id: {
type: ObjectId,
ref: 'types',
required: true,
},
number: {
type: Number,
required: true,
},
});
// types model
const mongoose = require('mongoose');
//Model
module.exports = mongoose.model('types', {
name: {
type: String,
required: true,
trim: true,
},
price: {
type: Number,
required: true,
},
people: {
type: Number,
required: true,
},
images: [String],
details: {
type: String,
required: true,
trim: true,
},
});
For the get request, I have.
router.get('/', async (req, res) => {
console.log(req.query);
Rooms.find(req.query)
.populate({ path: 'types', select: 'name price people images details' })
.then((data) => {
console.log(data);
res.send(data);
})
.catch((err) => {
res.send(err);
});
});
I am trying to populate to achieve the following results as the room with its type as name price people image details, unfortunately, I only get the following. Any help would be appreciated. Thanks
[
{
"_id": "5e88e3107f10f555cffc36c9",
"type_id": "5e88cb8360363053b67c3f85",
"number": 103,
"__v": 0
}
]

mongoose: create new element in X collection, update another in Y collection

I am trying to develop a CRUD app for users to store, add, delete and update recipes. It's built on MEVN stack. As I need to show the user, which recipes they have created, I am trying to create a recipe based on this model:
const RecipeSchema = new Schema({
title: {
type: String,
required: [true, 'Title of the recipe is required'],
},
category: {
type: Array,
required: [true, 'Category is required'],
},
description: {
type: String,
required: [true, 'Description is required'],
},
imgUrl: {
type: String,
required: [true, 'Image is required'],
},
ingredients: {
type: Array,
required: [true, 'Ingredients are required'],
},
timeOfPreparation: {
type: String,
required: true,
},
preparation: {
type: String,
required: true,
},
sourceName: {
type: String,
required: true,
},
sourceUrl: {
type: String,
required: true,
},
author: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});
const Recipe = mongoose.model('Recipe', RecipeSchema);
module.exports = Recipe;
And at the same time update User model, based on this:
const UserSchema = Schema({
googleId: String,
name: String,
favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
authoredRecipes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
In the controller, I have this method (as per #Stock Overflaw comment):
exports.create_new_recipe = (req, res, next) => {
Recipe.create(req.body)
.then(recipe => {
User.update(
{ _id: req.body.author },
{
$push: { authoredRecipes: recipe.id },
}
);
res.send(res.status);
})
.catch(error => {
res.status(500).json({ error });
});
};
This method is called when I go to /create endpoint. However, even though I do get all the correct ids (req.body.author and recipe.id), I cannot get this to work. In my mLab recipe collection the recipe is displayed correctly (all data that I have inserted with authorId), however in the User collection, the array of authoredRecipes stays empty.
How can I get mongoose to both create an object in one collection as well as update another object based on their ids?
The documentation for findByIdAndUpdate requires the _id field as its value, not an object:
User.findByIdAndUpdate(req.body.author, {
$push: { authoredRecipes: recipe.id }
});
// equivalent to the more general method:
User.findOneAndUpdate({ _id: req.body.author }, {
$push: { authoredRecipes: recipe.id }
});
// and if you don't need the modified document in your callback, this should be faster:
// EDIT: this is advised against (we should use a user object, not the collection)
User.update({ _id: req.body.author }, { // or updateOne
$push: { authoredRecipes: recipe.id }
});
Edit: a working, minimal example
Mind {new: true} maybe? Depending on how you test whether it works...
const mongoose = require('mongoose');
const fs = require('fs');
const userIdFile = './tmp.txt'; // just for this test
mongoose.connect('mongodb://localhost/meuh', {
useNewUrlParser: true, // removes a deprecation warning
useFindAndModify: false // removes another deprecation warning
});
// make schemas/models
const RecipeSchema = mongoose.Schema({
title: { type: mongoose.Schema.Types.String }
});
const UserSchema = mongoose.Schema({
name: { type: mongoose.Schema.Types.String },
data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }]
});
const RecipeModel = mongoose.model('Recipe', RecipeSchema);
const UserModel = mongoose.model('User', UserSchema);
// user precreation
// UserModel.create({
// name: 'me, myself and I'
// }).then((user) => {
// fs.writeFile(userIdFile, user.id, console.log.bind(null, 'error writing file:'));
// mongoose.connection.close();
// });
// return;
// fetch user
const userId = fs.readFileSync(userIdFile);
let pushedRecipeId; // to test everything went smooth
RecipeModel.create({
title: 'pasta solo'
}).then((recipe) => {
console.log('created recipe:', recipe);
pushedRecipeId = recipe.id;
return UserModel.findOneAndUpdate(
{ _id: userId },
{ $push: { data: recipe.id } },
{ new: true } // forces callback to be passed a fresh object
);
}).then((user) => {
console.log('updated user:', user);
console.log('izok:', !!~user.data.indexOf(pushedRecipeId));
mongoose.connection.close();
}).catch((err) => {
console.log('error', err);
mongoose.connection.close();
})
Example output I got:
# creating user (uncommented this part)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
error writing file: null
# calling for $push (re-commented user creation)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72be7032bd2f1acad37c95, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# again $push
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c020c2ac7a1b8c65fa36, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95, 5c72c020c2ac7a1b8c65fa36 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# and again
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c023bf62331b97ef096b, title: 'pasta solo', __v: 0 }
updated user: { data:
[ 5c72be7032bd2f1acad37c95,
5c72c020c2ac7a1b8c65fa36,
5c72c023bf62331b97ef096b ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# end
ubuntu#ubuntu-VirtualBox:~/web/test$
I don't see what's wrong in your code, but at least you have something to compare with... hope this helps!

Resources