how to add 7 days in date manually using mongodb(aggregation)? - mongodb

I have a query in which I'm getting data using start Date and end Date. And it's working perfectly fine. now I want to add 7 days in my start date using aggregation and want to get data of start Date + 7 added days. i have tried the below code but couldn't get any success
{
$match: {
publishedAt: {
$lt: new Date(startDate),
$gte: new Date(endDate),
},
},
},
{
$set: {
publishedAt: {
$add: [new Date(startDate), 1000 * 60 * 60 * 24],
},
},
},

Use this:
{
$set: {
publishedAt: {
$dateAdd: {
startDate: new Date(startDate),
unit: "day",
amount: 7
}
}
}
}
Or use a 3rd party library, e.g. Luxon
{
$set: {
publishedAt: DateTime.now().plus({ days: 7 }).toJSDate()
}
}

Related

Express, Mongodb (mongoose) - Data comparison and count

In my mongodb database i have datas like:
{ id: 'ran1', code: 'ABC1', createdAt: 'Sep 1 2022', count: 5 }
{ id: 'ran2', code: 'ABC1', createdAt: 'Sep 2 2022', count: 3 }
{ id: 'ran3', code: 'ABC2', createdAt: 'Sep 1 2022', count: 2 }
{ id: 'ran4', code: 'ABC1', createdAt: 'Oct 1 2022', count: 1 }
{ id: 'ran5', code: 'ABC1', createdAt: 'Oct 2 2022', count: 2 }
{ id: 'ran6', code: 'ABC2', createdAt: 'Ocr 1 2022', count: 1 }
now as an output i want all the data from October but i also want to count and compare the percentage.
So the output for October will be
{code: 'ABC1', totalCount: the sum of total count of oct (1+2) =3 , percent: (total count of oct - total count of sep)/total count of oct * 100 }
{code: 'ABC2', totalCount: 1, percent: -100}
I tried to achieve these output using two different aggregation and later map the current month aggregation with each element from previous month aggregation. But i think there are some better solution.
Here is my code
const { filterDate, shop } = req.query;
const splittedFilter = filterDate.split("-");
const query = {
shopUrl: { $regex: shop, $options: "i" },
createdAt: {
$gte: new Date(splittedFilter[0]),
$lte: new Date(splittedFilter[1]),
},
};
const currentCodes = await BlockedCode.aggregate([
{
$match: query,
},
{
$group: {
_id: "$discountCode",
totalCount: { $sum: "$count" },
},
},
]);
const prevQuery = {
shopUrl: { $regex: shop, $options: "i" },
createdAt: {
$gte: new Date(splittedFilter[2]),
$lte: new Date(splittedFilter[3]),
},
};
const previousCodes = await BlockedCode.aggregate([
{
$match: prevQuery,
},
{
$group: {
_id: "$discountCode",
totalCount: { $sum: "$count" },
},
},
]);
const result = currentCodes.map((code) => {
const foundPrevCode = previousCodes.find((i) => i._id === code._id);
if (foundPrevCode?._id) {
const prevCount = foundPrevCode?.totalCount;
const currCount = code?.totalCount;
const difference = currCount - prevCount;
const percentage = (difference / currCount) * 100;
return { ...code, percentage };
} else {
return { ...code, percentage: 100 };
}
});
#shahamar Rahman i don't understand percentage logic can you explain little more!
upto this i filtered and counted the data based October month,plz check it and let me know if it's helps you
https://mongoplayground.net/p/Fet3UUI9LDC
db.collection.aggregate([
{
$addFields: {
month: {
$month: {
$toDate: "$createdAt"
}
}
}
},
{
$match: {
month: 10
}
},
{
$group: {
_id: {
code: "$code"
},
count: {
$sum: "$count"
}
}
}
])

How to find data by funcion in mongoose?

