Mongo $lt doesn't work as expected when comparing dates - mongodb

Problem
I have the following aggregation query that compares today's date with a fixed date and should set result to "pass" if today's date is less than the future date.
However the "$lt" case isn't fulfil and thus result is always set to "fail"
Document
[
{
"future_date": {
"$date": {
"$numberLong": "62135596800000"
}
}
}
]
Query
db.collection.aggregate([
{
"$addFields": {
"current_date": new Date(),
"future_date": new Date(62135596800000),
"result": {
$switch: {
branches: [
{
case: {
"$lt": [
new Date(),
"$future_date"
]
},
then: "pass"
}
],
default: "fail"
}
}
}
}
])
Result
[
{
"_id": ObjectId("5a934e000102030405000000"),
"current_date": ISODate("2022-08-17T13:21:36.552Z"),
"future_date": ISODate("3939-01-01T00:00:00Z"),
"result": "fail"
}
]
According to the Mongo docs, I have structured the "$lt" statement correctly, so I'm not quite sure what I'm missing.
Playground Example
https://mongoplayground.net/p/VECSNjQWESc

You can't add a field and use it in the same stage.
Corrected pipeline with two $addFields stages:
db.collection.aggregate([
{
"$addFields": {
"future_date": new Date(62135596800000), // override the future_date field in the document with this value
}
},
{
"$addFields": {
"current_date": new Date(),
"status": {
$switch: {
branches: [
{
case: {
"$lt": [
new Date(),
"$future_date"
]
},
then: "pass"
}
],
default: "fail"
}
}
}
}
])
The document you have shown in playground is not the same as the one in the question:
[
{
"future_date": {
"date": { // this is date not $date
"$numberLong": "62135596800000" // this is number, not date
}
}
}
]
For this, the aggregation would be:
db.collection.aggregate([
{
"$addFields": {
"current_date": new Date(),
"future_date": new Date(62135596800000),
"status": {
$switch: {
branches: [
{
case: {
"$lt": [
new Date(),
{
$toDate: "$future_date.date" // convert to date before comparison
}
]
},
then: "pass"
}
],
default: "fail"
}
}
}
}
])

Related

How to convert time string to ISO format in mongodb aggregation?

All document in my collection are same as this:
{
"_id": {
"$oid": "6396c58284bfad036f960288"
},
"title": "This is a nice title.",
"time": "3266 sec"
}
But I need to convert time field like this:
{
"_id": {
"$oid": "6396c58284bfad036f960288"
},
"title": "This is a nice title.",
"time": "PT3266S"
}
$set - Set the time field. With $regexFind to capture the matched group.
$set - Set the time field. With $concat to concat string with "PT", the first element of time.captures array and "S".
db.collection.aggregate([
{
$set: {
time: {
$regexFind: {
input: "$time",
regex: "(\\d+) sec"
}
}
}
},
{
$set: {
time: {
$concat: [
"PT",
{
$arrayElemAt: [
"$time.captures",
0
]
},
"S"
]
}
}
}
])
Demo # Mongo Playground
Or you can combine both $set stages into one:
db.collection.aggregate([
{
$set: {
time: {
$concat: [
"PT",
{
$arrayElemAt: [
{
$getField: {
field: "captures",
input: {
$regexFind: {
input: "$time",
regex: "(\\d+) sec"
}
}
}
},
0
]
},
"S"
]
}
}
}
])

Query using $dateFromString and $lt and $lge in mongodb

