Crud operations mongodb , nodejs , express , mongoose - node.js

import mongoose from "mongoose";
const productSchema = mongoose.Schema(
{
name: {
type: String,
required: true
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
}
},
{ timestamps: true }
);
const Product = mongoose.model("Product", productSchema);
export default Product;
import mongoose from "mongoose";
const categorySchema = mongoose.Schema(
{
name: {
type: String,
required: true,
unique: true
}
},
{ timestamps: true }
);
const Category = mongoose.model("Category", categorySchema);
export default Category;
// create Products
const createProduct = asyncHandler(async (req, res) => {
const { name } = req.body;
const product = new Product({
name,
category: req.category._id
});
if (product) {
const createdProduct = await product.save();
res.status(201).json(createdProduct);
} else {
res.status(404).json({ message: "Product already exists" });
}
});
// update product
const updateProduct = asyncHandler(async (req, res) => {
const { name, categoryName } = req.body;
const product = await Product.findById(req.params.id);
if (product) {
product.name = name;
product.categoryName = categoryName;
const updatedProduct = await product.save();
res.json(updatedProduct);
} else {
res.status(404);
throw new Error("Product not found");
}
});
I'm not able to get the category data into the product data its shows
BSON error , I want the data to look this way.
> products = [ {
> _id : "", name: "", category : { _id:"", name:"" } } ]
and i want to use this data to as create api - product name &
category name which automatically creates a id for product & category
just including products & category names

I will try to help you as what I have understood from your post.
First of all in the product schema you are only saving category ID and you are not mentioning the product category name field. You need to add that to your productSchema as shown below.
const productSchema = mongoose.Schema(
{
name: {
type: String,
required: true
},
categoryId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
categoryName:{
type:String
}
},
{ timestamps: true }
);
const Product = mongoose.model("Product", productSchema);
Now what you have to do is that, since you have the categoryId, you have to fire a query to your Category collection before saving as shown below.
// Create Products
const createProduct = asyncHandler(async (req, res) => {
const { name, categoryId } = req.body;
//Querying Category collection
const category = await Category.find({_id:categoryId})
//Creating Product Object
const product = new Product({
name,
categoryId,
categoryName:category.name
});
if (product) {
const createdProduct = await product.save();
res.status(201).json(createdProduct);
} else {
res.status(404).json({ message: "Product already exists" });
}
});
Now you will be able to save the name also in the product collection.
And going through your update handler you should not update the category name in your product document, what the best practice is to use categoryId from req.body and query to category collection for the name. If the you want to change the category name change it in your category collection and use that particular object Id to update it in your product collection.
Hope this helps you.
Thanks

Related

How to save a document that is associated to another foreign key? in MONGODB (NO SQL)

