MongoDB optimized way to select document count change timeline - node.js

I want to select the progress of changing the number of documents for each day of the current week.
Users collection (very schematically): { _id, username (str), social (obj, default: null), createdAt (int timestamp) }
I need to select this data:
// Count of the new documents per each day. Example of the data I need:
{
all: { // Social field is null
timeline: [Monday: 1, Tuesday: 0, Wednesday: 4, Thursday: 26, Friday: 24, Saturday: 30, Sunday: 47]
},
social: { // Social field is not null
timeline: [Monday: 0, Tuesday: 0, Wednesday: 2, Thursday: 8, Friday: 5, Saturday: 16, Sunday: 9]
}
}
Better way will be if this was aggregator.
Here is my not optimized code:
let obj = { all: { timeline: [] }, social: { timeline: [] } }
const mondayStartTime = getMonday(new Date()).getTime();
const weekDaysStartTime = getWeekDaysStartTime(mondayStartTime);
const this_week_registered = await (
await users_collection.find({ createdAt: { $gte: mondayStartTime } })
).toArray();
for (let i = 0; i < weekDaysStartTime.length; i++) {
obj.all.timeline.push(
this_week_registered.filter((obj) =>
obj.createdAt >= weekDaysStartTime[i] && weekDaysStartTime[i + 1]
? obj.createdAt < weekDaysStartTime[i + 1]
: true,
).length,
);
obj.social.timeline.push(
this_week_registered.filter((obj) =>
obj.social && obj.createdAt >= weekDaysStartTime[i] && weekDaysStartTime[i + 1]
? obj.createdAt < weekDaysStartTime[i + 1]
: true,
).length,
);
}
I need to make this in one query.

Related

Convert an object into a key value pair in JSON

