Mongoose Schema type Object Id Mapping - node.js

Backend Code:
This is orderModel.js
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
orderItems: [
{
name: { type: String, required: true },
qty: { type: String, required: true },
image: { type: String, required: true },
price: { type: String, required: true },
productId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Product",
},
},
],
deliveryAddress: {
address: { type: String, required: true },
area: { type: String, required: true },
// gender: { type: String, required: true },
postalCode: { type: String, required: true },
district: { type: String, required: true },
division: { type: String, required: true },
country: { type: String, required: true, default: "Bangladesh" },
},
paymentMethod: {
type: String,
required: true,
},
paymentStatus: {
id: { type: String },
status: { type: String },
update_time: { type: String },
email: { type: String },
},
taxPrice: {
type: Number,
required: true,
default: 0.0,
},
deliveryCost: {
type: Number,
required: true,
default: 0.0,
},
totalPrice: {
type: Number,
required: true,
default: 0.0,
},
discount: {
type: Number,
required: true,
default: 0.0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
paidAt: {
type: Date,
},
isDelivered: {
type: Boolean,
required: true,
default: false,
},
deliveredAt: {
type: Date,
},
},
{
timeStamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
This is orderController.js -
import asyncHandler from "express-async-handler";
import Order from "../models/orderModel.js";
// review: desc => Create New Order
// review: route => POST api/orders
// review: access => Private
const createOrder = asyncHandler(async (req, res) => {
const {
orderItems,
deliveryAddress,
paymentMethod,
taxPrice,
deliveryCost,
discount,
totalPrice,
} = req.body;
if (orderItems && orderItems.length === 0) {
res.status(400);
throw new Error("No order items");
} else {
try {
const order = new Order({
orderItems,
user: req.user._id,
deliveryAddress,
paymentMethod,
taxPrice,
deliveryCost,
discount,
totalPrice,
});
console.log("ORDER CREATED", order);
const createdOrder = await order.save();
console.log("ORDER saved", createdOrder);
res.status(201).json(createdOrder);
} catch (error) {
res.status(400);
throw new Error("Invalid order data", error);
}
}
});
// review: desc => GET order by id
// review: route => GET api/orders/:id
// review: access => private
const getOrderById = asyncHandler(async (req, res, next) => {
const order = await Order.findById(req.params.id).populate(
"user",
"name email"
);
if (order) {
res.json(order);
} else {
res.status(404);
throw new Error("Order not found");
}
});
export { createOrder, getOrderById };
Frontend Code:
const handleSubmit = (e) => {
e.preventDefault();
dispatch(
createOrder({
orderItems: cartItems,
deliveryAddress: addressObject,
paymentMethod,
taxPrice: vat,
deliveryCost,
discount,
totalPrice,
})
);
};
Here When I am sending request to backend, It's giving me validation error. As - orderItems.0.productId: Path productId is required. Why I am getting this error ? mongoose should not map data and get productId from sent cartItems, where is a field is _id ? Your time and help is most appreciated, Thanks.

Related

MongoDB geting an empty array from an aggregation

So I created this controller to get me the sum of all the orders totalPrice made by months
const getMonthlyOrderTotal = async (req, res) => {
try {
const year = req.params.year;
const aggregatePipeline = [
{
$match: {
createdAt: {
$gte: new Date(`${year}-01-01T00:00:00.000`),
$lt: new Date(`${year}-12-31T23:59:59.999`)
}
}
},
{
$group: {
_id: {
$month: "$createdAt"
},
total: {
$sum: "$totalPrice"
}
}
},
{
$sort: {
_id: 1
}
}
];
const orderTotals = await Order.aggregate(aggregatePipeline);
res.json(orderTotals);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
this is the orderModel I am using
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
client: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Client",
},
orderItems: [
{
name: { type: String, required: true },
qty: { type: Number, required: true },
image: { type: String },
price: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Product",
},
},
],
totalPrice: {
type: Number,
required: true,
default: 0.0,
},
taxPrice: {
type: Number,
required: true,
default: 0.0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
paidAt: {
type: Date,
},
isDelivered: {
type: Boolean,
required: true,
default: false,
},
deliveredAt: {
type: Date,
},
},
{
timestamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
and when I try to test this API in postman "http://localhost:5001/orders/orderstotal/2022" I always get an empty array even though there is stored data in mongoDB orders Collection

How to return documents in mongoose and express js which are associated with only logged in user?

Here i have two mongoose models orders and users which have one to many relationship.
user.model.js
import mongoose from "mongoose";
const userSchema = mongoose.Schema(
{
firstname: {
type: String,
required: [true, "Name is required"],
},
lastname: {
type: String,
required: [true, "LastName is required"],
},
email: {
type: String,
required: [true, "Email is required"],
unique: true,
},
password: {
type: String,
required: [true, "Password is required"],
},
isAdmin: { type: Boolean, default: false },
},
{
timestamps: true,
}
);
const User = mongoose.model("User", userSchema);
export default User;
order.model.js
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
customerId: { type: String },
paymentIntentId: { type: String },
products: [
{
id: { type: String },
name: { type: String },
category: { type: String },
price: { type: String },
size: { type: String },
color: { type: String },
thumbnail: { type: String },
qty: { type: Number },
},
],
// subTotal: { type: Number, required: true },
total: { type: Number, required: true },
shipping: { type: Object, required: true },
deliveryStatus: { type: String, default: "pending" },
paymentStatus: {
type: String,
required: true,
},
},
{
timestamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
I have some orders created by different users in my database. Now i am trying to get those specific orders associated with currently logged in user.
order.controller.js
export const getAllOrders = async (req, res) => {
const { _id } = req.user;
// console.log(typeof id);
try {
const orders = await Order.find({userId: _id});
console.log(orders);
res.status(200).json({ orders });
} catch (error) {
res.status(500).json({ msg: error.message });
}
};
I have tried this one but it always return an empty array.

Problem with update a single doc in monogdb using express and mongoose

I'm quiet new to mongodb and I'm actually trying to implement a follow-unfollow method in the backend
there are two types of users in the database
Mentors and mentees
only mentees can follow the mentors and mentors can only accept the request
the schema
Mentors
const MentorsSchema = mongoose.Schema({
name: { type: String, required: true },
designation: { type: String, required: true },
yearNdClass: {
type: String,
required: ["true", "year and class must be spciefied"],
},
respondIn: { type: String, required: true },
tags: {
type: [String],
validate: (v) => v == null || v.length > 0,
},
socialLinks: {
github: { type: String, default: "" },
twitter: { type: String, default: "" },
facebook: { type: String, default: "" },
instagram: { type: String, default: "" },
},
watNum: { type: Number, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
about: { type: String },
followers: [
{ type: mongoose.Schema.Types.ObjectId, ref: "Mentees", default: "" },
],
pending: [
{ type: mongoose.Schema.Types.ObjectId, ref: "Mentees", default: "" },
],
});
Mentee
const MenteeSchema = mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
yearNdClass: {
type: String,
required: ["true", "year and class must be spciefied"],
},
socialLinks: {
github: { type: String },
twitter: { type: String },
facebook: { type: String },
instagram: { type: String },
},
about: { type: String },
skillLooksFor: { type: String, required: true },
watNum: { type: Number, required: true },
following: [{ type: mongoose.Schema.Types.ObjectId, ref: "Mentors",default:"" },
],
});
you can see that there are two fields for mentors both following and pending arrays which consist of the ids of the mentees who follow the mentors and the ids of the mentees which yet to be accepted as a follower
I planned to create an endpoint where when a mentee gives a follow request it should be reached into the mentor pending array so that he can accept it later
so my logic like this
// #desc follow a mentor
// #route POST /api/mentees/follow-mentor/:id
// #access private
menteeRoute.post(
"/follow-mentor/:id",
isAuthorisedMentee,
expressAsyncHandler(async (req, res) => {
const { id } = req.params;
const mentee = await Mentees.findById(req.mentee.id);
const mentor = await Mentors.findById(id).select("-password");
// console.log(mentor)
if (mentee) {
try {
await Mentees.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(id) },
{ $addToSet: { "following.0": mentor._id } },
{ new: true }
);
await Mentors.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(mentor._id) },
{
$addToSet: {
"pending.0": id,
},
},
{ new: true },
);
res.json({
data: {
mentor,
mentee,
},
});
} catch (error) {
console.log(error);
throw new Error(error);
}
}
})
);
but the code didn't work.
can anyone help me to resolve the problem?
basically, when a mentee gives a follow request it should update the following array of mentee with the id of mentor and it should also update the pending array of mentor with the id of the mentee
PS: any alternative ideas are also welcome
Try to remove the .0 index and use the $push method.
Also, you should return the updated objects:
menteeRoute.post(
'/follow-mentor/:id',
isAuthorisedMentee,
expressAsyncHandler(async (req, res) => {
const { id } = req.params;
const mentee = await Mentees.findById(req.mentee.id);
const mentor = await Mentors.findById(id).select('-password');
// console.log(mentor)
if (mentee) {
try {
const updatedMentee = await Mentees.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(id) },
{ $push: { following: mentor._id } },
{ new: true }
);
const updatedMentor = await Mentors.findOneAndUpdate(
{ _id: mentor._id },
{
$push: {
pending: id,
},
},
{ new: true }
);
res.json({
data: {
mentor: updatedMentor,
mentee: updatedMentee,
},
});
} catch (error) {
console.log(error);
throw new Error(error);
}
}
})
);

