how post nested array json objects relationships - node.js

I'm trying to post a service json with nested array json objects relationships todos. When I do that, app shows me next error:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'find' of undefined
This is my model class:
const serviceSchema = new Schema({
name: String,
subject: String,
pricePerHour: Number,
relatedTodos:
[{type: mongoose.Schema.Types.ObjectId,
ref:'todos'}],
createdAt: Date,
updatedAt: Date
});
This is my post route:
app.post('/api/services', async (req, res) => {
const { name, subject, pricePerHour} = req.body;
let todos = await Service.findById(req.params.id).TODO.find({});
if (!todos) {
return res.status(404).json({
message: "todos couldn't be found"
});
}
const service = new Service({
name,
description,
pricePerHour,
relatedTodos
})
try {
let newService = await service.save();
res.status(201).send(newService);
} catch (err) {
if (err.name === 'MongoError') {
res.status(409).send(err.message);
}
res.status(500).send(err);
}
});
How can I'm doing wrong?

Related

cannot implement search functionality. Getting error Cast to ObjectId failed for value \"search\" (type string) at path \"_id\" for model \"Post\"

everyone!
I am relatively newbie to web dev and have been watching mern project tutorial on Youtube. I am trying to implement searching functionality for posts. Posts are stored in MongoDB. After inserting title and/or tags for search form keep getting this error. {"message":"Cast to ObjectId failed for value "search" (type string) at path "_id" for model "Post""}. I am thinking that mongoose mistaking my search req.query for _id req.param but i am not sure. Any suggestions how to solve this error?
.models\Post.js
import mongoose from 'mongoose'
const PostSchema = new mongoose.Schema(
{
title: String,
message: String,
name: String,
creator: String,
tags: [String],
selectedFile: String,
likes: {
type: [String],
default: [],
},
createdAt: {
type: Date,
default: new Date()
}
}
)
const Post = mongoose.model('Post', PostSchema)
export default Post
controllers\posts.js
export const getPostsBySearch = async (req, res) => {
const { searchQuery, tags } = req.query;
try {
const title = new RegExp(searchQuery, "i");
const posts = await Post.find({ $or: [ { title }, { tags: { $in: tags.split(',') } } ]}).exec();
res.json({ data: posts });
} catch (error) {
res.status(404).json({ message: error.message });
}
}
axios endpoint
export const fetchPostsBySearch = (searchQuery) => API.get(`/posts/search?searchQuery=${searchQuery.search || 'none'}&tags=${searchQuery.tags}`);
actions\posts.js
export const getPostsBySearch = (searchQuery) => async (dispatch) => {
try {
dispatch({ type: START_LOADING });
const { data: { data } } = await api.fetchPostsBySearch(searchQuery);
dispatch({ type: FETCH_BY_SEARCH, payload: { data } });
dispatch({ type: END_LOADING });
} catch (error) {
console.log(error);
}
};
just change the route name for example
router.get("/v1/search", getPostsBySearch);
export const fetchPostsBySearch = (searchQuery) => API.get(`/posts/v1/search?searchQuery=${searchQuery.search || 'none'}&tags=${searchQuery.tags}`);
In your router place the /posts/search router above /posts/:id. The query is going to the route /posts/:id and is trying to cast the string value "search" to object id.
In this case the search query is being addressed by getPost instead of getPostBySearch.
postRouter.get('/:id', getPost);
postRouter.get('/search', getPostsBySearch)
Insted change its order to
postRouter.get('/search', getPostsBySearch)
postRouter.get('/:id', getPost);

Im trying to update a users information using MongoDB and JWT

when I use this code to try and update the user it appears as a server error. Im using JWT and mongodb but am unsure if im pulling the token or the id to update the users information. Below my controller code is attached and my schema.
const updateUser = async (req, res) => {
try {
const user = await User.findByIdAndUpdate(req.user.id)
if(!user) return res.status(400).send({ error: 'User not found'})
Object.assign(user, req.body);
user.save()
res.send({ data: user})
} catch (error) {
res.status(500).send({error: 'Server Error'})
}
}
const mongoose = require('mongoose');
let userSchema = new mongoose.Schema({
name: { type: String, required: true},
email: { type: String, required: true, unique: true},
password: { type: String, required: true},
date: { type: Date, default: Date.now}
})
module.exports = mongoose.model('user', userSchema)
Updated update function but i appear to have an error
const updateUser = async (req, res) => {
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id,req.body)
if(!updatedUser) return res.status(400).send('User cannot be updated!')
res.json(updatedUser)
} catch (error) {
res.status(500).send({error: 'Server Error'})
}
}
Try this :
//update user by id
exports.updateUser = async (req, res) => {
try {
const updatedUser = await
<--req.params.id is the user.id and req.body contains the requested fields to update -->
User.findByIdAndUpdate(req.params.id,req.body)
res.json(updatedUser);
}
catch (err) {
console.log(err);
res.status(500).json({ message: 'Internal server error' });
}
}

