mongodb sum of price*quantity - node.js

Hey I'm trying to sum of some values but it return empty.
const resp = await Cart.find(
{ userId },
{
totalPrice: {
$sum: { $multiply: ["$cartItems.price" , "$cartItems.quantity"] },
},
}
);
I also tried this.
const resp = await Cart.aggregate([
{
$match: { userId },
},
{
$project: {
totalPrice: {
$sum: { $multiply: ["$cartItems.price", "$cartItems.quantity"] },
},
},
},
]);
When I write $multiply it is return nothing.
do you guys have any idea?
To get sum of values

Related

mongoose divide two fields in put request

Can I update a field of a document with a division of two fields? Using Node and MongoDB, I'm trying to create a rating function, and I have to make a division, but nothing seems to work. I want the new value of rating to be, the current one divided by the number of votes.
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
{
$inc: { noVotes: 1 },
$inc: { rating: movie_rating },
$divide: { rating: [rating, noVotes] },
// rating: { $divide: [rating, noVotes] }
},
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});
You need to change few things
Sample
db.collection.update({},
[
{
"$set": {
"key2": {
$add: [
"$key2",
1
]
},
key3: {
"$divide": [
{
$add: [
"$key2",
1
]
},
"$key"
]
},
}
}
],
{
"multi": true,
"upsert": false
})
You need aggregate update as you need divide
You cannot use the updated value in the same operation
You cannot combine $inc, $set in aggregate update
Alternatively, you can use $add instead $inc
you can reperform the operation for the divide operation than making another update call
This can be done with $set,
It will look like this:
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
[
{
$set: {
noVotes: { $sum: ["$noVotes", 1] },
rating: { $sum: ["$rating", movie_rating] },
averageRating: { $divide: ["$rating", "$noVotes"] },
},
},
],
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});

Aggregate query with lookup is works very slow | MongoDB

I have two collections. The first collection is distributors and the second collection is reports.
reports collection contains about 300K of data. I need to get the data for the specified time interval. But when using a lockup, the request slows down a lot. It takes an average of 7-8 seconds.
My query is like:
getFilterByDistributorTest = async ( req: Request, res: Response ): Promise<void> => {
DistributorModel.aggregate( [
{
$lookup: {
from: 'drugreports',
let: { "dataID": "$_id" },
pipeline: [
{
$match: { $expr: { $ne: ["$is_updated", null] } }
},
{ $match: { $expr: { $eq: ["$mode_40_distributor", "$$dataID"] } } },
{ $match: { mode_40_date: { $gte: new Date( "01-01-2020" ), $lte: new Date( "02-02-2020" ) } } },
{
$group:
{
_id: "$mode_40_distributor",
totalQuantity: { $sum: "$quantity" },
totalPricesUSD: { $sum: "$sum_price.usd" },
totalPricesUZS: { $sum: "$sum_price.uzs" },
totalPricesRUB: { $sum: "$sum_price.rub" },
totalPricesEUR: { $sum: "$sum_price.eur" },
}
},
{
$project: {
_id: 1,
usd: "$totalPricesUSD",
uzs: "$totalPricesUZS",
rub: "$totalPricesRUB",
eur: "$totalPricesEUR",
qty: "$totalQuantity",
}
}
],
as: "filterByTurnover_"
}
},
{ $unwind: { path: "$filterByTurnover_", preserveNullAndEmptyArrays: true } },
{ $limit: 25 },
] )
.exec( function ( err, result ) {
if ( err ) {
return res.status( 404 ).json( {
status: "Error",
code: 10004,
} );
}
return res.json( {
status: "Success",
data: result
} );
} );
};
The above query takes place in 7-8 seconds. It's too slow. What are the options to speed up the query?
If I do not use $lockup, the request starts in 0.006 seconds. If I use $lockup it lasts 7-8 seconds.
Thank you!

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too. For ex consider the below code -
const result = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$sort: { StudentData: -1 }
},
{
$count: 'count'
},
{
$skip: skip
},
{
$limit: limit
}
]
);
Here I want to save two things in the result variable - the number of documents and individually all the documents.
let [{ totalItems, result }] = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$facet: {
result: [
{
$sort: { BirthDate: -1 },
},
{
$skip: skip
},
{
$limit: limit
}
],
totalItems: [{ $count: 'count' }]
}
},
{
$addFields: {
totalItems: {
$arrayElemAt: ["$totalItems.count", 0]
},
}
}
]
);

$match stage in mongodb aggregation pipeline not working

