MongoDB Node.js aggregation too slow for function - node.js

i hope this question is not answered too many times.
However my problem is that i need to extract objects from an array inside a document.
The way i have solved this is with the aggregation pipeline builder integrated in MongoDB, with this i get the correct result.
But when i implemented this in the API i am building the data does not return with the request. So i suspect the operation is too slow or something along those lines.
My code looks like this:
const searchIngredientInInventory = async (req: Request, res: Response) => {
const dbConn = await getDb();
const aggDoc = [
{
'$match': {
'type': {
'$ne': 'shoppingList'
}
}
}, {
'$unwind': {
'path': '$container'
}
}
]
dbConn.collection(coll).aggregate(aggDoc).toArray((err, result) => {
if (err) {
res.status(400).send(err);
console.log(err)
} else {
res.status(200).send(result);
}
})
}
And the database has this structure:
Cluster
Database
Collection
Doc1
Array with objects
Doc2
Array with objects
The collection is not large, its 5 documents and 36 kilobytes.
The return i get is empty array: []
Does anybody have any idea what i am doing wrong?

The problem was in the aggregation document, or at least the problem stopped occurring when I updated it.
The new aggregation document looks like this now:
const aggDoc = [
{
'$match': {
'type': {
'$ne': 'shoppingList'
}
}
}, {
'$unwind': {
'path': '$container'
}
}, {
'$project': {
'item': '$container.item',
'expirationDate': '$container.expirationDate'
}
}, {
'$match': {
'item': {
'$regex': ingredient
}
}
}
]

Related

mongoose query multiple operations in one request

I'm trying to use the $set, $addToSet and $inc at the same time for my report of sales and
tbh I'm not even sure if I did the right approach since it's not working.
once I send the request, the console gives me the error 404 but when I check the req.body the data was correct. so I was wondering if the problem is my query on mongoose because this was the first time I use multiple operations on mongoose query
export const report_of_sales = async (req, res) => {
const { id } = req.params;
console.log(req.body);
try {
if (!mongoose.Types.ObjectId.isValid(id)) return res.status(404).json({ message: 'Invalid ID' });
let i;
for (i = 0; i < req.body.sales_report.length; i++) {
await OwnerModels.findByIdAndUpdate(id, {
$inc: {
total_clients: req.body.total_clients,
total_product_sold: req.body.sales_report[i].qty,
sales_revenue: req.body.sales_report[i].amount
},
$set: {
"months.$[s].month_digit": req.body.months[i].month_digit,
"months.$[s].targetsales": req.body.months[i].targetsales,
"months.$[s].sales": req.body.months[i].sales,
},
$addToSet: {
sales_report: {
$each: [{
identifier: req.body.sales_report[i].identifier,
product_name: req.body.sales_report[i].product_name,
generic_name: req.body.sales_report[i].generic_name,
description: req.body.sales_report[i].description,
qty: req.body.sales_report[i].qty,
amount: req.body.sales_report[i].amount,
profit: req.body.sales_report[i].profit
}]
}
}
}, {
arrayFilters: [
{
"s.month_digit": req.body.months[i].month_digit
}
],
returnDocument: 'after',
safe: true,
}, { new: true, upsert: true })
}
} catch (error) {
res.status(404).json(error);
}
}
Well, you are looking at the body, but you are actually using query parameter named id. This is probably undefined, which leads to ObjectId.isValid(id) returning false.
You should decide on whether to pass this data as a query param or in the request body and adjust your code accordingly.

Collection field showing undefined when aggregate is used in MongoDB

I'm trying to concat two arrays with aggregate. However, output is showing Cannot read properties of undefined (reading 'allAddresses'). And wanted to implement in EJS. How can I achieve that? There is a code below:
app.post('/indexReplaced.html', (req, res) => {
Address.find({}, function(err, item){
const response = Address.aggregate([
{
$group: {
_id: null,
addresses: {
$push: "$addresses"
}
}
},
{
$project: {
allAddresses: {
$reduce: {
input: "$addresses",
initialValue: [],
in: {
"$concatArrays": [
"$$value",
"$$this"
]
}
}
}
}
}
]);
const allAddresses = response[0]["allAddresses"];
res.render("indexCalculated", {
address: item,
allAddresses: allAddresses
})
})
});
P.S: Tried to nest aggregate pipeline into Address.find() to render the collection as well. I hope it doesn't affect the pipelane.

check an array of string value with array of object in mongodb

