How to correctly save references to mongodb from request - node.js

I want to save data and references of child document into parent document from request, I have managed to achieve that, but I am not sure is this correct way of doing it.
I have found in stackoverflow that first we have to save child document. so we can save references of child document into parent document
This is my structure of request
"category": "movie",
"overview": "This is overview",
"poster_path": "https://image.tmdb.org/t/p/w500/aWeKITRFbbwY8txG5uCj4rMCfSP.jpg",
"release_date": "2021-12-01",
"title": "Sing 2",
"vote_average": 8.2,
"cast": [
{
"name": "Matthew McConaughey",
"profile_path": "wJiGedOCZhwMx9DezY8uwbNxmAY.jpg"
},
{
"name": "Reese Witherspoon",
"profile_path": "6Pp3BrY2JbJg77Po8NOBO6zOA8m.jpg"
}
]}
//Show Schema
const showSchema = new Schema({
title: {
type: String,
unique: true,
required: [true, 'Title can not be empty.'],
trim: true,
text: true
},
slug: String,
poster_path: {
type: String,
required: [true, 'Cover can not be empty.'],
trim: true
},
overview: {
type: String,
required: [true, 'Description can not be empty.'],
trim: true,
text: true
},
release_date: {
type: Date,
default: Date.now(),
required: [true, 'Release date can not be empty.']
},
category: {
type: String,
trim: true,
required: [true, 'Please provide category']
},
vote_average: {
type: Number,
min: [1, 'Rating must be 1 or above 1'],
max: [10, 'Rating must be 10 or below 10']
},
vote_count: {
type: Number,
default: 0
},
cast: [
{
type: mongoose.Schema.ObjectId,
ref: 'Cast'
}
]
})
//Create slug
showSchema.pre('save', function(next) {
this.slug = slugify(this.title, {lower: true})
next()
})
showSchema.pre(/^find/, function (next) {
this.populate({
path: 'cast',
select: '-__v'
})
next()
})
//Cast Schema
const castSchema = new Schema({
name: {
type: String,
trim: true,
text: true,
unique: true,
required: [true, 'Please provide name of actor']
},
profile_path: {
type: String
}
})
And this is how I did it
exports.add = async (req, res) => {
const show = {
category: req.body.category,
overview: req.body.overview,
poster_path: req.body.poster_path,
release_date: req.body.release_date,
title: req.body.title,
vote_average: req.body.vote_average,
}
try {
Cast.insertMany(req.body.cast, function(error, createdCast){
if(error){
console.log('Cast', error)
return
}
Show.create(show, function(error, createdShow){
if(error){
console.log('Show', error)
return
}
createdCast.forEach(element => {
createdShow.cast.push(element._id)
});
createdShow.save(function(error, show){
if(error){
return
}
console.log('saved Show', show)
})
})
})
} catch (error) {
console.log(error)
res.status(400).json({
message: 'fail',
error: error
})
}
}

Related

Mongoose Schema type Object Id Mapping

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.

Mongoose $push whole array in database

I'm having an error on my database where the sub array that I push to my database is missing and created a new id which means it detects data pushed inside.
here's the data I pushed. (toDeliver array has 4 objects inside).
I'm trying to send the whole array along with the string outside of the array.
after the request here what I receive on my database which is mongoDB.
the object inside toDeliver array is incomplete and created a ObjectId.
but the string outside of array was save the database.
here's my schema.
const OwnerSchema = mongoose.Schema({
username: {
require: true,
type: String,
},
password: {
require: true,
type: String,
},
isAdmin: {
type: Boolean,
default: true,
},
store: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
quantity: {
type: Number,
required: true,
},
categoryfilter: {
type: String,
required: true
},
description: {
type: String,
required: true,
},
specs: {
type: String,
required: true
},
imageBase64: {
type: String,
required: true,
},
timestamp: {
type: String,
required: true,
}
}
],
delivery: [
{
clientname: {
type: String,
required: true
},
address: {
type: String,
required: true
},
email: {
type: String,
required: true
},
number: {
type: Number,
required: true
},
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
],
toDeliverPaidViaPaypal: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
]
}
]
});
here's my backend.
export const delivery = async (req,res) => {
const { id } = req.params;
console.log(id);
console.log(req.body);
try {
if(!id) return res.status(404).json({ message: 'ID not found' });
await OwnerModels.findByIdAndUpdate(id,
{
$push: {
delivery:
{
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver:
[{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}]
,
toDeliverPaidViaPaypal: []
}
}
},
{
new: true,
},(err,res)=> {
if(err) return console.log({ error: err });
console.log({ result: res.delivery });
}).clone();
} catch (error) {
res.status(500).json({ message: 'Server error' });
}
}
hope ya guys can help me. thank you
I think you need to add square brackets around the toDeliver object to make it an array like your console object:
$push: {
delivery: {
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver: [{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}],
toDeliverPaidViaPaypal: []
}
}
Also add "_id: false" to toDelivery in your schema to repress id from being generated for the sub-object:
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
_id: false,
}
],

