How to use populate in mongoose? - node.js

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,
})

Related

How to waiting while document updating in mongoose?

I have this code:
module.exports = async (msg) => {
const postId = msg.wall.copyHistory[0].id;
const userId = msg.wall.ownerId;
const bonusePostManager = new BonusePostManager(postId)
const post = await bonusePostManager.getPost(postId);
if (!post) return;
if (post.reposters.includes(userId)) return;
const balanceManager = new BalanceManager(userId, 0);
const doubleCheckReposter = await bonusePostManager.getPost(postId);
if (doubleCheckReposter?.reposters.includes(userId)) return;
bonusePostManager.addReposter(userId)
.catch(console.error)
.then((res) => {
balanceManager.plusBalance(post.bonuseAmount, 'balance').then(async (res) => {
await messageAssistant.sendMessage({
peer_id: userId,
text: `Вы сделали репост, вы получаете ${numberWithSpace(post.bonuseAmount)}`
})
})
})}
If a person makes a repost from two devices at the same time, then the document does not have time to update and allows the possibility of a double repost. I tried using the $addToSet operator:
addReposter(userId, postId = this.postId) {
return Bonuse.updateOne({
id: postId,
}, {
$addToSet: {
'reposters': userId
}
})
}
But it doesn't help, I really don't know how to fix it. I return the promiss everywhere, try to wait for them, but this does not fix the situation, please help me!
I also attach the BonusePost scheme:
const { Schema } = require('mongoose');
const PostSchema = new Schema({
postId: {
type: Number,
unique: true,
index: true
},
active: {
type: Boolean,
default: true,
index: true,
},
bonuseAmount: {
type: Number,
},
reposters: {
type: Array,
default: [],
}
})
module.exports = PostSchema;
And model:
const { model } = require('mongoose');
const PostSchema = require('./schema');
const Bonuse = new model('Bonuse', PostSchema)
module.exports = Bonuse;

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

Getting an empty array Mongoose

I can not get data from my MongoDb collection via mongoose - I'm getting an empty array out of my request. It only happens when I'm using a route which I posted below.
Code
router.get("/options", async (req,res) => {
try {
const { animalClass} = req.body;
if (!animalClass) {
const animalClasses = await AnimalClass.find({});
console.log(animalClasses);
return res
.status(200)
.json({animalClasses})
} else {
const animalTypes = await AnimalType.find({class: animalClass});
console.log(animalTypes);
return res
.status(200)
.json({animalTypes})
}
} catch (err) {
res
.status(500)
.json({msg: err})
}
});
Schema
const mongoose = require('mongoose');
const animalClassSchema = new mongoose.Schema({
name: {type: String, required: true}
})
module.exports = AnimalClass = mongoose.model('animalClass',animalClassSchema);
Specify the collection name when creating the schema, like:
const animalClassSchema = new mongoose.Schema({
name: {type: String, required: true}
}, { collection: 'animalClass' });
By default, Mongoose pluralizes your collection name. This option allows you to override that behavior. More info in the docs:
https://mongoosejs.com/docs/guide.html#collection

Filter collection on associated field ObjectId

SEE EDIT AT BOTTOM OF QUESTION.
I have a Node.js Express web application using MongoDB and Mongoose with collections for articles and comments. They have a one-to-many association where one article can have many comments.
The mongoose model schema is as follows:
// models/article
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: { type: String },
content: { type: String },
}, {timestamps: true});
module.exports = mongoose.model('Article', articleSchema);
and
// models/comment.js
const mongoose = require('mongoose');
const commentSchema = new mongoose.Schema({
content: { type: String },
article: { type: mongoose.Schema.Types.ObjectId, ref: 'Article' },
}, {timestamps: true});
module.exports = mongoose.model('Comment', commentSchema);
I have a route with a parameter for the article id
// routes.js
router.get('/articles/:articleId/comments', commentsController.list);
And a controller with a callback function to query the database and return the comments with the given article id. It uses the mongoose find() method filtering on the article id taken from the route parameter.
// controllers/commentsController.js
exports.list = (req, res, next) => {
Comment.find({ article: req.params.articleId })
.exec((err, comments) => {
res.render('comments/list', { title: 'Comments', comments: comments });
});
};
But this turns up no results. Just experimenting I can see that the req.params.articleId is a string and any comment.article is an object so they match with a loose comparison == but not a strict comparison === unless I convert comment.article.toString(). Anyway, what is the proper way to do such a query. All my attempts have failed.
EDIT: I found the problem. The code above is as it should be. The issue must be related to how I seeded the DB which I did directly in MongoDB. I deleted all those records and just added them from the application and it works with the code above.
One way to approach this is to add the comments to your article model.
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: { type: String },
content: { type: String },
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
}, {timestamps: true});
articleSchema.set('toJSON', {
transform: (document, returnedObject) => {
const article = returnedObject
article.id = article._id.toString()
delete article._id
}
})
module.exports = mongoose.model('Article', articleSchema);
Then get the comments in one of these ways:
const router = require('express').Router()
const Article = require('../models/article')
const Comment = require('../models/comment')
// article with comments
router.get('/:id', async (request, response, next) => {
try {
const article = await Article.findById(request.params.id)
.populate(
'comments', {
content: 1
}
)
response.json(article.toJSON())
} catch (err) {
console.log(err)
}
})
// list of comments belonging to an article
router.get('/:id/comments', async (request, response, next) => {
try {
const article = await Article.findById(request.params.id)
if (!article) {
response.status(404).json({ error: 'invalid request' })
}
const comments = await Comment.find({ article: request.params.id })
.populate(
'article', {
title: 1
}
)
response.json(comments.map(comment => comment.toJSON()))
} catch (err) {
console.log(err)
}
})
module.exports = router

Cascade Delete in mongo

I am new to MongoDB. I created 4 collections & they are connected with each other. (I am using node.js to write it)
Here, it's my question. How can I delete all records at once? Is there something like deep level population?
This one holds all models.
const DataModel = mongoose.Schema({
_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', require: true},
order: { type: mongoose.Schema.Types.ObjectId, ref: 'Order', require: true},
});
User model
const userSchema = mongoose.Schema({//other stuff});
Order model
const orderSchema = mongoose.Schema({
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
//other stuff
});
Product model
const productSchema = mongoose.Schema({//other stuff});
I can delete the entry with these code from the database, but the other entries still there
exports.delete_data = (req, res, next) => {
const id = req.params.userId;
userDataModel.deleteOne({_id: id})
.exec()
.then(docs => {
res.status(200).json({
message: 'Record Deleted',
request: {
type: 'POST'
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
Update: However, I wonder, Could I call other defined delete functions for order, product inside delete_data
As #Geert-Jan suggest, cascade delete is my solution. The link that geert-jan gave solve my problem. However, I wonder, Could I call other defined delete functions for order, product inside delete_data
i did this and it could be good for someone who wants to delete documents in cascade linked to any field of a model.
async blackHole() {
try {
const rtn = new ApiResponse<any>();
const userId = id;
const accountId = mongoose.Types.ObjectId(id);
var CollectionNames: any[] = [];
mongoose.connection.db.listCollections().toArray(function (err, collections) {
CollectionNames = collections.map(c => c.name);
CollectionNames.forEach((element: any) => {
mongoose.connection.db.collection(element).deleteMany({ "account": accountId });
});
});
const accountController = new AccountController(this.wsParams);
await accountController.delete(id)
await super.delete(userId);
return rtn;
} catch (error: any) {
const rtn = new ApiResponse<any>();
rtn.message = error;
rtn.success = false;
rtn.status = 422;
return rtn;
}
}
I hope you can use it :D

Resources