get the length of a query in mongoose - javascript

I need to get the length of a query and return the length and full query, like this:
{
"total": 73,
"votes": [
{ some fields.. },
{ some fields.. },
]
}
my solution is:
const votes = await VotesModel.find();
const total = votes.length;
return {total, votes}
but the question is, can I get the same result in a simple query?
or what would be the correct way to return the length and the query?

You need call .toArray() function.
const votes = await VotesModel.find().toArray();
const total = votes.length;
return {total, votes}

Related

Undefined when searching with Find Mongoose Express

I receive an array of products that I need to search for with Mongoose, so I use Find passing the _id of each product. But when going through the array and trying to search, I only receive the data of the first product, for the others I always receive undefined.
This is my code:
const calc = async (details) => {
let grandSubtotal = 0; }
console.log(details);
for (let i = 0; i < details.length; i++) {
let verifyProduct = await Product.find({ _id: details[i]._id});
console.log(verifyProduct[i].salePrice); //It only shows the first product, the others are undefined
.......
}
In my MongoDB database I have the products all saved with salePrice always, in this way:
{
"_id": "628fa841cde1d960c675ee24",
"barCode": "0000075053765",
"idProduct": "03",
"name": "MALBORO ARTESANAL 20",
"desc": "PAQUETE 20 CIGARROS",
"presentation": "PIECES",
"salePrice": 550,
"purchasePrice": 526,
"stock": 0,
"available": true,
"img": [],
"status": false
}
How can I obtain the salePrice information of all the products that I receive since for now I only receive information on the first one and the others are always undefined?
this because, you are using .find({})
let verifyProduct = await Product.find({ _id: details[i]._id});
You're querying using .find({ _id: details[i]._id}), you will always have result in the form of [{..onevalue at 0..}] because .find() returns result in an []
so, when you execute the loop for the first time, your i will be 0 and so when you access the verifyProduct[0].salePrice it will have value. But when your i become 1, your verifyProduct will still have result at 0 position only.
Fix:
const calc = async (details) => {
let grandSubtotal = 0;
console.log(details);
for (let i = 0; i < details.length; i++) {
let verifyProduct = await Product.findById(details[i]._id);
// no array, so access it directly
console.log(verifyProduct.salePrice);
}
}
since you are querying by _id, you can use .findById({}) instead of .find().
Database query in a for loop is super inefficient. You should use the $in MongoDB operator to select multiple documents at once.
Example
const arrayOfIds = ["631318a217f73aa43a58855d", "63132ba7525da531e171c964"];
Product.find({ _id: { $in: arrayOfIds }});

how to calculate the total dynamically and store the total number with variable

I have an object which contains the marks of a subject, I am trying to calculate the total of 1st_CQ and 1st_MCQ of the subjects dynamically, after 1st CQ and MCQ I want to calculate the 2nd part 2nd_MCQ & 2nd_CQ, sometimes there will no MCQ like the English subject.
const subNums = {
english_1st_CQ : 24,
english_2nd_CQ: 24,
math_1st_CQ: 40,
math_1st_MCQ: 10,
math_2nd_CQ:35,
math_2nd_MCQ:10,
// .....
}
so I have created an Object like this, the idea is to loop the subject and pass the first and second subject numbers into a function and the function will return the total.
const subject = [
{
sub: "english",
first: {
CQ1st: "english_1st_CQ",
MCQ1st: undefined,
},
second: {
CQ1st: "english_2nd_CQ",
MCQ1st: undefined,
},
{
sub: "math",
first: {
CQ1st: "math_1st_CQ",
MCQ1st: "math_1st_MCQ",
},
second: {
CQ1st: "math_2nd_CQ",
MCQ1st: "math_2nd_MCQ",
},
},
}]
I am stuck here, I am not able to store the total number inside a variable, [sub.sub] = this throws an error that number 32 is not iterable (cannot read property Symbol(Symbol.iterator))
I want to store the totals with their subject Name like this english: 48, math_1st: 50, math_2nd:45
const subTotal = subject.map((sub: any) => {
let obj;
[sub.sub] = calculateTotal(subNums[sub.first.CQ1st], rest[sub.first.MCQ1st]);
});
You don't need to create another object to map data. According to your object model, we can find a pattern {subject name}_{subject position}_{suffix} in keys.
With that, we can try to find matched combinations to generate results by reduce
const value = {
english_1st_CQ: 5,
english_2nd_CQ: 10,
math_1st_CQ: 15,
math_1st_MCQ: 20,
math_2nd_CQ: 25,
math_2nd_MCQ: 30,
};
const finalResult = Object.entries(value).reduce((result, [key, value]) => {
const [subjectName, subjectPosition, suffix] = key.split('_')
const fullSubjectName = `${subjectName}_${subjectPosition}`
if(!result[fullSubjectName]) {
result[fullSubjectName] = 0;
}
result[fullSubjectName] += value
return result
}, {})
console.log(finalResult)

