Mongoose Nested Queries - find and updating two fields in different documents - node.js

What Im trying to do is have one user send an amount to another user, Id like for the amount to be subtracted from the sender's balance, and added to the receiver's balance. The problem is that the receivers balance is updated and works, but the previous query to subtract the amount from the sender isn't working.
I understand there are no joins in mongoose (at least not in the classical sense), so Id need to query the user's balance first and then update it in another query. But surely there is a way to nest these queries? Im hoping I just have the syntax wrong.
user.js
const { Decimal128 } = require("mongodb");
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
useraddress: {
type: String,
required: true,
},
userbalance: {
type: Decimal128,
required: true,
}
});
module.exports = User = mongoose.model("users", UserSchema);
server.js
app.post("/sendamount", function (req, res) {
var amount = 100;
var senderAddress = "Bob123";
var receiverAddress = req.body.receiver;
// Take amount from sender balance
User.findOne({useraddress:senderAddress}, (err, sub) => {
if (err) return handleError(err);
var mybalance = parseFloat(sub.userbalance)
console.log(mybalance)
User.findOneAndUpdate(
{ useraddress: senderAddress },
{ userbalance: mybalance - amount }),
// send to receiver balance
User.findOne({useraddress:receiverAddress}, (err, sub2) => {
if (err) return handleError(err);
var receiverbalance = parseFloat(sub2.userbalance)
console.log(receiverbalance)
// add amount to receiver's balance
User.findOneAndUpdate(
{ useraddress: receiverAddress },
{ userbalance: receiverbalance + amount },
function (err, data) {
if (err) res.send(err);
res.send(data);
console.log("found " + data)
}
)
})
})
})
Would someone mind checking my code? Thanks

try to this subtrack process
I added example field, you have this areas change
const asyncSubtrackProcess = async (id, amount) => await User.findOneAndUpdate({
_id: mongoose.Types.ObjectId(id),
userbalance: { $gt: 0 }
},
{
$inc: {
userbalance: - parseInt(amount)
}
});
const result = () => amounts.map(async item => await asyncSubtrackProcess(item.id, item.amount));
result();

Related

Mongoose get multiple collection data using query

I am trying to join two mongoose collection and fetch all related details using the query which i have mention below. Problem is I am getting only one table details employee collection not able get two collection detail when I call GetEmployeeDetails(emp_id) . Need a suggestion. How to get two collection data in one query.
const EmployeeInfoSchema = mongoose.Schema({
employee_id: String,
client_id: {
type: Schema.Types.Number,
ref: "client",
},
email: String,
contact: String,
});
const ClientInfoSchema = mongoose.Schema({
client_id: Number,
employee_id: {
type: Schema.Types.String,
ref: "employee",
},
project: String,
organization: String,
});
let employeeInfo = mongoose.model("employee", EmployeeInfoSchema);
let clientInfo = mongoose.model("client", ClientInfoSchema);
module.exports = { employeeInfo, clientInfo };
Query
async function GetEmployeeDetails(emp_id) {
let employee_info = await Storage.employeeInfo
.find()
.where({ employee_id: emp_id })
.populate({
path: "client",
})
.exec(function (err, block) {
if (err) {
console.log("%s", err);
}
console.log("Employee details is %s", employee_info);
});
return employee_info;
}
Try to change your GetEmployeeDetails method like this:
async function GetEmployeeDetails(emp_id) {
try {
let employee_info = await Storage.employeeInfo
.find({ employee_id: emp_id })
.populate('client')
.exec(function (err, block) {
if (err) console.log('%s', err);
else console.log('Employee details is %s', employee_info);
});
return employee_info;
} catch (err) {
res.status(400).send('Error getting details');
}
}

find all in mongoose

