Statistics with Mongo DB - mongodb

I have the following DB structure :
{
"uploadedAt": "2021-09-22T22:09:12.133Z",
"paidAt: "2021-09-30T22:09:12.133Z",
"amount": {
"currency": "EUR",
"expected": 70253,
"paid": 0
},
}
I would like to know how do I calculate the total amount that still need to be paid (expected - paid), and the average date between uploadedAt and paidAt. This for multiple records.
My function for getting the data is (the criteria should be updated to get this data).
const invoiceParams = new FindParams();
invoiceParams.criteria = { company: company._id }
const invoices = await this.findAll(invoiceParams);
FindAll function looks like:
async findAll(
params: FindParams,
ability?: Ability,
includeDeleted: boolean = false,
): Promise<Entity[]> {
let queryCriteria: Criteria = params.criteria;
let query: DocumentQuery<Entity[], Entity> = null;
if (!includeDeleted) {
queryCriteria = {
...queryCriteria,
deleted: { $ne: true },
};
}
try {
if (ability) {
ability.throwUnlessCan('read', this.entityModel.modelName);
queryCriteria = {
...toMongoQuery(ability, this.entityModel.modelName),
...queryCriteria,
};
}
query = this.entityModel.find(queryCriteria);
if (params.populate) {
query = query.populate(params.populate);
}
if (params.sort) {
query = query.sort(params.sort);
}
if (params.select) {
query = query.select(params.select);
}
return query.exec();
} catch (error) {
if (error instanceof ForbiddenError) {
throw new ForbiddenException(error.message);
}
throw error;
}
}
Update:
const paymentTime = await this.invoiceModel.aggregate([
{
$group: {
_id: "$account",
averageSpread: { $avg: { $subtract: ["$paidAt", "$uploadedAt"] } },
count: { $sum: 1 }
}
}
]);

Try this aggregation pipeline:
db.invoiceParams.aggregate([
{
$set: {
expectedPaid: { $subtract: ["$amount.expected", "$amount.paid"] },
averageDate: { $toDate: { $avg: [{ $toLong: "$uploadedAt" }, { $toLong: "$paidAt" }] } }
}
}
])

Related

mongoose - update many from an array of Ids

im trying to update all keys ('indexOrder') in an array of objects in a document.
The values for the update are recived as an array from the client:
[{_id:'1s284hd72hdd', indexOrder: 1}, {_id:'543543531', indexOrder: 2}, etc..]
im trying to match the _id of the array from the client with the _id of the objects in the document. When a match is found -> it needs to update the 'indexOrder' in the document to its value from the array from the client.
currently im doing it with looping on the client array, and updating for each iteration.
async updateIndexOrder(orderList) {
try {
orderList.forEach(async ({_id, indexOrder}) => {
await Model.findOneAndUpdate({_id}, {$set:{indexOrder}})
})
return true;
} catch (err) {
throw new Error(err);
}
}
How can I update in one call instead of so many server calls?
something like:
async updateIndexOrder(orderList) {
const idList= orderList.map(x => x._id)
try {
await Model.updatMany(
{_id: {$in:{idList}},
{$set: {indexOrder: orderList[FIND INDEX OF THE ITERATING _id].indexOrder}}
)
} catch (err) {
throw new Error(err);
}
}
Thank you.
you can do it in mongodb 4.2 and onwards
exports.updateDIndexOrder = async (keyValPairArr) => {
try {
let data = await Model.collection.update(
{ _id: { $in: keyValPairArr.map(o => o._id) } },
[{
$set: {
indexOrder: {
$let: {
vars: { obj: { $arrayElemAt: [{ $filter: { input: keyValPairArr, as: "kvpa", cond: { $eq: ["$$kvpa.id", "$_id"] } } }, 0] } },
in: "$$obj.indexOrder"
}
}
}
}],
{ runValidators: true, multi: true }
)
return data;
} catch (error) {
throw error;
}
}

MongoDB - convert double to string with aggregation

I am new to MongoDB and I am trying to convert double to string. I am not sure why my results are not as needed.
export function StoreSettings(req, res) {
var id = req.params.id;
var id = mongoose.Types.ObjectId(id);
Setting.aggregate([
{
$match: { restaurantID: id }
},
{
$addFields: {
"appTheme.appBanner": {
$concat: [
"/App/Carousel/",
{ $toString: "$appTheme.appBanner" },
".png"
]
}
}
}
])
.exec()
.then(data => {
return res.json(data);
})
.catch(err => res.json({ data: "Data Not Found", err }));
}
==OUTPUT==
{
"_id": "5e3379be06558d0c40d035ee",
"appTheme": {
"appBanner": "/App/Carousel/1.58078e+12.png"
}}
=== i NEED it to be like this: ====
{
"_id": "5e3379be06558d0c40d035ee",
"appTheme": {
"appBanner": "/App/Carousel/1580782209156.png"
}}
what am i doing wrong?
Thanks!
As $appTheme.appBanner :1580782209156 is a double in database, then using $toString would result in 1.58078e+12. You need to convert it into NumberLong() using $toLong & then convert it to string, Try below :
Setting.aggregate([
{
$match: { restaurantID: id }
},
{
$addFields: {
"appTheme.appBanner": {
$concat: [
"/App/Carousel/",
{ $toString: { $toLong: "$appTheme.appBanner" } },
".png"
]
}
}
}
])
Test : MongoDB-Playground

Can Update document with mongodb query but not work when do in mongoose [duplicate]

This question already has answers here:
Update nested subdocuments in MongoDB with arrayFilters
(2 answers)
Closed 3 years ago.
My collection is like this: https://mongoplayground.net/p/91InBXrUq7R
With this query I can update replies.likes
db.getCollection("posts").updateOne(
{
"_id": ObjectId("5da832caeb173112348e509b"), //posts._id
"comments.replies._id":ObjectId("5db6a88f7c6cfb0d0c2b689b"),//replies._id
},
{ "$push": { "comments.$[outer].replies.$[inner].likes": "10000012" } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5db06e11d0987d0aa2cd5593") },//comments._id
{ "inner._id": ObjectId("5db6a88f7c6cfb0d0c2b689b") }//replies._id
]
}
)
But when I code using mongoose, express, collection not update
//Like Reply toggle
router.post("/toggleLikeReply", function(req, res, next) {
var id_post = req.body.id_post;
var id_comment = req.body.id_comment;
var id_reply = req.body.id_reply;
var id_user = req.user._id;
console.log("id_post: "+id_post+" id_comment: "+id_comment+" id_reply: "+id_reply+" id_user: "+id_user);
//todo
Post.aggregate([
{ $match: {_id: ObjectId(id_post),"comments._id": ObjectId(id_comment)}},
{ $unwind: "$comments"},
{ $match: { "comments._id": ObjectId(id_comment)}},
{ $project: {"replies": "$comments.replies", _id: 0}},
{ $match: { "replies._id": ObjectId(id_reply)}},
{ $project: {"likes": "$replies.likes", _id: 0}},
]).exec((err, users_liked) => {
var index = users_liked[0].likes[0].indexOf(id_user);
console.log(users_liked[0].likes[0]);
//todo
if (index == -1) {
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});
}else{
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $pull: {"comments.$[outer].replies.$[inner].likes": ObjectId(req.user._id)} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("💥", error);
}
};
updatePost().then(function(data) {res.send({ like: false, success: true})});
}
})
});
I logged the all the id is come and the same as I did with mongo query directly .
id_post: 5da832caeb173112348e509b
id_comment: 5db06e11d0987d0aa2cd5593
id_reply: 5db6a88f7c6cfb0d0c2b689b
id_user: 5da85558886aee13e4e7f044
What is wrong with my code using mongoose and express?
Try This Query
var mongoose = require('mongoose');
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
const updatePost = async () => {
try {
await Post.updateOne({
_id: ObjectId(req.body.id_post),
"comments.replies._id": ObjectId(req.body.id_reply)},
{ $push: {"comments.$[outer].replies.$[inner].likes": req.user._id} },
{
"arrayFilters": [
{ "outer._id": ObjectId(req.body.id_comment) },
{ "inner._id": ObjectId(req.body.id_reply) }
]
}
);
} catch (error) {
console.log("error", error);
}
};
updatePost().then(function(data) {res.send({ like: true, success: true})});

