saving ref of another document with mongoose and nodejs - node.js

i am new using mongodb and i am practicing with ref and populate... but i have a silly problem.
i am receiving from my client a object like this.
{
"name": "Jhon"
"books": [
{
"title": "whatever",
"pages": 300
},
{
"title": "otherBook",
"pages": 450
}
]
}
So i have two schemas, authorSchema and booksSchema... so what i pretend is save the books and take the _id of each book to save the author with it.
My code in nodejs
authorCtrl.saveAuthor = (req, res) => {
var booksId= []
for (i = 0; i < req.body.books.length; i++) {
booksModel.create(req.body.books[i], function (err, book) {
booksId.push(book._id)
})
}
var author= {
name: req.body.name,
books: booksId
}
console.log(author) // here i check and books array is empty,
authorModel.create(author).then((authorSaved) => {
res.json(authorSaved)
}).catch(err => {
res.json(err)
})
}
i know it is an asynchronous problem... but how can i do it??.. or what is the best practice to ref collections?
/////EDIT//////
Here are my schemas
Authors Schema
const mongoose = require('mongoose')
const { Schema } = mongoose;
const authorsSchema = new Schema({
name: { type: String },
books: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'books'
}]
})
module.exports = mongoose.model('authors', authorsSchema);
Books Schema
const mongoose = require('mongoose')
const { Schema } = mongoose;
const booksSchema = new Schema({
title: { type: String },
pages: { type: Number }
})
module.exports = mongoose.model('books', booksSchema);

Authors Schema:
const mongoose = require('mongoose')
const { Schema } = mongoose;
const authorsSchema = new Schema({
name: String,
books: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Book'
}]
})
module.exports = mongoose.model('Author', authorsSchema);
Books Schema:
const mongoose = require('mongoose')
const { Schema } = mongoose;
const booksSchema = new Schema({
title: String,
pages: Number,
})
module.exports = mongoose.model('Book', booksSchema);
NodeJS Code:
const author = {
name: req.body.name
}
AuthorModel.create(author, (error, createdAuthor)=>{
//handle error
BookModel.insertMany(req.body.books, function (err, createdbooks) {
// handle error
createdAuthor.books.push(createdbooks);
createdAuthor.save();
})
}

Try this,
authorCtrl.saveAuthor = (req, res) => {
var booksId= [];
for (var i = req.body.books.length - 1; i >= 0; i--) {
booksModel.create(req.body.books[i], (err, book) => {
booksId.push(book._id);
if(i == 0) { // This if condition executes at the end of the for loop.
var author= {
name: req.body.name,
books: booksId
};
console.log(author);
authorModel.create(author).then((authorSaved) => {
res.json(authorSaved);
}).catch(err => {
res.json(err);
});
}
});
}
}
Hope it helps...

You can do it with Javascript promises as below:
var booksId = [];
var promises = [];
req.body.books.forEach(element => {
promises.push(insertBook(element));
});
Promise.all(promises)
.then(function(data){
/* do stuff when success */
console.log('##GREAT##',booksId);
/*** INSERT ARRAY OF BOOK IDs INTO authorModel***/
})
.catch(function(err){
/* error handling */
});
function insertBook(element){
return new Promise(function(resolve, reject){
var book = new booksModel({
title: element.title,
page: element.page
});
book.save(function(err,data){
if(err){
console.log(err);
reject(err)
}else{
console.log('#success');
booksId.push(data._id)
resolve();
}
});
});
}

Related

Tools to create a functional Shopping Cart

I'm creating an e-commerce using Reactjs for frontend and Nodejs for backend (also express), and i want to create an shopping cart "connected" to the user account, where the user can recharge the page, close it, and the cart doesn't reset.
I'm looking for tools to create this, or some tutorial to do it with node-express, thanks! (i don't have code yet, cause i don't know from where starts)
This is User Model with Cart( with add-to-cart and remove from cart Function )
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
resetToken: String,
resetTokenExpiration: Date,
cart: {
items: [
{
productId: {
type: Schema.Types.ObjectId,
ref: "Product",
required: true,
},
quantity: {
type: Number,
required: true,
},
},
],
},
});
userSchema.methods.addToCart = function (product) {
const cartProductIndex = this.cart.items.findIndex((cp) => {
return cp.productId.toString() === product._id.toString();
});
let newQuantity = 1;
const updatedCartItems = [...this.cart.items];
if (cartProductIndex >= 0) {
newQuantity = this.cart.items[cartProductIndex].quantity + 1;
updatedCartItems[cartProductIndex].quantity = newQuantity;
} else {
updatedCartItems.push({
productId: product._id,
quantity: newQuantity,
});
}
const updatedCart = {
items: updatedCartItems,
};
this.cart = updatedCart;
return this.save();
};
userSchema.methods.removeFromCart = function (productId) {
const UpdatedCartItems = this.cart.items.filter((item) => {
return item.productId.toString() !== productId.toString();
});
this.cart.items = UpdatedCartItems;
return this.save();
};
userSchema.methods.clearCart = function () {
this.cart = { items: [] };
return this.save();
};
module.exports = mongoose.model("User", userSchema);
Here, Routes
router.get("/cart", (req, res, next) => {
req.user
.populate("cart.items.productId")
.then((user) => {
const products = user.cart.items;
// console.log(products);
res.render("home/cart", {
path: "/cart",
pageTitle: "Your cart",
products: products,
isAuthenticated: req.session.isLoggedIn,
});
// console.log(products);
})
.catch((err) => {
console.error(err);
});
});
router.post("/cart",(req, res, next) => {
const prodId = req.body.productId;
// console.log(prodId);
Product.findById(prodId)
.then((product) => {
return req.user.addToCart(product);
})
.then((result) => {
// console.log(result);
res.redirect("/cart");
});
});
Tech:-
Frontend:- Ejs View Engine
Backend:- Express, MongoDB Atlas

