mongodb - $dateFromString to support multiple format - mongodb

I have a string field in mongodb which should be converted to a date field.
The format of the string is like the following:
2014 - Only year, default month and day are 01 and 01, so it should be converted to date '2014-01-01'
2014-01 - With year and month, which should also be converted to date '2014-01-01'
2014-01-01 - Full date
$dateFromString in the following syntax doesn't seem to work:
$dateFromString: {
dateString: '$order.date',
format: '%Y-%m-%d',
}
How can I make $dateFromString to support multiple format?

What you could do is to add a new field via $addFields and then for its value create few if conditions using the $cond pipeline operator matching each of your date lengths (via $strLenCP) and concatenating the remaining parts (via $concat). Then since all of your date fields will now match the format %Y-%m-%d it should work ... like this:
db.getCollection('<YourCol>').aggregate([{
$addFields: {
dateFixed: {
$cond: {
if: { $eq: [{ $strLenCP: "$date"}, 4] }, // <-- "2011"
then: { $concat: ["$date", "-01-01"] },
else: {
$cond: {
if: { $eq: [{ $strLenCP: "$date" }, 7] }, // <-- "2011-01"
then: { $concat: ["$date", "-01"] },
else: "$date" // <-- "2011-01-01"
}
}
}
}
}
},
{
$project: {
date: {
$dateFromString: {
dateString: 'dateFixed',
format: '%Y-%m-%d'
}
}
}
}
])
You can see it working here

Related

MongoDB - Dates between using $match

So I try to use MongoDB $match to get data between 2 dates, but it turns out that the data is not returning a.k.a empty here. What it looks like:
db.collection.aggregate([
{
$match: {
date: {
$gte: new Date("2022-10-23"),
$lt: new Date("2022-10-25"),
},
}
},
{
$group: {
_id: "$title",
title: {
$first: "$title"
},
answer: {
$push: {
username: "$username",
date: "$date",
formId: "$formId",
answer: "$answer"
}
}
}
},
])
Here is the data that I try to run on the Mongo playground:
https://mongoplayground.net/p/jKx_5kZnJNz
I think there is no error with my code anymore... but why it gives an empty return.
Migrate the comment to the answer post for the complete explanation.
Issue 1
The document contains the date field as a string type while you are trying to compare with Date which leads to incorrect output.
Ensure that you are comparing both values in the exact type.
Either that migrate the date value to Date type or
converting the date field to Date type in the query via $toDate.
{
$match: {
$expr: {
$and: [
{
$gte: [
{
$toDate: "$date"
},
new Date("2022-10-23")
]
},
{
$lt: [
{
$toDate: "$date"
},
new Date("2022-10-25")
]
}
]
}
}
}
Issue 2
Since you are using $lt ($lt: new Date("2022-10-25")), it won't include the documents with date: new Date("2022-10-25").
For inclusive end date, you shall use $lte.
Demo # Mongo Playground

MongoDB: Creating a calculated field measuring the difference between date fields

Context
This question uses the same collection/ document schema construct from my other question: MongoDB: Creating calculated fields using the switch function (column aliasing)
Data Schema
Note: cal_date1 is the calculated value shown in my other post linked above. When performing these steps they are done in a pipeline so that (I hope) the calculated columns can be thought of as part of the general document schema as shown below.
{
_id:ObjectId("619756f12c115f24df503c26"),
uniqueid:"12345678",
date1:"2021-11-02 20:04:50.253",
date2:"2021-11-03 18:10:57.520",
date3:"2021-11-08 07:08:00.000",
date4:"2021-11-08 14:40:00.000",
date5:"2021-11-08 08:34:00.000",
cal_date1: "2021-11-03 18:10:57.520"
}
Questions
How can I calculate the difference in date values between two particular dates (with some conditional logic behind it).
Extension
Is there a way of calculate the working days between two dates where "working" is defined as dates that are inclusive of Mon-Fri (no Sat, Sun)?
Tried
I have been playing with the $dateDiff operator inside a switch function as shown below however come across the error unknown operator: $cal_date1
db.collection.aggregate([
{
$project:
{
"uniqueid": 1,
"date1": 1,
"date2": 1,
"date3": 1,
"date4":1,
"date5": 1,
"cal_date1": {
$switch: {
branches: [
{ case: {$ne:["$date2",null]}, then: "$date2"},
{ case: {$ne:["$date3",null]}, then: "$date3"},
{ case: {$ne:["$date4",null]}, then: "$date4"},
{ case: {$ne:["$date5",null]}, then: "$date5"}
],
default: "blank"
}
},
"cal_date2": {
$switch: {
branches: [
{ case: {$ne:["$date4",null]}, then: "$date4"},
{ case: {$ne:["$date4",null]}, then: "$date5"}
],
default: "blank"
}
},
"cal_date3": {
$switch: {
branches: [
{ case: {$ne:["$date5",null]}, then: "$date5"}
],
default: "blank"
}
}
}
}
])
--updated code
"cal_days_between_date1_caldate1": {
$switch:
{
branches: [
{case: { $eq: ["$date1", null]}, then: "blank"},
{case: { $eq: ["$cal_date1", "blank"]}, then: "blank"}
],
default: {
$dateDiff: {
startDate: {
$dateFromString: {
dateString: "$date1"
}
},
endDate: {
$dateFromString: {
dateString: "$cal_date1"
}
},
unit: "day"
}
}
}
}
Update: 2021-11-24: T3:08pm UTC
Have altered the case expression slightly to try and get a boolean output and now have a new error of unknown operator: $dateDiff
Update: 2021-11-30: T8:36pm UTC
Have added in the code which contains the referenced fields (top of code chunk) along with the additions of the $dateFromString operator. The calculated date code is meant to act as the next step in the pipeline which I output along with the other fields.
The problem is that you are trying to use Date operators on fields that are strings.
You should transforms your strings to dates before with $dateFromString. Check this playground and docs.
Also there was some mistakes in the $eq arrays
db.collection.aggregate({
"$project": {
"cal_days_between_date1_caldate1": {
$switch: {
branches: [
{
case: {
$eq: [
"$date1",
null
]
},
then: "blank"
},
{
case: {
$eq: [
"$cal_date1",
"blank"
]
},
then: "blank"
}
],
default: {
$dateDiff: {
startDate: {
$dateFromString: {
dateString: "$date1"
}
},
endDate: {
$dateFromString: {
dateString: "$cal_date1"
}
},
unit: "day"
}
},
}
}
}
})