How to Delete document and sub documents referenced from others collections - MongoDB Mongoose

I have this collection Cart (cart schema) to delete and it is referenced with 2 other schemes, Meal and Customer (owner user, its schema is: User Schema).
How can I delete the cart by passing as req.params.id the user's id from the HTTP request?
Cart Schema
const mongoose = require('mongoose');
const idValidator = require('mongoose-id-validator');
const Schema = mongoose.Schema;
const cartItemSchema = new Schema ({
quantity: { type: Number, required: true },
itemId: { type: mongoose.Types.ObjectId, required: true, ref: 'Meal' }
});
const cartSchema = new Schema ({
cartItems : [
cartItemSchema
],
customer: { type: mongoose.Types.ObjectId, required: true, ref: 'User'}
});
cartSchema.plugin(idValidator);
module.exports = mongoose.model('Cart', cartSchema);
I created a function to delete the document, but it doesn't work, it returns the message: 'Deleted cart.', but isn't true, the document remains in collection.
const deleteCartByUserId = async (req, res, next) => {
const userId = req.params.uid;
let cart;
try {
cart = await Cart.find({ customer: userId });
} catch(err) {
const error = new HttpError('Something went wrong, could not delete cart.', 500);
return next(error);
}
if(!cart) {
const error = new HttpError('Could not find cart for this user id.', 404);
return next(error);
}
try {
Cart.deleteOne({ customer: userId });
} catch(err) {
console.log(err);
const error = new HttpError('Something went wrong, could not delete cart.', 500);
return next(error);
}
res.status(200).json({ message: 'Deleted cart.' });
};
So the porblem was that you missed an await before delete one function call.
Also I've changed some of youre code to make it cleaner:
const functionHandler = fn =>
(req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch(next);
const deleteCartByUserId = functionHandler(async (req, res) => {
const { params: { uid: userId } } = req;
const cart = await Cart.findOneAndDelete({ customer: userId })
if(!cart) {
throw new HttpError('Could not find cart for this user id.', 404);
}
res.status(200).json({ message: 'Deleted cart.' });
});
In your error handler middleware you can check for error type and if it's not HttpError use internal server error.

cast to objectId failed for value xxx

I am using mongoDB populate model and when I try to save the data inside the schema it throws an error:
message: 'Cast to ObjectId failed for value
this is my schema
jobSiteInformation: {
buildingName: String,
street: String,
city: String,
},
data: [{
ref: 'data',
type: Schema.Types.ObjectId,
required: true,
}],
phase schema is like this
const listSchema= new Schema({
name: String,
progress: Number,
list: [],
});
this phase schema is array inside phases which is the quite large and thats the reason I moved to populate model.
anyway this is my route and when I run this it throws the error I pasted above.
router.post('/', async (req, res, next) => {
const info= new List({
jobSiteInformation: req.body.jobSiteInformation,
});
try {
const install = req.body.list;
install.map((inst) => info.lists.push(inst));
const saved= await partial.save();
return res.status(201).json({
result: saved,
});
} catch (e) {
console.log(e);
return next(e);
}
});
I have tried to google but I cannot find the what I am looking for. I have read other posts too but cannot understand what I am doing wrong here.
Assuming mongoose model for phase schema is Phase
// import Phase from ../models/phase
router.post('/request/partial', async (req, res, next) => {
const partial = new PartialRequest({
jobSiteInformation: req.body.jobSiteInformation,
});
try {
const install = req.body.installations;
let savedPhases = await Phase.insertMany(install); // TODO: handle error case
savedPhases.map((inst) => partial.installations.push(inst["_id"]));
const savedPartials = await partial.save();
console.log(savedPartials);
return res.status(201).json({
result: savedPartials,
});
} catch (e) {
console.log(e);
return next(e);
}
});

How do you save to three collections at once?

I'm building my first mean stack app. It's a review site that contains three models: User, Review, and Company.
When I make a review, I want the new review to be saved to the 'review' collection, and for that review to be connected by reference to the company being reviewed and the user who wrote the review. I also want the user to hold a reference to the review, and the company to hold a reference to all the reviews it has. Here are my models:
Review
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const reviewSchema = new Schema ({
companyName: String,
companyId: { type: Schema.Types.ObjectId, ref: 'Company'},
starRating: Number,
subject: String,
commentBody: String,
createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
});
const Review = mongoose.model("Review", reviewSchema);
module.exports = Review;
Company
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const companySchema = new Schema ({
companyName: String,
about: String,
basedIn: String,
materialOrigins: [String],
productRange: [String],
category: String,
reviews: [ {type: Schema.Types.ObjectId, ref: 'Review'} ],
socialRating: Number,
environmentalRating: Number,
priceRange: Number
});
const Company = mongoose.model("Company", companySchema);
module.exports = Company;
User
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema ({
email: String,
firstName: String,
lastName: String,
password: String,
image: Object,
aboutText: String,
reviews: [ { type: Schema.Types.ObjectId, ref: "Review" } ]
// comments: { type: Schema.Types.ObjectId, ref: 'Comment' }
});
const User = mongoose.model("User", userSchema);
module.exports = User;
This is my current route, which currently saves the review to the collection and attaches the user. However, the user doesn't get the review.
route
router.post('/:category/:company', (req, res) => {
var subject = req.body.subject;
var commentBody = req.body.commentBody;
var starRating = req.body.starRating;
var userId = req.body.userId;
if(!subject || !commentBody || !starRating) {
res.status(400).json({ message: "Subject, comment body, and star rating are required." });
return;
}
var newReview = Review({
starRating,
subject,
commentBody,
userId
});
User.findById(userId, {
}, (err, user) => {
if (err) {
return res.send(err);
} else {
console.log("checking out user in route", user);
user.reviews.push(newReview);
user.save();
newReview.save((err, review) => {
if (err) {
return res.status(400).json({ message: err });
} else {
res.status(200).json({ message: 'Review saved', review });
}
});
}
});
I haven't tried adding the company in because I'm trying to do one thing at a time. I've been looking at 'populate', but all of the documentation seems to only use two models at once. Is it possible to do three at once? Or am I overcomplicating this?
Apologies if this is all overcomplicated. I'm fairly new to MongoDB and MEAN stack in general. Thanks for your help.
Ok, I did it, for any people landing on this page wondering the same thing in the future.
Here's my route:
router.post('/:category/:company', (req, res, next) => {
var companyName;
var companyId;
var subject = req.body.subject;
var commentBody = req.body.commentBody;
var starRating = req.body.starRating;
var createdBy = req.body.createdBy;
if(!subject || !commentBody || !starRating) {
res.status(400).json({ message: "Subject, comment body, and star rating are required." });
return;
}
var newReview = Review({
starRating,
subject,
commentBody,
createdBy
});
//I need the companyId and companyInfo for later use in my review save. I'm calling the company with the name I have from my params, and setting the id and name with the received data from Mongo.
Company.findOne({"companyName": req.params.company}, (err, company) => {
if (err) {
return res.status(400).json({ message: err });
} else {
this.companyName = company.companyName;
this.companyId = company.id;
}
});
newReview.save((err, review) => {
//Push the review id to the user
if (err) {
return res.status(400).json({ message: err });
} else { User.findByIdAndUpdate({_id: createdBy },{$push: {reviews: review.id} }, (err) => {
if (err) {
console.log("There was an error pushing review to user");
next(err);
//Push the review id to the company
} else { Company.findOneAndUpdate({ "companyName": req.params.company}, {$push: {reviews: review.id}}, (err, company) => {
if (err) {
console.log("There was an error pushing review to company");
next(err);
} else {
//Updates the review by setting companyId and companyName properties to review for Mongo
Review.update({_id: review.id}, {$set: {companyId: this.companyId, companyName: this.companyName}}, (err, changes) => {
if(err) {
return res.status(400).json({message : err});
} else {
console.log("updating review successfully with company info", changes);
}
});
console.log ("Review successfully saved");
res.json({
review: review,
});
}
});
}
});
}
});
});
If anyone has feedback on how this could be done better/more efficiently, let me know. Cheers.

Resources