Mongoose - Cannot modify field value

I am building a simple shop backend for practice purposes. I have three schemas Product, Customer and Order.
What I am trying to achieve is to subtract the ordered quantity from the stock quantity for each product inside an order, when the order is created. Clearly I am doing something wrong cause my productsToUpdateInDbArray contains the correct products (checked it with console log) but I can't find a way to make it work.
stockQty field inside Products collection is not updating.
My controller code is:
'use strict'
// require validator for string validation
const validator = require('validator');
// import Order, Customer, Product Models
const Order = require("../models/order.model");
const Customer = require("../models/customer.model");
const Product = require("../models/product.model");
// DEFINE CONTROLLER FUNCTIONS
// listAllOrders function - To list all orders
exports.listAllOrders = (req, res) => {
Order.find({}, (err, orders) => {
if (err) {
return res.status(500).send(`Internal server error: ${error}`);
}
if (orders && orders.length === 0) {
return res.status(404).send(`No orders found!`);
}
return res.status(200).json(orders);
});
};
// createNewOrder function - To create new order
exports.createNewOrder = (req, res) => {
const customerId = req.body?.customerId;
const productsArray = req.body?.products;
let productsToUpdateInDbArray = [];
if (!validator.isMongoId(customerId)) {
return res.status(400).send('Invalid customer Id');
}
Customer.findById(customerId, async (err, customer) => {
if (err) {
return res.status(500).send(`Internal server error: ${error}`);
}
if (!customer) {
return res.status(404).send(`No customers found!`);
}
if (!productsArray || productsArray.length === 0) {
return res.status(400).send(`No products found in the order!`);
}
for (let product of productsArray) {
if (!validator.isMongoId(product?.productId)) {
return res.status(400).send('Invalid product Id');
}
if (!product?.quantity || product?.quantity < 1) {
return res.status(400).send('Invalid product quantity');
}
let productFound = await Product.findById(product?.productId).exec();
if (!productFound) {
return res.status(404).send('Product not found!');
}
if (productFound.stockQty < product.quantity) {
return res.status(400).send('Not enough product quantity in stock')
}
productFound.stockQty -= product.quantity;
productsToUpdateInDbArray.push(productFound);
}
console.log(productsToUpdateInDbArray)
const newOrder = new Order(req.body);
newOrder.save((err, order) => {
if (err) {
return res.status(500).send(`Internal server error: ${error}`);
}
for (let item of productsToUpdateInDbArray) {
const filter = { _id: item._id };
const update = { stockQty: item.stockQty };
Product.findOneAndUpdate( filter, update )
}
return res.status(201).json(order);
});
});
};
And my models are:
'use strict';
// Import mongoose
const mongoose = require("mongoose");
// Declare schema and assign Schema class
const Schema = mongoose.Schema;
// Create Schema Instance and add schema propertise
const ProductSchema = new Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
imageUrl: {
type: String,
required: true
},
stockQty: {
type: Number,
required: true
}
});
// create and export model
module.exports = mongoose.model("Products", ProductSchema);
'use strict';
// Import mongoose
const mongoose = require("mongoose");
// Declare schema and assign Schema class
const Schema = mongoose.Schema;
// Create Schema Instance and add schema propertise
const OrderSchema = new Schema({
products: [
{
productId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Products"
},
quantity: {
type: Number,
default: 1
}
}
],
customerId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Customers"
}
});
// create and export model
module.exports = mongoose.model("Orders", OrderSchema);
findOneAndUpdate will only execute the query when a callback is passed. So in your case you can either add an await or callback.
await Product.findOneAndUpdate( filter, update );
or
Product.findOneAndUpdate( filter, update, callback );

Route for getting a review for a book in a MERN-stack book review app?