I have array of strings like this
let fromHour = ['2.5','3','3.5']
let toHour = ['2.5','3','3.5']
I have an array of object saved in mongoDB
timeRange = [
{
from:'2.5',
to:'3'
},
{
from:'3',
to:'3.5'
}
]
I want to check if any of my array of string value exist in that object value
I have tried this but it give me this error ( Unrecognized expression '$match' )
checkAppoint = await Appointment.aggregate([
{
$project: {
date: myScheduleFinal[k].date,
status: { $in: ['pending', 'on-going'] },
timeRange: {
'$match': {
'from': { $in: fromHolder },
'to': { $in: toHolder },
},
},
},
},
]);
also I have tried this solution and it work for me but it take to much time so I am trying this with aggregate
checkAppoint = await Appointment.findOne({
date: myScheduleFinal[k].date,
status: { $in: ['pending', 'on-going'] },
timeRange:{$elemMatch:{
from:{$in:fromHolder},
to:{$in:toHolder}
}}
});
So anyone have a solution for that
Just try $elemMatch and $in operators,
using find() method
checkAppoint = await Appointment.find({
timeRange: {
$elemMatch: {
from: { $in: fromHour },
to: { $in: toHour }
}
}
})
Playground
using aggregate() method
checkAppoint = await Appointment.aggregate([
{
$match: {
timeRange: {
$elemMatch: {
from: { $in: fromHour },
to: { $in: toHour }
}
}
}
}
])
Playground
So I have found a way around to solve this problem and I will share the solution I used
First I want to minimize my request to mongodb so I am now making just one request that bring all the appointment with the required date
and I want to make it this way because my fromHour and toHour array will change many time through single request
helperArray => contains all the day I want to check it's range
let checkAppoint = await Appointment.find({
date: { $in: helperArray },
status: { $in: ['pending', 'on-going'] },
});
now inside my for loop I will go through that data
checkAppoint.filter((singleAppoint) => {
if (singleAppoint._doc.date === myScheduleFinal[k].date) {
singleAppoint._doc.timeRange.map((singleTime) => {
if (fromHolder.includes(singleTime.from)) {
busy = true;
}
});
}
});

How to get count by array of values using MongoDB and Node.js

I need to get sum of count of document using MongoDB and Node.js. I am explaining my code and document below.
var finalOut=[
{
"location": "NEW DELHI",
"nos_of_fos": 15,
"login_id": [
"9619300317",
"9619300343",
"9619300338",
"9619300351",
"9619300322",
"9619300316",
"9619300323",
"9619300328",
"9619300341",
"9619300309",
"9619300310",
"9619300329",
"9619300353",
"9619300356",
"NORTH#oditeksolutions.com"
],
},
{
"location": "North West Delhi",
"nos_of_fos": 6,
"login_id": [
"9619300355"
],
}
]
The above is my input. I am explaining my code below.
finalOut.forEach(function(listItem, index){
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
])
.toArray((cerr,cdocs)=>{
console.log(cdocs);
})
finalOut[index]['total_remarks']=cdocs;
})
Here I need the total count of all login_id arrays value which is present inside feedback document.
You can not use forEach for asynchronous operations:
Here I will use the async library but you can use any library that you like.
async.forEachOf(finalOut, (listItem, index, callback) => {
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
{
$count: ‘theCount’
}
])
.toArray((cerr,cdocs)=> {
finalOut[index]['total_remarks']=cdocs.theCount;
callback();
})
}, err => {
// here you can access the filled `finalOut` object
});

MongoDB: Set field value to 10 if less than 10, otherwise increment by one

Consider the following code:
findAndModify({id: id}, undefined, {$inc: {counter: 1}, {$max: {counter: 10})
This fails with an error because both $inc and $max try to update the same field.
But what if I want to set the counter to 10 if its value is less than 10 and if not, increment its value by 1? How do I do that in one atomic operation?
Is there a way to apply the updates sequentially in a single operation?
I don't think that can be done in a single operation. However, you can create an aggregate query with the conditional statements that you then use in your update.
(async () => {
try {
const results = await Model.aggregate([
{ '$match': { 'id': id } },
{ '$project': {
'counter': {
'$cond': [
{ '$lt': ['$counter', 10 ] },
10,
{ '$add': ['$counter', 1 ] }
]
}
} }
]).exec();
const data = results[0];
const { counter } = data;
const doc = await Model.findOneAndUpdate({ id },
{ '$set': { counter } },
{ new: true }
).exec();
console.log(doc);
}
catch (err) {
console.error(err);
}
})()
For an atomic update, include a query that restricts the increment for documents that only have counter less than the given ceiling value of 10:
findAndModify(
{
'id': id,
'counter': { '$lt': 10 }
}, undefined,
{ '$inc': { 'counter': 1 } },
callback
)

Resources