i want to find profit of each account using mongodb and node.js i wrote this code but the main problem is i $match stage of aggregation pipeline i want to filter document according to accountNum and also rate but in my query filtering according to account number not work it return an empty array when i comment the account number and only filter by rate and grouping according to account number it work but i want to only find the profit of one person not all and filter data in $match state according to rate and accountNum both.
my code that return null value(filtering according to rate and accountNum in $match. i have some data with that account number):
const profit = await roznamcha.aggregate([
{
$match: {
rate: { $ne: 0 },
accountNum:{$eq:'$req.query.accountNum'}
}
},
{
$addFields: {
resultMultiply: {
$divide: [
{ $multiply: ["$credit", { $subtract: [100, "$rate"] }] }, 100
]
},
resultMultiplyde: {
$divide: [
{ $multiply: ["$debit", { $subtract: [100, "$rate"] }] }, 100
]
},
}
},
{
$group: {
_id: 0,
sumcredit: {
$sum: '$resultMultiply'
},
sumdebit: {
$sum: '$resultMultiplyde'
}
}
},
{
$addFields: {
finalProfit: { $subtract: ["$sumcredit", "$sumdebit"] }
}
}
])
res.status(201).json({
status: 'success',
data: {
profit
}
})
})
the code that work and return a value (filter according to rate only in $match stage)
const profit = await roznamcha.aggregate([
{
$match: {
rate: { $ne: 0 },
}
},
{
$addFields: {
resultMultiply: {
$divide: [
{ $multiply: ["$credit", { $subtract: [100, "$rate"] }] }, 100
]
},
resultMultiplyde: {
$divide: [
{ $multiply: ["$debit", { $subtract: [100, "$rate"] }] }, 100
]
},
}
},
{
$group: {
_id: '$accountNum',
sumcredit: {
$sum: '$resultMultiply'
},
sumdebit: {
$sum: '$resultMultiplyde'
}
}
},
{
$addFields: {
finalProfit: { $subtract: ["$sumcredit", "$sumdebit"] }
}
}
])
res.status(201).json({
status: 'success',
data: {
profit
}
})
})

MongoDB, Node and Express return all collections that don't exist in a separate collection that has a large amount of data

I've been pulling my hair out for weeks over this one.
I have a collection (this is a cut down version):
const SubscriberSchema = new Schema({
publication: { type: Schema.Types.ObjectId, ref: "publicationcollection" },
buyer: { type: Schema.Types.ObjectId, ref: "buyercollection" },
postCode: { type: String },
modifiedBy: { type: String },
modified: { type: Date }
});
I also have a collection containing the 1.75 million UK Postcodes
const PostcodeSchema = new Schema({
postcode: { type: String }
});
What I want to do is to return any record in the Subscriber collection which doesn't exist within the Postcode collection.
When I try a very simple aggregation using Mongoose on anything >100 records in the Subscriber collection, I'm getting either a timeout or a >16MB return error.
Here's what I've tried so far:
router.get(
"/badpostcodes/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const errors = {};
Subscriber.aggregate([
{
$match: {
publication: mongoose.Types.ObjectId(req.params.id),
postCode: { "$ne": null, $exists: true }
}
},
{
$lookup: {
'from': 'postcodescollections',
'localField': 'postCode',
'foreignField': 'postcode',
'as': 'founditem'
}
},
// {
// $unwind: '$founditem'
// },
{
$match: {
'founditem': { $eq: [] }
}
}
], function (err, result) {
if (err) {
console.log(err);
} else {
if (result.length > 0) {
res.json(result);
} else {
res.json("0");
}
}
})
}
);
The unwind didn't seem to do anything but it's commented out to show I tried to use it.
I've also tried using a pipeline on the lookup instead but that didn't work, similar to the following (sorry, I don't have my original code attempt so this is from memory only):
$lookup: {
'from': 'postcodescollections',
'let': { 'postcode': "$postCode" },
'pipeline': [
{
'$match': {
'postcode': { $exists: false }
}
},
{
'$unwind': "$postCode"
}
],
'as': 'founditem'
}
Thanks in advance so I can hopefully retain some hair!
You are doing a match on all postcodes that don't match and then unwinding those - that will be a 1.75m documents for each subscriber! The syntax in $lookup is also incorrect I think.
I think you can try something like the following - adjust accordingly for your data:
Do a $lookup to find a matching postcode in postcodes, then do a match to filter those subscribers that that don't have any founditem elements: "founditem.0": {$exists: false}
See an example:
db.getCollection("subscribers").aggregate(
[
// Stage 1
{
$match: {
postCode: { "$ne": null, $exists: true }
}
},
// Stage 2
{
$project: {
_id: 1,
postCode: 1
}
},
// Stage 3
{
$lookup: {
from: "postcodescollections",
let: { p: "$postCode" },
pipeline: [
{
$match: {
$expr:
{
$eq: ["$$p","$postcode"] }
}
},
{ $project: { _id: 1 } }
],
as: "founditem"
}
},
// Stage 4
{
$match: {
"founditem.0": {$exists: false}
}
},
]
);

Resources