I am trying to write a MERN stack app used for reviewing books.
I have 2 MongoDB (Mongoose) schemas: models/Book.js and models/Review.js, where one Book can have multiple Reviews.
These are my schemas:
models/Review.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let reviewSchema = new Schema(
{
text: {
type: String
},
book: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Book'
}
}
);
module.exports = mongoose.model('Review', reviewSchema)
models/Book.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let bookSchema = new Schema(
{
title: {
type: String
},
detail: {
type: String
}
}, {
collection: 'books'
}
);
module.exports = mongoose.model('Book', bookSchema)
How should a route look like if I want to get all data about a single book via this URL: /book/<book_id_from_mongodb>?
I can only get Book's data but I don't know how to get all Reviews that belong to that particular book:
/routes/book.route.js:
let mongoose = require('mongoose'),
express = require('express'),
router = express.Router();
let bookSchema = require('../models/Book');
let reviewSchema = require('../models/Review');
// Get a single book - HOW SHOULD I MODIFY THIS ROUTE TO GET ALSO ALL REVIEWS FOR THAT BOOK ?
router.route('/book/:id').get((req, res) => {
bookSchema.findById(req.params.id, (error, data) => {
if (error) {
res.status(500).send(err);
} else {
res.status(200).json(data)
}
})
})
I tried this (not working):
router.route('/book/:id').get((req, res) => {
bookSchema.findById(req.params.id, (error, data) => {
if (error) {
res.status(500).send(err);
} else {
var bookData = data;
reviewSchema.find({"book": mongoose.Schema.Types.ObjectId(req.params.id)}, (error, data) => {
if (error) {
res.status(500).send(err);
} else {
var reviewData = data;
bookData.reviews = reviewData;
res.status(200).json(bookData);
}
});
}
})
})
The best way to do it would be to put a reviews element into your bookSchema and reference the ObjectId of the review.
Then on your get route, use .populate("reviews").exec(your callback).
Book Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let bookSchema = new Schema(
{
title: {
type: String
},
detail: {
type: String
}
}, {
collection: 'books'
}, {
reviews: [
type: mongoose.Schema.Types.ObjectId,
ref: 'Review'
]
}
);
module.exports = mongoose.model('Book', bookSchema)
Route
let mongoose = require('mongoose'),
express = require('express'),
router = express.Router();
let bookSchema = require('../models/Book');
let reviewSchema = require('../models/Review');
// Get a single book - HOW SHOULD I MODIFY THIS ROUTE TO GET ALSO ALL REVIEWS FOR THAT BOOK ?
router.route('/book/:id').get((req, res) => {
bookSchema.findById(req.params.id).populate("reviews").exec((error, data) => {
if (error) {
res.status(500).send(err);
} else {
res.status(200).json(data)
}
})
})
When creating a review you need to push it to the bookSchema.reviews and then use bookSchema.save() to it.
Let me know if it does not work.
I may have missed something.

I get a save() is a not a function error mongoose

exports.clearHours = (req, res, next) => {
Hour
.find({ user: req.body.userId })
.then(hour => {
for (let i=0;i<hour.length;i++) {
hour[i].hours = 0;
}
return hour.save()
})
.then(result => {
res.status(200).json({message: 'Working hours have been successfully updated.'});
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
})
};
I am trying to save the formatted array on the database and I get this error. The updated code is passed properly but when I am trying to save the array it comes up with this error. Any ideas why?
This is my hour model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const hourSchema = new Schema ({
day: {
type: String,
required: true
},
hours: {
type: Number,
required: true
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
}
});
module.exports = mongoose.model('Hour', hourSchema);
It seems you are fetching the document into memory and re-setting the hour field to 0, better you can run an update query into the database itself.
On top require mongoose -
const mongoose = require('mongoose');
Below is clearHours method refactored.
exports.clearHours = async (req, res, next) => {
try {
const query = {
user: mongoose.Types.ObjectId(req.body.userId)
};
const update = {
$set: {
hours: 0
}
};
await Hour.update(query, update).exec();
res.status(200).json({message: 'Working hours have been successfully updated.'});
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};

Mongoose query doesn't execute properly, no error message

Here's my Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostsSchema = new Schema({
userId: String,
postId: String,
title: String,
description: String,
tags: { many: String, where: String, what: String },
date: { type: Date, default: Date.now },
}, { collection : 'posts'});
const Posts = mongoose.model('Post', PostsSchema);
module.exports = Posts;
Here's my route with the query:
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Posts = require('../models/Posts');
router.get('/', (req, res, next) => {
const refreshOrLoadMore = params.refreshOrLoadMore || '';
if (refreshOrLoadMore === 'loadMore') {
console.log('1');
Posts.find({}).sort({date:-1}).limit(10, (err, data) => {
if(err) {
console.log('2');
res.send(err);
} else {
console.log('3');
res.json(data);
}
});
}
});
The if statement returns true and the first console.log is triggered. But after that none of the other console.logs are triggered and just nothing happens. No data is being send and no error is being send.
So my guess is, that i did something wrong with the Schema, but i did it just as i did my other ones and they do work.
Can someone point out where i went wrong?
Thanks in advance!
Try this
if (refreshOrLoadMore === 'loadMore') {
console.log('1');
Posts.find({}).sort({date:-1}).limit(10)
.exec((err, data) => {
if(err) {
console.log('2');
res.send(err);
} else {
console.log('3');
res.json(data);
}
});
}

Resources