I've been trying to render my API response from Nodejs to display on my react table. I want to perform some calculations and filter out a few things on the response from the API response. But later while trying to display it on my tables, I'm having complications. I know its a pretty simple task but I'm unable to come up with the proper logic or approach for this. Thank you for helping out.
.then(response => {
console.log(response);
const result = response.data.rows;
setRes(result)
var arr = [];
Object.keys(result).forEach(function(key) {
arr.push(result[key]);
});
const filtered = {
Greeters:
{
nvCount: 0,
Taken: 0,
vCount: 0
},
Cleaning:{
nvCount: 0,
Taken: 0,
vCount: 0,
},
Intercession:{
nvCount: 0,
Taken: 0,
vCount: 0,
},
Media:{
nvCount: 0,
Taken: 0,
vCount: 0,
},
KidsChurch:{
nvCount: 0,
Taken: 0,
vCount: 0,
},
};
arr.map((el, i) => {
if (el.team.includes('Greeters')) {
if (el.preference.includes('NON-VEG')) {
filtered.Greeters.nvCount++;
}
else if (el.preference.includes('VEG')) {
filtered.Greeters.vCount++;
}
if (el.taken===true ) {
filtered.Greeters.Taken++;
}
}
else if (el.team.includes('Cleaning')) {
if (el.preference.includes('NON-VEG')) {
filtered.Cleaning.nvCount++;
}
else if (el.preference.includes('VEG')) {
filtered.Cleaning.vCount++;
}
if (el.taken===true ) {
filtered.Cleaning.Taken++;
}
}
else if (el.team.includes('Intercession')) {
if (el.preference.includes('NON-VEG')) {
filtered.Intercession.nvCount++;
}
else if (el.preference.includes('VEG')) {
filtered.Intercession.vCount++;
}
if (el.taken===true ) {
filtered.Intercession.Taken++;
}
}
else if (el.team.includes('Media')) {
if (el.preference.includes('NON-VEG')) {
filtered.Media.nvCount++;
}
else if (el.preference.includes('VEG')) {
filtered.Media.vCount++;
}
if (el.taken===true ) {
filtered.Media.Taken++;
}
}
else if (el.team.includes('Kids Church')) {
if (el.preference.includes('NON-VEG')) {
filtered.KidsChurch.nvCount++;
}
else if (el.preference.includes('VEG')) {
filtered.KidsChurch.vCount++;
}
if (el.taken===true ) {
filtered.KidsChurch.Taken++;
}
}
});
The response data from the API:
0
:
{id: '56', name: 'Prajwal V', phone: '990*******', preference: 'NON-VEG', team: 'Greeters', taken: false}
1
:
{id: '57', name: 'Amulya', phone: '63605******', preference: 'NON-VEG', team: 'Greeters', taken: true}
2
:
{id: '58', name: 'Devika', phone: '8618******', preference: 'NON-VEG', team: 'Greeters', taken: false}
3
:
{id: '59', name: 'Guru', phone: '9019*****', preference: 'NON-VEG', team: 'Greeters', taken: true}
4
:
{id: '60', name: 'Peter', phone: '9988*****', preference: 'VEG', team: 'Cleaning', taken: false}
I just want to find out the count("NON-VEG"), count("VEG"), and count(taken) for each team.
And I want to display these based on a SELECT dropdown that chooses the Team. eg: Select: 'Greeters', the count("NON-VEG"), count("VEG"), and count(taken) for 'Greeters' must be displayed in tabular format.
Current attempt,
<Select
className='SelectTeam'
closeMenuOnSelect={true}
components={animatedComponents}
isMulti={false}
options={teams}
name='team'
onChange={handleSelect}
/>
{res.map(el => {
return(
<div>
{team == el.team &&
<tr key={el.team}>
<td>{el.name}</td>
<td>{el.preference}</td>
</tr>
}
</div>
)
})
}
The data in the 'filtered' object has all the necessary data needed. In other words, I just need to display that filtered data based on the team selected or tell me a better approach to handle this complication
Thank you so much for helping out.
You can construct the filtered object dynamically like below code
let filtered = {};
teams.map(team=>{
filtered[team.name] = {
nvCount: 0,
Taken: 0,
vCount: 0,
};
});
result.map(user => {
if (user.preference.includes("NON-VEG")) {
filtered[user.team].nvCount += 1;
} else if (user.preference.includes('VEG')) {
filtered[user.team].vCount += 1;
}
if (user.taken === true) {
filtered[user.team].Taken += 1;
}
});
console.log(filtered);
setFilteredData(filtered);
Your filtered object would look like this,
{
"Greeters": {
"nvCount": 4,
"Taken": 2,
"vCount": 0
},
"Cleaning": {
"nvCount": 0,
"Taken": 0,
"vCount": 1
}
}
store it in filteredData state and display it
{selectedTeam && (
<>
<div>NV count: {filteredData[selectedTeam].nvCount}</div>
<div>Veg count: {filteredData[selectedTeam].vCount}</div>
<div>Taken: {filteredData[selectedTeam].Taken}</div>
</>
)}
selectedTeam is the selected data from your select box.

Node Ecommerce Cart NaN Issue

Working on calculating a total for my cart which it seems that I'm running into a number issue. I'm not really sure where I am going wrong with this function. Please disregard if I haven't calculated the totals correctly as I just coded it, but with the JSON I am passing numbers and not strings.
JSON
{
"id": "611afa8b9069c9126cff3357",
"discount": {
"title": "None",
"type": "None",
"percent": 0
},
"items": [
{
"sku": 1000,
"qty": 2,
"price": 10.99
},
{
"sku": 1001,
"qty": 2,
"price": 16.99
},
{
"sku": 1003,
"qty": 1,
"price": 15.99
}
]
}
const calculateTotal = (items, discount) => {
let total = 0;
let discountAmt = 0;
console.log(discount);
console.log(items);
console.log(items.length);
for (let i = 0; (j = items.length), i < j; i++) {
discountAmt = 0;
if (discount.type == "Item" && discount.itemNum == j.sku) {
discountAmt = j.price * (discount.percent / 100);
total = total - discountAmt;
} else {
total = total + j.price * j.qty;
}
}
if (discount.type == "Order") {
discountAmt = 0;
discountAmt = total * (discount.percent / 100);
total = total - discountAmt;
}
console.log(total);
return total;
};
Console Returning
{ title: 'None', type: 'None', percent: 0 }
[
{ sku: 1000, qty: 2, price: 10.99 },
{ sku: 1001, qty: 2, price: 16.99 },
{ sku: 1003, qty: 1, price: 15.99 }
]
3
NaN
NaN
If you want to loop through your items, then your loop is not correct.
You can do something like this:
for (let i = 0; i < items.length; i++) { // i as index
let j = items[i]; // j will have the object based on the index
discountAmt = 0;
if (discount.type == "Item" && discount.itemNum == j.sku) {
discountAmt = j.price * (discount.percent / 100);
total = total - discountAmt;
} else {
total = total + j.price * j.qty;
}
}
or much simpler:
for (let j of items) {
discountAmt = 0;
if (discount.type == "Item" && discount.itemNum == j.sku) {
discountAmt = j.price * (discount.percent / 100);
total = total - discountAmt;
} else {
total = total + j.price * j.qty;
}
}

MongoDb multiply aggregate returns null sometimes

I have this aggregate:
const dpi = (imgSize.height * imgSize.width) / (printDpi * printDpi);
let printSizes = await printSizeModel
.aggregate([
{
$project: {
id: 1,
width: 1,
height: 1,
price: 1,
shippingWidth: 1,
shippingHeight: 1,
shippingLength: 1,
shippingWeight: 1,
framePrice: 1,
hasFrame: 1,
total: { $multiply: ['$width', '$height'] },
},
},
{ $match: { total: { $lt: dpi } } },
])
.exec();
Width and height are both numbers and DPi is number as well (DPI is float and width and height are int)
I am using mongoos and Nodejs. This aggregate sometimes returns correct result and sometimes returns null. Based on my understanding this should be correct, but I might be missing something in here
After using Math.ceil to convert the number to Int the issue went away, so I can say that the issue was the float number:
const dpi = Math.ceil((imgSize.height * imgSize.width) / (printDpi * printDpi));
let printSizes = await printSizeModel
.aggregate([
{
$project: {
id: 1,
width: 1,
height: 1,
price: 1,
shippingWidth: 1,
shippingHeight: 1,
shippingLength: 1,
shippingWeight: 1,
framePrice: 1,
hasFrame: 1,
total: { $multiply: ['$width', '$height'] },
},
},
{ $match: { total: { $lt: dpi } } },
])
.exec();

Find sum of objects inside array using reduce

I am trying to find the total from objects inside an array, which each object has a price and quantity,
i can find the total when the array has exactly two objects, but for more than two it produces NaN.
arr = [ { quantity: 1, price: 30 },
{ quantity: 1, price: 40 },
{ quantity: 2, price: 10 },
{ quantity: 1, price: 10 } ]
const reducer = (accumulator, currentValue) => {
var a = accumulator.quantity * accumulator.price;
var b = currentValue.quantity * currentValue.price;
return a + b;
}
console.log(arr.reduce(reducer)); // sum if array contains 2 objects, NaN otherwise.
let arr = [
{ quantity: 1, price: 30 },
{ quantity: 1, price: 40 },
{ quantity: 2, price: 10 },
{ quantity: 1, price: 10 }
]
let reducer = (acc, cur) => {
return acc + (Number(cur.quantity) * Number(cur.price));
};
console.log(arr.reduce(reducer, 0));
// 100
Your reducer function seems to be wrong. Accumulator no longer has any parameters to it, since well, it accumulates - its an integer.
Also, set a initial value for your accumulator to start accumulating from, as shown in the reduce function, second parameter input
arr = [ { quantity: 1, price: 30 },
{ quantity: 1, price: 40 },
{ quantity: 2, price: 10 },
{ quantity: 1, price: 10 } ]
const reducer = (accumulator, currentValue) {
return accumulator + (currentValue.quantity * accumulator.price);
}
console.log(arr.reduce(reducer, 0 ));
you can simply say
arr = [ { quantity: 1, price: 30 },
{ quantity: 1, price: 40 },
{ quantity: 2, price: 10 },
{ quantity: 1, price: 10 } ]
const total = arr.reduce((total,item)=>{
total += item.quantity * item.price;
return total
},0)

Populate model of model on mongoose

I've been digging into this for a while now, found no answer valid and nothing like I want to do on Google so I've come here to ask this question.
How do you populate a Schema like this?
var category_schema = new mongoose.Schema({
name: {required: true, type: String},
parent: {type: Number, ref: 'Category' },
categories: [ {type: Number, ref: 'Category'} ]
});
What I'm doing right now is this:
Category.find({parent: null}).populate({path: 'categories', model: 'Category'}).exec(function(err, categories) {
But doing a console.log on categories would show on the console:
[ { __v: 2,
_id: 18,
name: 'Categoria',
parent: null,
categories:
[ { __v: 1,
_id: 19,
name: 'Children',
parent: 18,
categories: [Object] }, // <= THIS SHOULD HAVE ACTUAL CATEGORIES
{ _id: 20, name: 'Children 2', parent: 18, __v: 0, categories: [] } ] } ]
Any kind of help would be much appreciated.
Thanks.
Edit:
This is what I'm using to show em up
ul.list-group
if inputs == true
li.list-group-item
.radio
label
input(type='radio', class='radio', name='parent_id' value='0')
= 'No category'
mixin make_list(categories, edit)
each category in categories
li.list-group-item
.radio
if inputs == true
input(type='radio', name='parent_id', value='#{category._id}')
= category.name
if categories.length
each category in categories
li.list-group-item
.radio
label
if inputs == true
input(type='radio', name='parent_id', value='#{category._id}')
= category.name
if category.categories.length
ul
+make_list(category.categories, edit)
Since I had no answers, I started digging by myself on how to do this...
Now, this might not be the best way to do it, but oh well;
category_schema.static('make_tree', function(callback) {
Category.find(function(err, categories) {
var parents_array = [];
for(var i = 0; i < categories.length; i++) {
var category = categories[i];
if (category.parent == null || category.categories.length) {
categories[i].categories = [];
parents_array.push(categories[i]);
}
}
for(var x = parents_array.length -1; x >= 0; x--) {
for(var y = 0; y < categories.length; y++) {
if (categories[y].parent === parents_array[x]._id) {
console.log('adding ' + categories[y].name + ' to ' + parents_array[x].name);
parents_array[x].categories.push(categories[y]);
}
}
}
console.log(parents_array);
// Remove parents which have parents.
for(var i = parents_array.length - 1; i >= 0; i--) {
if (parents_array[i].parent) {
parents_array.splice(i, 1);
}
}
callback(parents_array);
});
});
Thing is..., I still get this when I do a console log:
PARENTS ARRAY NOW
[ { __v: 1,
_id: 23,
name: 'Categoria',
parent: null,
categories: [ { parent: 23, name: 'Hijo', _id: 24, __v: 2, categories: [Object] } ] } ]
I think you might be experiencing a vagary of the node console. What happens if you do console.log(categories[0].categories)? (assuming categories is the name of that fisrt object.

Resources