Mongoose aggregate throws error when zero match

I'm trying to aggregate a field in mongodb based on some conditions. This works fine when there's at least single match, but it throws error otherwise. I want to the aggregated value as 0 when there's no match. What need to be modified in below code?
Note: fromDate and toDate passed through API Payload.
"errorMesssage": "Cannot read property 'amount' of undefined"
DTO:
export class MetricsDTO {
fromDate: Date;
toDate: Date;
}
Service
async getRevenue(metricClause: Partial<MetricsDTO>) {
if (Object.keys(metricClause).length == 0) {
var rev = await this.bookingsModel.aggregate([
{ $group: { _id: null, amount: { $sum: '$GrossAmount' } } }
])
} else {
var rev = await this.bookingsModel.aggregate([
{ $match: { TxnDate: { $gte: metricClause.fromDate, $lte: metricClause.toDate } } },
{ $group: { _id: null, amount: { $sum: '$GrossAmount' } } }
])
}
return rev[0].amount;
}
Payload
{
"fromDate": "2019-10-24",
"toDate": "2019-10-25"
}
It is not the aggreage causing the exception, this line causes the error, because you didn't check if the rev is null or not.
return rev[0].amount;
So to handle null, you can:
if (rev && rev[0]) {
return rev[0].amount;
} else {
return 0;
}

Sum object key by key with Mongo on Node

I'm trying to group all this object in one, the idea is to combine all the object.
My function know is like this:
app.get('/stats/:id(\\d+)/weapon/:weapon', function(req, res, next) {
db.collection('stats').aggregate( [
{ $match: { _id: parseInt(req.params.id, 10) } },
{ $unwind: "$session" },
{ $addFields: { weapon: { $objectToArray: '$session.weapons.' + sanitize(req.params.weapon) }, _id: false } },
{ $addFields: { weapon: { $arrayToObject: "$weapon" } } },
{ $project: { weapon: "$weapon", _id: false } }
], function(err, doc) {
if( !err ) {
res.json(doc);
}
else {
console.log(err);
res.end();
}
});
});
and return something like this:
[
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
},
{
"weapon":{
"shots":46,
"kills":4,
"hitbox":{
"head":3,
"chest":4,
"stomach":3,
"left_leg":2
},
"hs":3,
"dmg":479
}
},
{
"weapon":{
"shots":30,
"hitbox":{
"head":7,
"chest":4
},
"kills":4,
"dmg":590
}
}
]
My idea is to return only one instance of weapon with the sum key by key.
I already try $group and concat array but i can't get the result that i want...
I want like this:
[
{
"weapon":{
"shots":160,
"hitbox":{
"head":17,
"chest":12,
"stomach":3,
"left_leg":2
},
"kills":12,
"hs":3,
"dmg":1659
}
}
]