mongodb - get first X results and overall count of aggregate function

I have a aggregate query I'm using to get the 10 first results of a lookup between 2 collections.
I'm only getting the first 10, because if I use no limit and get 50 results the query gets slow (4-5 secs) (any suggestions on that will also be great)
So because Im doing some kind of scan I need to let the client know the number of total results, so it can query more when needed. currently im running the cursor twice and im sure that is not ideal.
const grades = database.collection('grades');
const match = { userId };
const aggregationPipeline = [
{ $match: match},
{ $addFields: { userIdObj: { $toObjectId: '$userId' } } },
{
$lookup: {
from: 'users',
localField: 'userIdObj',
foreignField: '_id',
as: 'userDetails',
},
},
];
const aggCursor = grades.aggregate(aggregationPipeline);
const aggCursorCount = grades.aggregate([...aggregationPipeline, {
$count: 'count',
}]);
const count = await aggCursorCount.toArray();
const allValues = await aggCursor.limit(10).toArray();
res.json({grades: allValues, count: count[0].count});
Im sure there is a more efficient way to get what I need. Still learning all mongodb stuff.
Thanks!

How to change my object from being only one object with arrays inside, to an array of objects?

So I've got this part of a code where I'm creating response for my project. Now I've managed to create data, but I've got response that I need to changes.
First here is my code:
exports.getById = (req, res) => {
const id = req.params.a_id;
articleService
.getById(id)
.then((article) => {
bankService
.getRates()
.then((list) => {
let prr = article.price;
let price = parseFloat(prr.replace(/\.| ?€$/g, '').replace(',', '.'));
let mjeseci = req.body.months;
let ratanks = list.map((rata) =>
LoanJS.Loan(price, !mjeseci ? 60 : mjeseci, rata.NKS)
);
const kreditNKS = ratanks.map((index) => index.sum);
const rataNKS = ratanks.map(
(index) => index.installments[0].installment
);
let eks = list.map((stopa) => stopa.EKS);
let name = list.map((ime) => ime.bank.name);
let nks = list.map((stopa) => stopa.NKS);
let type = list.map((ime) => ime.interest_type.name);
res.status(200).json({
kredit: {
kreditNKS: kreditNKS,
rataNKS: rataNKS,
stopaEKS: eks,
stopaNKS: nks,
tip: type,
ime: name,
}
})
.catch((err) => {
res.status(500).send('Error 1 ->' + err);
});
})
.catch((err) => {
res.status(500).send('Error ->' + err);
});
};
Explain of what it does: So I'm fetching single article from my DB which has price inside it, then I'm getting data about loan also from DB. Now I'm using that data from DB, using .map function to get values one by one and calculating for that values my final loan(that is ratanks part). Now I'm also extracting some other data that I need present to the user on the frontend.
Now my problem: It's sending my res as an object with one object, who has key:value pairs and values are array of data inside it. But I want it to be an array with multiple objects.
My response in postman right now:
{
"kredit": {
"kreditNKS": [
118406.54,
118348.2,
119400.33,
118022.46,
118262.44,
118811.84
],
"rataNKS": [
19734.42,
19724.7,
19900.05,
19670.41,
19710.41,
19801.97
],
"stopaEKS": [
"6.24",
"5.65",
"8.26",
"3.13",
"4.03",
"5.68"
],
"stopaNKS": [
"4.11",
"3.94",
"7",
"2.99",
"3.69",
"5.29"
],
"tip": [
"Fiksna",
"Promjenjiva",
"Fiksna",
"Promjenjiva",
"Fiksna",
"Fiksna"
],
"ime": [
"ZiraatBank",
"ZiraatBank",
"UniCredit",
"Raiffeisen Bank",
"Raiffeisen Bank",
"ASA Banka"
]
}
}
Where I need it to be something like this:
[
{
"kreditNKS":118406.54,
"rataNKS": 19734.42,
"stopaEKS": "6.24",
"stopaNKS": "4.11",
"tip": "Fiksna",
"ime": "ZiraatBank"
},
{
"kreditNKS":118348.2,
"rataNKS": 19724.7,
"stopaEKS": "5.65",
"stopaNKS": "3.94",
"tip": "Promjenjiva",
"ime": "ZiraatBank"
},
{
"kreditNKS":119400.33,
"rataNKS": 19900,05,
"stopaEKS": "8.26",
"stopaNKS": "7",
"tip": "Fiksna",
"ime": "UniCredit"
}
etc.....
]
Is it possible to modify something like this?
Any tips are welcome!
Thanks!
it looks like you're mapping your list into 4 arrays and then putting them inside a single object, where each array is a property of the said object.
let eks = list.map((stopa) => stopa.EKS);
let name = list.map((ime) => ime.bank.name);
let nks = list.map((stopa) => stopa.NKS);
let type = list.map((ime) => ime.interest_type.name);
res.status(200).json({
kredit: {
kreditNKS: kreditNKS,
rataNKS: rataNKS,
stopaEKS: eks,
stopaNKS: nks,
tip: type,
ime: name,
}
})
The way someArray.map(eachThing => doSomethingWithThing(thing)) works is that you iterate the entire array "someArray" and execute a function for each thing inside of it.
This means that instead of doing LoanJS.Loan(price, !mjeseci ? 60 : mjeseci, rata.NKS) for all the items of the list and write that to a new array called "ratanks", you can write your own function for all the items of the list, and during the iteration of each item do something like const loan = LoanJS.Loan(price, !mjeseci ? 60 : mjeseci, eachItem.NKS).
This being said, you should be able to get the object you want by mapping your list into an array of objects like this
const mappedKredits = list.map((eachObject) => {
const computedLoan = LoanJS.Loan(price, !mjeseci ? 60 : mjeseci, eachObject.NKS);
const kreditNKS = computedLoan.sum;
const rataNKS = computedLoan.installments[0].installment;
return {
"kreditNKS": kreditNKS,
"rataNKS": rataNKS,
"stopaEKS": eachObject.EKS,
"stopaNKS": eachObject.NKS,
"tip": eachObject.interest_type.name,
"ime": eachObject.bank.name,
}
});
res.status(200).json(mappedKredits);
Btw, try to use more descriptive names for the variables, if they're in English it'll be even better, otherwise, it makes it a bit harder for folks like me to understand what the code is doing, thus making it harder to help you.

How do I merge two downloaded arrays into a single JSON array?

I'm pretty new to learning to code. So i might get a lot of basics wrong.
Basically i am downloading API content from two different accounts via request-promise and want to merge them into a bigger array. I'm struggling with escaping my local data from the request-promise function and also combining it with the second array
Here's what i got so far:
//request the site and do some stuff with the data
rp(rpOptions)
.then(function (parsedBody) {
let incomingData1 = (parsedBody); //turning data into a value to change it a little
incomingData1.forEach((incomingData1) => {incomingData1.yearsRetired = 0}); //to add a new property
incomingData1 = JSON.stringify(parsedBody, ["favFood", "age", "work", "yearsRetired"], 2); //to filter only relevant properties into a JSON thing (i eventually want to save it to a txt file)
});
i'd then do the same for the second account and then try to get that data outside of the function and merge it into a single array so that it looks like this:
{
"first_account_name": {
"individual1": {
"favFood": 'fries',
"age": 23,
"work": 'astronaut'
"yearsRetired": 0
},
"individual2": {
"favFood": 'banana',
"age": 55,
"work": 'zookeeper'
"yearsRetired": 0
{
...
}
},
"second_account_name": { ... }
"individual6": {
"favFood": 'apple',
"age": 49,
"work": 'dinosaur'
"yearsRetired": 0
"individual7": {
"favFood": 'sausage',
"age": 33,
"work": 'doctor'
"yearsRetired": 0
{
...
}
how do i get my data into a variable outside of rp? and how do i set it up so that it ends up like a nested array?
Thanks a lot and sorry for being confusing :P
What you are looking for is a global array that gets data pushed into it on every Promise request called right. So firstly, create a simple array and place it on top of the page or if you are using a class just insert it into the respective fields.
Let accountDetails = [];
Next, inside then function call this variable like so,
rp(rpOptions)
.then(function (parsedBody) {
let incomingData1 = (parsedBody);
incomingData1.forEach((incomingData1) => {incomingData1.yearsRetired = 0});
incomingData1 = JSON.stringify(parsedBody, ["favFood", "age", "work", "yearsRetired"], 2);
accountDetails.push({
"individual1" : incomingData1
})
});
If you're using ES6
const processData = (data) => {
return data.map((item) => ({
favFood: item.favFood,
age: item.age,
work: item.work,
yearsRetired: 0
}))
}
// any value returned by then will be wrapped in promise
// and can be `await` ed
// you can also use
// const [ data1, data2 ] = await Promise.all([
// rp(requestOption1).then(data => processData(data)),
// rp(requestOption2).then(data => processData(data))
// ])
// if you want it to be executed parallely
const data1 = await rp(requestOption1).then(data => processData(data));
const data2 = await rp(requestOption2).then(data => processData(data));
const mergedData = [
...data1,
...data2
];
If you don't have async await
const processData = (data) => {
return data.map((item) => ({
favFood: item.favFood,
age: item.age,
work: item.work,
yearsRetired: 0
}))
}
Promise.all(
rp(requestOption1).then(data => processData(data)),
rp(requestOption2).then(data => processData(data))
).then(results => {
const mergedData = results.reduce((collection, result) => {
return collection.concat(result);
}, []);
})
Note:
I wrote the function name processData because I don't know what is being processed. I suggest you to be more specific on the function name. (e.g. what it does)

Categories