Mongoose with mongodb how to return just saved object that have full or customize property with other collection?

I need your help. I face with problem that I cant get full property or custom property that return from saved. My explain bellow :
Post model :
const postSchema = new mongoose.Schema(
{
content: {
type: String,
trim: true,
required: [true, "Post must be required"],
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
liked: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
disliked: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
imageUrl: {
type: String,
trim: true,
},
},
{ timestamps: true }
);
User model :
const userSchema = new mongoose.Schema({
name: {
type: String,
unique: true,
trim: true,
required: [true, 'Name must be required']
},
email: {
type: String,
unique: true,
trim: true,
required: [true, 'Email must be required']
},
password: {
type: String,
unique: true,
trim: true,
required: [true, 'Password must be required'],
minlength: [6, 'Password must be at least 6 characters']
},
}, {timestamps: true});
function create new Post and response Post to client :
exports.createOnePost = async (req, res, next) => {
console.log("create one");
try {
const post = await Post.create({
content: req.body.content,
author: req.user.userId,
liked: req.body.liked,
imageUrl: req.body.imgUrl,
});
res.status(200).json({
status: "success",
data: { post },
});
} catch (error) {
next(error);
}
};
After saved Post to database , I want to return full property of User and Post as well.
But I just received a Post :
{
content: 'sfdfsfd44434',
author: new ObjectId("61c1e08837d77c6187b9a746"),
liked: [],
disliked: [],
_id: new ObjectId("620c77d7574ce70b2417c1a1"),
createdAt: 2022-02-16T04:04:39.016Z,
updatedAt: 2022-02-16T04:04:39.016Z,
__v: 0
}
Hope that a solution from you guys. Thank you so much !
Since you already have the user you can just return it in your response like this:
res.status(200).json({
status: "success",
data: { post: { ...post.toObject(), author: req.user } },
});

Is there a way to remove the mongoose.Schema.ObjectId when Im getting an item from mongodb using mongoose?

I have two models
Movies.js
import mongoose from 'mongoose';
const MovieSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Please add a title to the movie'],
unique: true,
trim: true
},
description: {
type: String,
required: [true, 'Please add a description']
},
video: {
type: String,
required: [true, 'Please add a video']
},
genre: {
type: [String],
required: true,
enum: [
'Drama',
'Action',
'Comedy',
'Documentary',
]
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
MovieSchema.virtual('actors', {
ref: 'Actor',
localField: '_id',
foreignField: 'movies.movie',
justOne: false
})
const Movie = mongoose.model('Movie', MovieSchema);
export default Movie;
and Actor.js
import mongoose from 'mongoose'
const ActorSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: [true, 'Please add an actor name']
},
born: {
type: String,
required: [true, 'Please add a born date']
},
bornCountry: {
type: String,
required: [true, 'Please add a country']
},
description: {
type: String,
required: [true, 'Please add an actor description']
},
movies: [{
_id: false,
movie: {
type: mongoose.Schema.ObjectId,
ref: 'Movie'
},
role: {
type: String
}
}]
})
const Actor = mongoose.model('Actor', ActorSchema);
export default Actor;
Thats my controller method to get a single Movie from db
const getMovie = async (req, res, next) => {
const movie = await Movie.findById(req.params.id).populate('actors', 'name')
if (!movie) {
res.status(201).json({ success: false, message: 'Movie not found' })
}
res.status(200).json({ success: true, data: movie })
}
And thats the response
{
"success": true,
"data": {
"genre": [
"Action"
],
"_id": "603fca3353ffd238484c6fab",
"title": "Gambito da rainha",
"description": "Orphaned at the tender age of nine, prodigious introvert Beth Harmon discovers and masters the game of chess in 1960s USA. But child stardom comes at a price.",
"video": "https://www.youtube.com/watch?v=CDrieqwSdgI",
"__v": 0,
"actors": [
{
"_id": "60400f2e9c782f3d600a93f5",
"name": "Anya Taylor-Joy",
"movies": [
{
"movie": "603fca3353ffd238484c6fab"
}
]
}
],
"id": "603fca3353ffd238484c6fab"
}
}
Is there a way to not send back the data.actors.movies.movie id from the response? I tried to select only the name with .populate('actors', 'name'), but im still getting the mongoose.Schema.ObjectId in my response.
Fields referenced through virtual schema are only computed after the query is executed, so they are not reachable during the populate, see limitations.
If you want to unset the property you should use a custom function to exclude it.
Alternative solution is change your Movie schema, including Actor reference:
const MovieSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Please add a title to the movie'],
unique: true,
trim: true
},
description: {
type: String,
required: [true, 'Please add a description']
},
video: {
type: String,
required: [true, 'Please add a video']
},
genre: {
type: [String],
required: true,
enum: [
'Drama',
'Action',
'Comedy',
'Documentary',
]
},
actors: [{ type: mongoose.Schema.ObjectId, ref: 'Actor' }]
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
const Movie = mongoose.model('Movie', MovieSchema);
Query:
const movie = await Movie.findById(req.params.id).populate({
path: 'actors',
select: 'name born bornCountry description'
});
Result expected:
{
genre: [ 'Action' ],
actors: [
{
_id: 6040d7c9965dd306b41c7e9e,
name: 'Anya Taylor-Joy',
born: 'England',
bornCountry: 'England',
description: 'actor'
}
],
_id: 6040d7c9965dd306b41c7e9d,
title: 'Gambito da Rainha',
description: 'Orphaned at the tender age of nine',
video: 'https://www.youtube.com/watch?v=CDrieqwSdgI',
__v: 1,
id: '6040d7c9965dd306b41c7e9d'
}