I would like to ask on how I can save a Sub Category by getting the ID of the Category.
Category Model
import mongoose from 'mongoose'
const categorySchema = mongoose.Schema({
name: {
type: String,
required: true,
},
})
const Category = mongoose.model('Category', categorySchema)
export default Category
Sub Category model
import mongoose from 'mongoose'
const subCategorySchema = mongoose.Schema({
subname: {
type: String,
required: true,
},
categoryid: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Category',
},
})
const SubCategory = mongoose.model('SubCategory', subCategorySchema)
export default SubCategory
My Controller
const createSubCategory = asyncHandler(async (req, res) => {
const { subname, categoryid} = req.body
const subCategoryExists = await SubCategory.findOne({
subcategoryname,
}).populate('categoryname')
if (subCategoryExists) {
res.status(400)
throw new Error('SubCategory already exists')
}
const name = await Category.find({}).populate('_id')
const subCategory = new SubCategory({
categoryid: name,
subcategoryname,
})
const createdSubCategory = await subCategory.save()
if (createdSubCategory) {
res.status(201).json({
_id: createdSubCategory._id,
categoryid: createdSubCategory,
subname: createdSubCategory.subname,
})
} else {
res.status(400)
throw new Error('Invalid SubCategory')
}
})
My question is
How am I going to save my Sub Category in which I will select the ID Value of the Category and insert it to the Sub Category - categoryname field ? Is is working but all I gets is the first ID in the category even if you typed another ID.
I wanted to have something like this upon saving in mongodb
SubCategory
{
_id: 123,
subcategory: "Sub Category",
categoryname:{
_id: 123456(categoryid),
categoryname: "Categoryname"
}
}
I have managed to figure it out, you need to make it match. In your request.body you have your field name called categoryid so in the Category.FindOne({_id:}
you need to match what you type in categoryid to the findone of _id in order for you to get that. :D
const createSubCategory = asyncHandler(async (req, res) => {
const { subname, categoryid} = req.body
const subCategoryExists = await SubCategory.findOne({
subcategoryname,
}).populate('categoryname')
if (subCategoryExists) {
res.status(400)
throw new Error('SubCategory already exists')
}
const id= await Category.find({_id: categoryid}).populate('_id')
const subCategory = new SubCategory({
categoryid: id,
subcategoryname,
})
const createdSubCategory = await subCategory.save()
if (createdSubCategory) {
res.status(201).json({
_id: createdSubCategory._id,
categoryid: createdSubCategory,
subname: createdSubCategory.subname,
})
} else {
res.status(400)
throw new Error('Invalid SubCategory')
}
})

MongoDB Update a document's key when another document is updated

I am using Express.js, Node.js, MongoDB, and Mongoose stack
I have three documents in the database which are Warehouses, Inventories, and Items.
A Warehouse has one Inventory, and each Inventory is assigned to a Warehouse.
An Inventory contains many Items.
items.model.js
const mongoose = require('mongoose');
const itemSchema = new mongoose.Schema({
name:{
type: String
},
dateCreated:{
type: Date,
default: Date.now()
},
price:{
type:Number
},
type:{
type:String
},
category:{
type:String
},
description:{
type: String
},
picture:{
type: String
}
})
const Item = mongoose.model('item',itemSchema)
module.exports.Item=Item
module.exports.itemSchema = itemSchema
inventories.model.js
const mongoose = require('mongoose');
const itemSchema = require('../items/items.model').itemSchema;
const inventorySchema = new mongoose.Schema({
name:{
type: String,
unique: true
},
dateCreated:{
type: Date
},
items:{
type:[itemSchema]
}
})
const Inventory = mongoose.model('inventory',inventorySchema)
module.exports.inventorySchema=inventorySchema;
module.exports.Inventory=Inventory;
warehouses.model.js
const mongoose = require('mongoose');
const inventorySchema = require('../inventories/inventories.model').inventorySchema
const warehouseSchema = new mongoose.Schema({
name:{
type:String
},
location:{
type:String
},
inventory:{
type:inventorySchema
},
dateCreated:{
type:Date,
default:Date.now()
},
numberOfEmployees:{
type:Number
}
})
const Warehouse = mongoose.model('warehouse',warehouseSchema)
module.exports.Warehouse=Warehouse
I have an endpoint which assigns an Inventory to a Warehouse based on the Inventory's name. I also have an endpoint that adds an Item to an Inventory
warehouses.controller.js
/**
*
* Assign an inventory to warehouse based on
* Warehouse (id), inventory (name)
*/
const assignInventory = () => async (req, res) => {
try {
const inventoryName = req.body.inventory
const inventoryDoc = await Inventory.findOne({ name: inventoryName })
if (!inventoryDoc) {
return res.status(404).json('No Inventory with this ID exists!')
}
const warehouseID = req.params.id
const doc = await Warehouse.findByIdAndUpdate(
{ _id: warehouseID },
{ ...req.body, inventory: inventoryDoc }
)
if (!doc) {
return res.status(404).end()
}
return res.status(200).json({
message: 'Assigned Inventory to warehouse successfully',
data: doc
})
} catch (error) {
console.error(error)
res.status(400).end()
}
}
inventories.controller.js
/**
*
* Add an Item to an Inventory based on
* Inventory (id), Item (name)
*/
const addItemToInventory = () => async(req,res)=>{
try {
//Retreive the inventory id from the URL Parameters
const inventoryID= req.params.id
//Retreive the item name from the request body
const itemName= req.body.name;
//Fetch the item document from the database based on the request body
const itemDoc = await Item.find({name:itemName})
if(!itemDoc)
{
return res.status(404).json(`Can't find item with this name`)
}
console.log(itemDoc)
//Update the inventory with the the new item document added to its item list
const inventoryDoc = await Inventory.findById({_id:inventoryID})
console.log(inventoryDoc)
var inventoryList = inventoryDoc.items
console.log('Updated')
inventoryList.push(itemDoc)
console.log(inventoryList)
const updatedInventoryDoc = await Inventory.findByIdAndUpdate({_id:inventoryID},{$push:{items:itemDoc}})
if(!updatedInventoryDoc){
return res.status(404).end()
}
res.status(200).json({data: updatedInventoryDoc})
} catch (e) {
console.error(e)
}
}
The problem is that whenever I add an Item to an Inventory that is already assigned to a Warehouse, if I fetched that Warehouse, it will not show the added Item.
In the image above, I added an Item to the Inventory and fetched that Inventory.
The Inventory was previously added to a Warehouse.
Yet as seen above here, The Warehouse still has only one item inside it.
Is there anything that can reference an Inventory inside a Warehouse? so that after any update to any Inventory, the Warehouse will listen to these updates.
Thanks in advance.
Edit 1: Added Inventory, Warehouse, and Item models.
After searching I found out that I should change the structure of the warehouse.model.js to make it referring to the ObjectID of an Inventory, so it is going to look like this.
const mongoose = require('mongoose');
const inventorySchema = require('../inventories/inventories.model').inventorySchema
const warehouseSchema = new mongoose.Schema({
name:{
type:String
},
location:{
type:String
},
//Instead of inventory:inventorySchema
inventory:{
type: mongoose.Schema.Types.ObjectId,
ref: "inventory"
},
dateCreated:{
type:Date,
default:Date.now()
},
numberOfEmployees:{
type:Number
}
})
const Warehouse = mongoose.model('warehouse',warehouseSchema)
module.exports.Warehouse=Warehouse
and inside the warehouses.controller.js, we should call populate on any database call that retrieves the Warehouse documents like this
const readWarehouse = () => async (req, res) => {
try {
const doc = await Warehouse.find({}).populate('inventory')
if (!doc) {
return res.status(404).end()
}
res.status(200).json({ data: doc })
} catch (e) {
console.error(e)
res.status(400).end()
}
}
const readOneWarehouse = () => async (req, res) => {
try {
const id = req.params.id
const doc = await Warehouse.findOne({ _id: id }).populate('inventory')
if (!doc) {
return res.status(404).end()
}
res.status(200).json({ data: doc })
} catch (e) {
console.error(e)
res.status(400).end()
}
}
Also updated the assignInventory() to be like that
const assignInventory = () => async (req, res) => {
try {
const inventoryName = req.body.inventory
const inventoryDoc = await Inventory.findOne({ name: inventoryName })
if (!inventoryDoc) {
return res.status(404).json('No Inventory with this ID exists!')
}
const inventoryDocID=inventoryDoc._id
const warehouseID = req.params.id
const doc = await Warehouse.findByIdAndUpdate(
{ _id: warehouseID },
{ ...req.body, inventory: inventoryDocID }
)
if (!doc) {
return res.status(404).end()
}
return res.status(200).json({
message: 'Assigned Inventory to warehouse successfully',
data: doc
})
} catch (error) {
console.error(error)
res.status(400).end()
}
}

How to use populate in mongoose?

I have two collections where one holds list of systems and the other holds list of battery attached to the system. I want to use populate method so that when I run the query using system id it shows me the details of battery is also shown.
My schema for system and battery are as follows.
const mongoose = require('mongoose');
const { Schema } = mongoose;
const SystemSchema = new Schema(
{
serialNumber: String,
location: String,
BPIDs: [
{
type: Schema.Types.ObjectId,
ref: 'batteryPack'
}
]
},
{
timestamps: true
}
);
const Systems = mongoose.model('system', SystemSchema);
module.exports = Systems;
My battery model is as follows:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const batteryPackSchema = new Schema(
{
systemSerialNumber: String,
batteryID: Number,
batteryVoltage: Number,
totalCurrent: Number,
stateOfCharge: Number
{
timestamps: true
}
);
const BatteryPacks = mongoose.model('batteryPack', batteryPackSchema);
module.exports = BatteryPacks;
My query route is as follows:
router.get('/details/:id', async (req, res) => {
try {
const deviceDetails = await Systems.findOne({ _id: req.params.id }).populate('batteryPack').lean();
return res.status(200).send({
deviceDetails
});
} catch (error) {
return res.status(500).send(error.stack);
}
});
On running query through postman it shows the following error:
MongooseError: Cannot populate path batteryPack because it is not in your schema. Set the strictPopulate option to
false to override.
at getModelsMapForPopulate
I was passing wrong argument inside populate method. The code is working flawlessly now.
const deviceDetails = await Systems.findOne({ _id: req.params.id }).populate('BPIDs').lean();
const deviceDetails = await Systems.findOne({ _id: req.params.id },{},{
populate: { path: 'BPIDs' },
lean: true,
})

How to add multiple foreign keys for one record in express js using sequelize

I have a table called Stories there are few columns and 3 foreign keys: category, sub category and language.
To make association, I have added sequelize function as below which added CategoryId, SubCategoryId and LanguageId columns to the Story table.
story.belongsTo(category, { as: 'Category' });
story.belongsTo(subCategory, { as: 'SubCategory' });
story.belongsTo(language, { as: 'Language' });
How can I add story to story table?
Below is my code.
const Category = require('../models/category');
const SubCategory = require('../models/subCategory');
const Language = require('../models/language');
exports.postStory = (req, res, next) => {
const storyTitle = req.body.title;
const description = req.body.description;
const categoryId = req.body.categoryId;
const subCategoryId = req.body.subCategoryId;
const languageId = req.body.languageId;
Category.findOne({
where: {
id: categoryId
}
}).then(category => {
return SubCategory.findOne({
where: {
id: subCategoryId
}
})
}).then(subcategory => {
return Language.findOne({
where: {
id: languageId
}
}).then(language => {
//save operation here
const story = new Story({
story_type: storyType,
title: storyTitle,
description: description,
categoryId: categoryId,
subCategoryId: subCategoryId,
languageId: languageId,
createdBy: 1
});
return story.save()
.then((result) => {
res
.status(201)
.json({
message: "Story added to database",
statusCode: 201,
CreatedBy: 1,
result: result,
});
})
})
}).catch((error) => {
if (!error.statusCode) {
error.statusCode = 500;
}
next(error);
});
Although it is adding stories to Story table but it is not adding categoryId, Sub categoryId and languageId, it is adding only null values for those fields as per below screen capture.
I cannot figure out how to add CategoryId, SubCategoryId, LanguageId to story.
The foreign key you are using while creating your story instance is in camelCase but you have defined the alias in PascalCase.
Change either alias in you association definition
story.belongsTo(category, { as: 'category' });
story.belongsTo(subCategory, { as: 'subCategory' });
story.belongsTo(language, { as: 'language' });
Or Change key in story instance
const story = new Story({
story_type: storyType,
title: storyTitle,
description: description,
Category: categoryId,
SubCategoryId: subCategoryId,
LanguageId: languageId,
createdBy: 1
});
Note: Add foreign key constraint for all associations to not allow insert/update on null value.
story.belongsTo(category, {
as: "category",
foreignKey: {
allowNull: false
}
});

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 );

Resources