NodeJS and mongoose CastError: Cast to [ObjectId] failed for value

I am using molecularjs actions to enter data into my database. Primarily I am using NodeJS with mongoose to define models. So far I am able to create a model but when I try to create an one-to-many relation it gives me this error
errors: { 'deliveryParcels.0': CastError: Cast to [ObjectId] failed
for value "[{"parcelId":"LHE660851871415","address":"24 E muhafiz town
lahore","type":"COD","addressLocation":{"lat":31.4532941,"lng":74.2166403},"customerName":"ALI
MALHI","customerPhone":"3314488396","processingStatus":"pending","amount":2500,"vendorOrderId":"other"}]"
at path "deliveryParcels.0"
I want both collections rider-tasks and delivery-parcels to be created simultaneously but only the rider-tasks collection gets created but not the delivery-parcels. What am I doing wrong in my schema?
Here are my models
rider-tasks model
import mongoose, { Schema } from "mongoose";
import { getDbConnection } from "../../../utils/db/db-connect";
/* eslint-disable id-blacklist */
import { IRiderTaskModel } from "../interfaces/rider-task.interface";
const RiderTaskSchema: Schema<IRiderTaskModel> = new Schema<IRiderTaskModel>(
{
_id: { type: String, required: true, unique: true },
riderId: { type: String, required: true },
totalAmount: { type: Number, required: true },
riderName: { type: String, required: true },
cityId: { type: String, required: true },
cityName: { type: String, required: true },
status: { type: String, required: true },
adminName: { type: String, required: true },
adminId: { type: String, required: true },
createdAt: { type: Date, required: true },
deliveryParcels: [
{
type: Schema.Types.ObjectId,
ref: "delivery-parcels",
},
],
},
{ _id: true }
);
const RiderTask = getDbConnection("deliveryManagement").model<IRiderTaskModel>(
"rider-tasks",
RiderTaskSchema
);
export { RiderTask };
delivery-parcels
import mongoose, { Schema } from "mongoose";
import { getDbConnection } from "../../../utils/db/db-connect";
import { IDeliveryParcelModel } from "../interfaces/delivery-parcels.interface";
const DeliveryParcelsSchema: Schema<IDeliveryParcelModel> = new Schema<IDeliveryParcelModel>(
{
parcelId: { type: String, required: true },
address: { type: String, required: true },
addressLocation: {
lat: { type: Number, required: true },
lng: { type: Number, required: true },
},
customerName: { type: String, required: true },
customerPhone: { type: String, required: true },
processingStatus: { type: String, required: true },
amount: { type: Number, required: true },
riderTaskId: { type: Schema.Types.String, ref: "rider-tasks" },
type: { type: String, required: true, enum: ["COD", "NONCOD"] },
postedStatus: {
status: { type: String },
statusKey: { type: String },
signature: { type: String },
reason: { type: String },
checkBoxData: [{ type: String }],
adminId: { type: String },
adminName: { type: String },
},
}
);
const DeliveryParcels = getDbConnection(
"deliveryManagement"
).model<IDeliveryParcelModel>("delivery-parcels", DeliveryParcelsSchema);
export { DeliveryParcels };
My service
private async createdRiderTaskHandler(ctx: Context<IRiderTaskModel>) {
const { params } = ctx;
const finalData: IParcelData[][] = await Promise.all(
params.deliveryParcels.map((parcel) =>
this.broker.call("parcel-data.getParcels", {
id: parcel.parcelId,
})
)
);
const wrongParcels: string | any[] = [];
const check = finalData[0].filter(
(data) => data.currentStatus.status === "Dispatched"
)[0];
if (check) {
wrongParcels.push(check.parcelId);
const parcel = wrongParcels.join("\r\n");
throw new Error(
'These parcels "' + parcel + '" have already been dispatched'
);
}
const codAmount = params.deliveryParcels
.filter((data, i) => data.type === "COD")
.reduce((total, b) => total + b.amount, 0);
const customId = "RTD-" + Math.floor(100000 + Math.random() * 900000);
try {
const taskData = omit({
...params,
_id: customId,
totalAmount: codAmount,
forceAssignment: false,
createdAt: new Date(),
});
const data = await RiderTask.create(taskData);
return { message: data };
} catch (error) {
this.logger.error(error);
return error;
}
}

How to delete the referenced document in one collection and its record from the referred other collection

In my NodeJS API and MongoDB, I'm trying to delete a record which is a reference to another collection.
What I would like to do is to delete the referred objectId and the records related to the other collection which is referred.
I have 2 models Profiles and Posts and I want to delete the same one post from Profile and Post collection.
I was able to delete the reference id in Profile but I don't know how to delete also the record from Posts collection.
I tried this:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
err => {
if (err) {
throw new ErrorHandlers.ErrorHandler(500, err);
}
res.json({ Message: "Deleted" });
}
);
} catch (error) {
res.status(500).send(error);
}
}
And my 2 models:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
profile: {
type: Connect.Schema.Types.ObjectId,
ref: "Profile",
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Just add a query to remove the post after pulling it's ID from the profile collection:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
// You don't need an error callback here since you are
// using async/await. Handle the error in the catch block.
);
await Posts.remove({ _id: req.params.postId });
} catch (error) {
// This is where you handle the error
res.status(500).send(error);
}
}

Resources