I have a collection in mongodb using mongoose packages like below
[
{
_id: new ObjectId("62ae97b6be08b688f93f2c07"),
reportId: '1',
method: 'A1',
category: 'B2',
date: '2022-06-19',
time: '22:55',
emergency: 'normal',
__v: 0
},
{
_id: new ObjectId("62ae97b6be08b688f93f2c08"),
reportId: '2',
method: 'A3',
category: 'B5',
date: '2022-06-18',
time: '23:05',
emergency: 'normal',
__v: 0
},
{
_id: new ObjectId("62ae97b6be08b688f93f2c09"),
reportId: '3',
method: 'A5',
category: 'B1',
date: '2022-06-19',
time: '23:55',
emergency: 'urgent',
__v: 0
}
]
and I want to filter this data, and here is my find function()
const options = [
{ method: { $in: ['A1','A2'] } },
{ emergency: { $in: data.emergency } },
{ category: { $in: data.category } }
];
const response = await Report.find({ $or: options,});
Until now, it works perfectly, but I still got one more filter: the date and time (They are all int type String).
I want to search for the range date and time between last night after 23 o'clock to 23 o'clock tonight.
But I have no idea how to write the query, please help me figure it out, thanks!!!
Here is my testing query:
date: {
$where: function () {
const yesterday = moment().subtract(1, 'days').format('YYYYMMDD') + '2300';
const date = moment(this.date).format('YYYYMMDD') + this.time.replace(':', '');
const today = moment().format('YYYYMMDD') + '2300';
return yesterday < date && date <= today;
},
},
I would recommend you to first save dates in a format that supports comparators (lt, gt, etc.) such as a Timestamp or even a plain number. That way you can use MongoDB filters like this:
let filter = {
date: { $gt: new Date(..), $lt: new Date(..) }
}
// This can be executed directly in the Mongo shell.
{
created_on: {
$gt: ISODate("1972-12-12"),
$lt: ISODate('2022-06-019')
}
}
But if you insist on using strings, then you would need to filter all your documents, and then parse each one to do your comparison.

MongoDB Aggregate Query Returning Empty Array

I'm new to Mongo and am trying to run an aggregate command on my model in my Node.js application (using Express). I'm trying to run the query for finding all users registered within the last month.
When I run User.find(), it returns all 2 users in the DB, so I know the users are definitely there.
However, when I run this, data is just an empty array. Is there a solution I'm missing here?
router.get("/stats", verifyTokenAndAdmin, async (req, res) => {
const date = new Date();
const lastYear = new Date(date.setFullYear(date.getFullYear() - 1));
try {
const data = await User.aggregate([
{ $match: { createdAt: { $gte: lastYear } } },
{
$project: {
month: { $month: "$createdAt" },
},
},
{
$group: {
_id: "$month",
total: { $sum: 1 },
},
},
]);
res.status(200).json(data)
} catch (err) {
res.status(500).json(err);
}
});
Also, the response is a 200 so no errors there.

How to delete data from more than 1 week ago in MongoDB?