I am trying to return some records for date range from mongodb and I am trying to use the following query to query the collection test for the field StartDate:
db.test.aggregate([
{
$match: {
"StartDate": {
"$gte": [
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
],
"$lt": [
{
"$dateFromString": {
"dateString": "2021-04-01T00:00:00.0000000Z"
}
}
]
}
}
}
])
The above query didn't return anything but I am sure there are some data between the dates. Any ideas what have I missed? Thanks!
The $dateFromString is a aggregation pipeline operator, so it requires to match in $expr expression condition, and expression have different syntax of $gte and $lt,
db.test.aggregate([
{
$match: {
$expr: {
$and: [
{
$gte: [
"$StartDate",
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
]
},
{
$lt: [
"$StartDate",
{
"$dateFromString": {
"dateString": "2021-04-01T00:00:00.0000000Z"
}
}
]
}
]
}
}
}
])
Playground
Second option: You can use $toDate operator alternate of $dateFromString,
Playground
Update as per new requirement:
Field activities.dateOfActivity is an array, it requires to iterate through loop and check each date's conditions,
$map to iterate loop of activities.dateOfActivity array and put both condition inside, it will return true if both condition satisfy otherwise return false
$anyElementTrue will check return array of boolean have any true condition then return document
db.test.aggregate([
{
$match: {
$and: [
{
"$expr": {
$anyElementTrue: {
$map: {
input: "$activities.dateOfActivity",
in: {
$and: [
{
"$gte": [
"$$this",
{
"$dateFromString": {
"dateString": "2021-03-01T00:00:00.0000000Z"
}
}
]
},
{
"$lt": [
"$$this",
{
"$dateFromString": {
"dateString": "2021-04-20T00:00:00.0000000Z"
}
}
]
}
]
}
}
}
}
}
]
}
}
])
Playground

Mongodb groupby range of date and id

I have a database
{
"_id": "222jMQDEHuHXTuDeF",
"customer_id": "QfdAFubKS9ytdbhbq",
"createdDate": {
"$date": "2020-07-27T08:19:40.791Z"
}
},
{
"_id": "278jKLDEHuHXItDeF",
"customer_id": "HtdAFubJS8ytdnjbe",
"createdDate": {
"$date": "2020-07-26T08:19:40.791Z"
}
},
{
"_id": "128lRLDEHuHXItPhy",
"customer_id": "KodATubJS8yyqkjbe",
"createdDate": {
"$date": "2020-07-25T08:19:40.791Z"
}
}
I need to get data of the previous week where current date is the end date and group by date and customer_id and get the count of customer_id in mongodb.
You can compare like following. For the easiness I have added previouseWeekStart, but you can directly call it inside the $match stage instead of using $addFields. This should definitely work, but I can't show a demo in mongoplayground since I have added subtract symbol (-) for calculating previous date.
db.collection.aggregate([
{
$addFields: {
previouseWeekStart: new Date(new Date()-7*60*60*24*1000)
}
},
{
$match: {
$expr: {
$and: [
{
$gt: [
"$createdDate",
"$previouseWeekStart"
]
},
{
$lt: [
"$createdDate",
new Date()
]
}
]
}
}
},
{
$group: {
_id: {
cusId: "$customer_id",
date: "$createdDate"
},
count: {
$sum: 1
}
}
}
])

Mongo: how project a boolean if 'today' is between two date fields?

I've a collection where docs have this fields
"new_from" : ISODate("2019-07-08T00:00:00.000+0000"),
"new_to" : ISODate("2019-07-21T23:59:59.000+0000"),
I'd like to project a new field called new that simply should be true (or 1, or "1") if the today's date is between new_from and new_to
Is it possible to calculate today's date inside a projection?
You can use below $project stage
const todaysDate = moment().toDate()
{ "$project": {
"boolean": {
"$and": [
{ "$gte": ["$new_from", todaysDate] },
{ "$lte": ["$new_to", todaysDate] }
]
}
}}
You can use below $project with $cond
{
$project: {
new:
{
if: {
$and: [
{ $lt: ['$new_to', new Date()] },
{ $gt: ['$new_from', new Date()] },
}
]
},
then:true,
else:false
}
}
},

MongoDB aggregate array documents

I have a MongoDB collection containing elements like this:
{
"name": "test",
"instances": [
{
"year": 2015
},
{
"year": 2016
},
]
}
How can I get the minimum and maximum value for year within the document named test? E.g. I want to aggregate all documents inside that array, but I can't find the syntax for that. Thanks in advance!
Both $min and $max takes an array as a parameter and in your case path instances.year returns an array so your query can look like below:
db.col.aggregate([
{
$match: { name: "test" }
},
{
$project: {
minYear: { $min: "$instances.year" },
maxYear: { $max: "$instances.year" }
}
}
])
You can use below aggregation
db.collection.aggregate([
{ "$project": {
"maxYear": {
"$arrayElemAt": [
"$instances",
{
"$indexOfArray": [
"$instances.year",
{ "$max": "$instances.year" }
]
}
]
},
"minYear": {
"$arrayElemAt": [
"$instances",
{
"$indexOfArray": [
"$instances.year",
{ "$min": "$instances.year" }
]
}
]
}
}}
])