mongodb error no documents found

Here is my code to find winners of a contest :
var date = moment().subtract('days', 1).format("YYYY-MM-DD");
console.log(date)
Contest.findOne({date: date}, function(err, contest){
if(!err){
if(contest){
Participant.find({ questionID : contest._id, random : { $near : [Math.random(), 0] } }).limit(5).exec(function(err, participants){
async.map(participants, function(participant, callback) {
contest.winners.push(participant)
contest.save(function(err) {
callback(err);
})
}, function(err) {
if (!err) {
console.log("Added winners to contest")
} else
console.log(err)
});
});
}
else{
console.log("No contest found")
}
}
else{
console.log(err)
}
})
Schema :
var ContestSchema = new Schema(
{
question:{
type: String,
trim: true
},
answers:[{
option: {type: String},
correct: {type: Boolean, default : false}
}],
date: {
type: String,
trim: true
},
priority: {
type: Number,
trim: true,
default : 0
},
winners : [{
type: Schema.Types.ObjectId,
ref: 'Participant'
}]
})
/*
*Participant Schema
*/
var ParticipantSchema = new Schema(
{
questionID:{
type: String,
trim: true
},
userID:{
type: String,
trim: true
},
name:{
type: String,
trim: true
},
email:{
type: String,
trim: true
},
mobile:{
type: String,
trim: true
},
address:{
type: String,
trim: true
},
landmark:{
type: String,
trim: true
},
city:{
type: String,
trim: true
},
state:{
type: String,
trim: true
},
random:{
type: [Number],
default : [Math.random(), 0],
index: '2d'
}
})
mongoose.model('Contest', ContestSchema)
mongoose.model('Participant', ParticipantSchema)
While saving winners to the contest, it saves the winners, but I am getting error :
{
"status": {
"error": 1,
"message": {
"message": "No matching document found.",
"name": "VersionError"
}
}
}
What is this error and how can I solve this ?
I would try rewriting the async.map operation. This would result in a single call to contest.save and you don't have to fear any race conditions.
async.map(participants, function(participant, callback) {
contest.winners.push(participant)
callback();
}, function(err) {
contest.save(function(err) {
if (!err) {
console.log("Added winners to contest")
} else {
console.log(err)
}
});
});

Resources