MongoDb multiply aggregate returns null sometimes - node.js

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

Related

How to request data from mongodb (mongoose) from subdocument within the interval "from" and "to"?

I need to return data that matches the range, and that's it. When requested, I will be returned an entire document that meets the conditions.
Do not know how to properly compose a query so that the database returns a ready data rage, without the need to filter after response on the server side.
Document structure:
{
symbol: "test",
data: [{
timestamp: 1,
value: 10,
},
{
timestamp: 2,
value: 20,
},
{
timestamp: 3,
value: 30,
},
{
timestamp: 4,
value: 40,
},
]
}
My request:
function request(symbol, from, to) {
return model.findOne({
symbol,
data: {
$elemMatch: {
timestamp: {
$gte: from,
$lt: to
}
}
}
}).then(res => res.data)
}
request('test', 2, 3)
Got response as full document structure. Using res.data.filter bad approach, because it has a big performance impact. Only the required data needs to be returned.
Example response as needed:
{
symbol: "test",
data: [
{
timestamp: 2,
value: 20,
},
{
timestamp: 3,
value: 30,
},
]
}
// or better
[
{
timestamp: 2,
value: 20,
},
{
timestamp: 3,
value: 30,
},
]
Thanks for the advice. Tried different variants from stackoverflow, nothing helped.
don't need the elementMatch function , you can query from subdocument as :
function request(symbol, from, to) {
return model.findOne({
symbol : symbol,
'data.timestamp' : { $gte: from, $lt: to }
}
}).then(res => res.data)
}

How do I query a set of objects with an array of values in mongoose?

I have a schema like this
const rankSchema = new Schema(
{
rank: { type: Object, default: {} },
lastUpdated: { type: Date, default: Date.now() },
},
{ minimize: false }
);
And my database has an object 'rank' with many other objects inside of it like this.
rank: {
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 },
Person3: { Stat1: 1, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 1 },
Person4: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 3 }
}
Now I have an array of strings that contains a few of these people
['Person1', 'Person2']
I want to be able to find all the person objects in that array and return their stats.
So essentially the final output after using the array of strings would be
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 }
I tried using $in and various different queries but nothing seems to work and I am stumped.
Thanks
You could use a combination of $objectToArray and $arrayToObject to filter your object by dynamic field names but if your parameters are known when you're building your query then it's easier to use regular .find() and apply projection:
db.collection.find({},{ "rank.Person1": 1, "rank.Person2": 1})
let input = ['Person1', 'Person2'];
let entries = input.map(p => ([`rank.${p}`, 1]))
let projection = Object.fromEntries(entries);
console.log(projection);
Mongo Playground

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)

Text Ellipsis in bubble chart

i'm using bubble chart from Highcharts, the label text inside of the bubbles is dynamic and sometimes can be bigger than the bubble itself,
I wonder if there's a way to make the text ellipsis according to the size of the bubble that contain it?
containerOptions = {
chart: {
type: 'bubble',
renderTo: $(container)[0],
events: {
drilldown: function (e) {
if (!e.seriesOptions) {
var chart = this,
drilldowns = {
'Animals': {
name: 'Animals',
data: [
{name: 'Dogs', y:2, x:10, z: 7, drilldown: true},
{name: 'Cats', y:4, x:12, z: 7}
]
},
'Dogs': {
name:"Dogs",
data: [
{name: 'Pitbull', y:3.7, x:7.6, z: 5, drilldown: false},
{name: 'German shepherd', y:6.7, x:6.9, z: 5, drilldown: false}
]
}
},
series = drilldowns[e.point.name];
chart.showLoading('Loading..');
setTimeout(function () {
chart.hideLoading();
chart.addSeriesAsDrilldown(e.point, series);
}, 1000);
}
}
}
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
style: { color: 'red' },
format: '{point.name}'
}
}
},
series: [{
name: 'Things',
colorByPoint: true,
data: [{
name: 'Animals',
y: 5,
x: 1,
z: 9,
drilldown: true
}, {
name: 'Fruits',
y: 2,
x: 9,
z: 9,
drilldown: false
}
]
}],
drilldown: {
series: [],
drillUpButton: {
relativeTo: 'spacingBox',
position: {
y: 0,
x: 0
}
}
}
}
}
You can loop through the data labels on load/redraw event and add/remove ellipsis according to the bubble's width and text's width.
function applyEllipsis() {
var series = this.series[0];
var options = series.options.dataLabels;
series.points.forEach(p => {
var r = p.marker.radius;
var label = p.dataLabel;
var text = label.text.textStr;
var bbox = label.getBBox(true);
while (bbox.width > 2 * r && text.length !== 1) {
text = text.slice(0, -1);
p.dataLabel.attr({
text: text + '\u2026'
});
bbox = label.getBBox(true);
}
p.dataLabel.align({
width: bbox.width,
height: bbox.height,
align: options.align,
verticalAlign: options.verticalAlign
}, null, p.dlBox);
});
}
Attach the function on load/redraw
Highcharts.chart('container', {
chart: {
type: 'bubble',
events: {
load: applyEllipsis,
redraw: applyEllipsis
}
},
example: http://jsfiddle.net/12d997o4/

How to group records by a child document field in mongoose?

Assuming we have following data
* Parent *
{ _id: 10, child: ObjectID(20) }
{ _id: 11, child: ObjectID(21) }
{ _id: 12, child: ObjectID(23) }
* Children *
{ _id: 20, name: 'test-1' }
{ _id: 21, name: 'test-2' }
{ _id: 22, name: 'test-3' }
{ _id: 23, name: 'test-1' }
I would like know what mongoose to use to get following result.
{ _id: 'test-1', count: 2}
{ _id: 'test-2', count: 1}
I am currently using following query, but the _id in the result is always { _id: null, count: 3 }
:(
Parent.aggregate([{
$group: {
_id: '$child.name',
count: { $sum: 1 }
}
}])
Any form of help is appreciated.
It's better way to create one single collections for avoiding two query on two different collections.
First you should find out all distinct child ids from parent collections and assign to it variable as below :
var allChildIds = db.parent.distinct("child")
After that used that child ids in children collection as below :
db.children.aggregate({"$match":{"_id":{"$in":allChildIds}}},{"$group":{"_id":"$name","sum":{"$sum":1}}})

Resources