I have Schema With name score, and that have an iduser: req.session.user._id.
and I have more than one items with same iduser. I wanna to find all the items with the same iduser. and I use the
var users = await storescors16.find({ id }) id = res.session.user._id.
but that show me all the items in score !
My code
//schema
const storescor = new mongoose.Schema({
iduser: String,
level: { type: String, default: null },
day: { type: String, default: null },
date: { type: String, default: null },
time: { type: String, default: null },
objectif: { type: String, default: null }
})
var storescors16 = mongoose.model("storescor", storescor);
//post infos to database
router.post('/control/edite-control', (req, res) => {
console.log(req.session.selectuserid);
var { level, day, date, time, ob } = req.body
var scor = new storescors16({
iduser: req.session.selectuserid,
level: level,
day: day,
date: date,
time: time,
objectif: ob,
})
//read infos from databse
router.get('/result', auth, async(req, res) => {
var id = req.session.user._id
console.log(id);
var user = User.findById(id, (err, docs) => {
if (err)
console.log(err);
else
console.log();
})
var ids = req.session.user._id
var notes = await storescors16.find({ ids })
console.log(notes);
let scor = user.quiz
res.render('./options/result', {
notes: notes,
scor: scor,
title: 'سجل درجات النجم',
name: session.user.name,
email: session.user.email,
})
});
I use nodejs as backend
If you want to find all use findAll function with where.
const users = await storescors16.findAll({ id });
That if I understand you currect.

Node express find and return response multple models

I'm fairly new to node & express, I'm trying to implement a register application.
I have 2 models, both models have one common field 'empID'.
const RegisterEntriesSchema = mongoose.Schema({
empID: Number,
registerType: String,
registerItemsQuantity: Number,
registerItemsDesc: String
}, {
timestamps: true
});
const RegisterEmpSchema = mongoose.Schema({
empID: Number,
empName: String,
empPhone: String,
empProj:String
}, {
timestamps: true
});
For my get call in which I need to merge the values, I get from RegisterEmpSchema with its corresponding
employee details from RegisterEmpSchema.
exports.findAllRegisterEntries = (req, res) => {
registerEntriesModel.find()
.then(result => {
var updatedResponse=[];
console.log(result[0].empID);
for(var i=0;i<result.length;i++){
registerEmpModel.find({ empID: result[i].empID })
.then(result2 => {
**//unable to access result here**
}).catch(err => {
console.log("exception catch called findAllRegisterEntries, find employee details "+err);
});
}
res.send(updatedResponse);
}).catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while retrieving register."
});
});
};
I basically need to get register data and its corresponding employee data.
How do I modify my find() code to use the key empID and do a join query fetch?
I think you better use populate, add ref to empID inside RegisterEntriesSchema
const RegisterEmpSchema = new mongoose.Schema({
empID: Number,
empName: String,
empPhone: String,
empProj: String
}, {
timestamps: true
});
const registerEmpModel = mongoose.model('RegisterEmpSchema', RegisterEmpSchema, 'registerEmployeeCollection');
const RegisterEntriesSchema = new mongoose.Schema({
registerType: String,
registerItemsQuantity: Number,
registerItemsDesc: String,
empID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'RegisterEmpSchema'
}
}, {
timestamps: true
});
RegisterEntriesSchema.index({ createdAt: 1 }, { expires: '525601m' });
const registerEntriesModel = mongoose.model('RegisterEntriesSchema', RegisterEntriesSchema, 'registerEntriesCollection');
module.exports = {
registerEmpModel, registerEntriesModel,
}
then use populate() to populate the RegisterEntriesSchema with correspondence empID
RegisterEntriesSchema.
find().
populate('empID').
exec(function (err, data) {
if (err) return console.log(err);
res.send(data);
});
check mongoose docs: https://mongoosejs.com/docs/populate.html

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

Mongoose .pre('save') does not trigger

I have the following model for mongoose.model('quotes'):
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var quotesSchema = new Schema({
created: { type: String, default: moment().format() },
type: { type: Number, default: 0 },
number: { type: Number, required: true },
title: { type: String, required: true, trim: true},
background: { type: String, required: true },
points: { type: Number, default: 1 },
status: { type: Number, default: 0 },
owner: { type: String, default: "anon" }
});
var settingsSchema = new Schema({
nextQuoteNumber: { type: Number, default: 1 }
});
// Save Setting Model earlier to use it below
mongoose.model('settings', settingsSchema);
var Setting = mongoose.model('settings');
quotesSchema.pre('save', true, function(next) {
Setting.findByIdAndUpdate(currentSettingsId, { $inc: { nextQuoteNumber: 1 } }, function (err, settings) {
if (err) { console.log(err) };
this.number = settings.nextQuoteNumber - 1; // substract 1 because I need the 'current' sequence number, not the next
next();
});
});
mongoose.model('quotes', quotesSchema);
There is an additional Schema for mongoose.model('settings') to store an incrementing number for the incrementing unique index Quote.number im trying to establish. Before each save, quotesSchema.pre('save') is called to read, increase and pass the nextQuoteNumber as this.number to the respectively next() function.
However, this entire .pre('save') function does not seem to trigger when saving a Quote elsewhere. Mongoose aborts the save since number is required but not defined and no console.log() i write into the function ever outputs anything.
Use pre('validate') instead of pre('save') to set the value for the required field. Mongoose validates documents before saving, therefore your save middleware won't be called if there are validation errors. Switching the middleware from save to validate will make your function set the number field before it is validated.
quotesSchema.pre('validate', true, function(next) {
Setting.findByIdAndUpdate(currentSettingsId, { $inc: { nextQuoteNumber: 1 } }, function (err, settings) {
if (err) { console.log(err) };
this.number = settings.nextQuoteNumber - 1; // substract 1 because I need the 'current' sequence number, not the next
next();
});
});
For people who are redirected here by Google, make sure you are calling mongoose.model() AFTER methods and hooks declaration.
In some cases we can use
UserSchema.pre<User>(/^(updateOne|save|findOneAndUpdate)/, function (next) {
But i'm using "this", inside the function to get data, and not works with findOneAndUpdate trigger
I needed to use
async update (id: string, doc: Partial<UserProps>): Promise<User | null> {
const result = await this.userModel.findById(id)
Object.assign(result, doc)
await result?.save()
return result
}
Instead of
async update (id: string, doc: Partial<UserProps>): Promise<User | null> {
const result = await this.userModel.findByIdAndUpdate(id, doc, { new: true, useFindAndModify: false })
return result
}
The short solution is use findOne and save
const user = await User.findOne({ email: email });
user.password = "my new passord";
await user.save();
I ran into a situation where pre('validate') was not helping, hence I used pre('save'). I read that some of the operations are executed directly on the database and hence mongoose middleware will not be called. I changed my route endpoint which will trigger .pre('save'). I took Lodash to parse through the body and update only the field that is passed to the server.
router.post("/", async function(req, res, next){
try{
const body = req.body;
const doc = await MyModel.findById(body._id);
_.forEach(body, function(value, key) {
doc[key] = value;
});
doc.save().then( doc => {
res.status(200);
res.send(doc);
res.end();
});
}catch (err) {
res.status(500);
res.send({error: err.message});
res.end();
}
});

Resources