I created a web-scraper to store data for a week to find a trend.
I wrote code to delete data from more than week ago every time the script runs.
However the data is still being stored for more than a week ago, is there a reason for this?"
example coin data createdAt field looks like
"createdAt": {
"$date": "2021-08-11T10:55:19.843Z"
},
coinSchema.statics.deleteOldData = async function () {
// delete old data
const today = new Date(Date.now());
today.setHours(0, 0, 0, 0);
const oneWeekAgo = new Date(Date.now());
const pastDate = oneWeekAgo.getDate() - 7;
oneWeekAgo.setDate(pastDate);
await this.deleteMany({
createdAt: {
$gte: today,
}, // 16 < 17 wont delete it prevent duplicates for one day
});
await this.deleteMany({
createdAt: {
$lt: pastDate,
}, // from 1 week ago
});
};
in the script i have the this run
async function main() {
await Coin.deleteOldData();
my coin model looks like :
const coinSchema = mongoose.Schema(
{
specNo: {
type: String,
required: true,
},
coinName: {
type: String,
required: true,
},
fullName: {
type: String,
required: false,
},
category: {
type: String,
},
array: [
{
GradeName: String,
PopulationCount: String,
trend: { type: Number, default: 0 },
},
],
},
{
timestamps: true,
}
);
Did you look at TTL based index? https://docs.mongodb.com/manual/core/index-ttl/
This is a good way to cleanup old data where DB itself takes care of it. In your case 7 days is 604800 seconds so if you create a index on createdAt with ttl 604800 then you should be all good!
db.collection.createIndex( { "createdAt ": 1 }, { expireAfterSeconds: 604800 } )
You can do it like this:
db.collection.deleteMany({
createdAt: { $lte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() },
})

How can I retrieve current week's data on Mongoose?

I have a Model for all requests that I get for my business (real estate).
I want to create an aggregation middleware based on the "createdAt" field which returns only models created between monday at midnight and the present time.
I have looked through all previous questions but could not find anything and the aggregation docs in mongo are so huge I cannot wrap my head around!
Do you have any tips?
I have tried this but it's returning an empty array:
getGestionaleStats = async (req, res) => {
try {
const richieste = await Richiesta.aggregate([
{
$match: {
createdAt: { $lt: new Date() }
}
},
{
$group: {
_id: { $week: '$createdAt' }
}
}
]);
res.status(200).json({
status: 'success',
data: { richieste }
});
} catch (err) {
console.error(err.message);
res.status(500).json({
status: 'error',
data: err.message
});
}
Of course the {$lt: new Date()} is probably where the problem lies.
Any suggestions?
This solution uses Aggregation query and a custom JavaScript function. The function takes a date object and returns the first Monday's date before today. This is used to get all the documents with createdAt date after the calculated date.
// Function returns the date of the "last Monday" from
// the given input date.
function getLastMonday(dt) {
let n = null; // last Monday conversion
switch (dt.getDay()) {
case 0: n = -5; break;
case 1: n = -6; break;
case 2: n = 0; break;
case 3: n = -1; break;
case 4: n = -2; break;
case 5: n = -3; break;
case 6: n = -4; break;
default: "This never happens";
}
let today_date = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
let last_monday_date = today_date.setDate(today_date.getDate() + n );
return last_monday_date;
}
var d = ISODate(); // -or- any date like ISODate("2019-11-26T00:00:00Z")
var LAST_MONDAY = getLastMonday(d);
db.test.aggregate( [
{
$addFields: {
last_monday: {
$dateFromParts : {
year: { $year: new Date(LAST_MONDAY) },
month: { $month: new Date(LAST_MONDAY) },
day: { $dayOfMonth: new Date(LAST_MONDAY) }
}
},
created_at: {
$dateFromParts : {
year: { $year: "$createdAt" },
month: { $month: "$createdAt" },
day: { $dayOfMonth: "$createdAt" }
}
}
}
},
{
$match: { $expr: { $gt: [ "$created_at", "$last_monday" ] } }
},
{
$project: { created_at: 0, last_monday: 0 }
}
] )
For a set of input documents like this:
{ _id : 1, createdAt : ISODate("2019-12-03T00:00:00Z") }
{ _id : 2, createdAt : ISODate("2019-11-12T02:00:00Z") }
{ _id : 3, createdAt : ISODate("2019-11-25T05:00:00Z") }
{ _id : 4, createdAt : ISODate("2019-11-26T00:00:00Z") }
{ _id : 9, createdAt : ISODate("2019-12-02T23:45:00Z") }
And, LAST_MONDAY = getLastMonday(ISODate("2019-12-04T05:40:20Z")), the aggregation query returns the document with _id : 1.
I'm using momentJS for this:
const result = Collection.aggregate([
{
$match: {
createdAt: {
$gte: moment().startOf('isoweek').toDate(),
$lt: moment().endOf('isoweek').toDate()
},
}
}
]);
I've found an answer with vanillaJS:
const richieste = await Richiesta.aggregate([
{
$match: {
createdAt: { $gte: getBeginningOfTheWeek(new Date()), $lt: new Date() }
}
},
{
$group: {
_id: null,
count: { $sum: 1 }
}
},
]}
where getBeginningOfTheWeek is as such:
exports.getBeginningOfTheWeek = (now) => {
const days = (now.getDay() + 7 - 1) % 7;
now.setDate(now.getDate() - days);
now.setHours(0, 0, 0, 0);
return now;
};
The latter function is from T.J. Crowder: get current week moday javascript