Find upcoming birthday from the array of date

I have array of dates childrendob like
[
"2000-01-21T15:18:49+05:30",
"2008-03-12T15:18:49+05:30"
]
I want to fetch data from users table in which the children birthday is after 7 days from today.
Like suppose today is 15 Jan and result should be all users for which childrendob is 21-1
I need mongodb query for this
Get month and day after 7 days from your client side,
let date = new Date();
date.setDate(date.getDate() + 7); // add 7 days in current date
let month = date.getMonth() + 1; // month
let day = date.getDate(); // date
$filter to iterate loop of dates array, check $and condition from month and day,
$toDate to convert string date to ISO date, it its already in ISO date then you can skip/remove this conversation
$month to get month from date
$dayOfMonth to get date form date
$match for dates should not be empty
db.collection.aggregate([
{
$project: {
dates: {
$filter: {
input: "$dates",
cond: {
$and: [
{ $eq: [{ $month: { $toDate: "$$this" } }, month] }, // input month variable
{ $eq: [{ $dayOfMonth: { $toDate: "$$this" } }, day] } // input day variable
]
}
}
}
}
},
{ $match: { dates: { $ne: [] } } }
])
Playground

Find avg difference in dates stored as strings

I have a Mongo database and I have stored dates as strings. Per document I have a field called "creationdate" and a field called "completiondate". The dates format is "YYYY-MM-dd" (ex "2011-12-18"). Even I can execute simple aggregation like greaterThan, greaterThanEqual, I cannot find the difference in dates, which I have to find to calculate the average days difference between completion and creation date.
The above query I have to write it on spring-boot with MongoTemplate if it is possible.
I am trying something like this but it doesn't work.
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("creationdate").gte(date1).lte(date2).andOperator(Criteria.where("completiondate").ne(""))),
Aggregation.project("servicerequesttype").and(DateOperators.DateFromString.fromStringOf("completiondate").withFormat("%Y-%m-%d")).minus(DateOperators.DateFromString.fromStringOf("creationdate").withFormat("%Y-%m-%d")).as("diff"),
Aggregation.group("servicerequesttype").avg("diff").as("average")
);
date1, date2 are given strings like "2011-01-01"
Is this what you are looking for?
db.collection.aggregate([
{
$project: {
creationdate: {
$dateFromString: {
dateString: "$creationdate",
format: "%Y-%m-%d"
}
},
completiondate: {
$dateFromString: {
dateString: "$completiondate",
format: "%Y-%m-%d"
}
}
}
},
{
$project: {
difference: {
$subtract: [
"$completiondate",
"$creationdate"
]
}
}
},
{
$group: {
_id: null,
average: {
$avg: "$difference"
}
}
},
{
$project: {
_id: 0,
dayAverage: {
$divide: [
"$average",
86400000
]
}
}
}
])
I have created interactive demo here: https://mongoplayground.net/p/wGRw12m3UbB
Hope it helps :)
Spring-Boot
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("creationdate").gte(date1).lte(date2).andOperator(Criteria.where("completiondate").ne(""))),
Aggregation.project("servicerequesttype").and(DateOperators.DateFromString.fromStringOf("creationdate").withFormat("%Y-%m-%d")).as("creationdate").and(DateOperators.DateFromString.fromStringOf("completiondate").withFormat("%Y-%m-%d")).as("completiondate"),
Aggregation.project("servicerequesttype").and("completiondate").minus("creationdate").as("difference"),
Aggregation.group("servicerequesttype").first("servicerequesttype").as("servicerequesttype").avg("difference").as("temp"),
Aggregation.project("servicerequesttype").and("temp").divide(86400000).as("average")
);

mongodb find between dates (month, year)

I have a collection with two fields, similar to the one bellow:
{
year: 2017,
month: 04 }
How can i select documents between 2017/07 - 2018/04?
Solved with:
db.collection.aggregate(
// Pipeline
[
// Stage 1
{
$addFields: {
"date": {
"$dateFromParts": {
"year": "$year",
"month": {"$toInt": "$month"}
}
}
}
},
// Stage 2
{
$match: {
"date": {
"$gte": ISODate("2017-07-01T00:00:00.000Z"),
"$lte": ISODate("2018-04-30T00:00:00.000Z")
}
}
},
]
);
Firstly you have to make sure you db is storing date in ISO format ( a format that mongo supports )
You can use following command to find documents :-
model.find({
date:{
$gte:ISODate("2017-04-29T00:00:00.000Z"),
$lte:ISODate("2017-07-29T00:00:00.000Z"),
}
})
Where model is the name of the collection and date is a attribute of
document holding dates in ISO format.