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

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
}
}
},

Related

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

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"
}
}
}
}
])

MongoDB: Add date comparison to arrayFilters

Objects of my collection have a field, that is an array of objects with one of the field being a string date
{
citizens: [{
name: 'John'
birthday: '1993/07/13'
},
{
name: 'Sarah'
birthday: '1996/07/13'
},
{
name: 'Natalia',
birthday: '2015/07/13'
}]
}
{
citizens: [{
name: 'Leo'
birthday: '1994/02/08'
},
{
name: 'Paul'
birthday: '1934/09/13'
},
{
name: 'Rego',
birthday: '2019/01/29'
}]
}
I want to set to all the users older than 18 status 'adult'
Here is what I try to do:
users.updateMany({}, {
$set: { 'citizens.$[elem].status': 'adult' },
},
{
arrayFilters: [
{ 'elem.status': { $exists: false } },
{ $lt: [{ $toDate: 'elem.$birthday' }, 18yearsaAgoDate] }, <-- 18years don't mean much here, I actually use $$NOW
],
multi: true,
});
But I get 'unknown top level operator: $lt' error when run this. How do I supposed to use $lt in arrayFilter?
Thanks in advance!
Here's how you could do it in a simple update using the aggregation pipelined updates:
db.collection.updateMany({},
[
{
$set: {
citizens: {
$map: {
input: "$citizens",
in: {
$mergeObjects: [
{
status: {
$cond: [
{
$gt: [
{
$dateDiff: {
startDate: {
$toDate: "$$this.birthday"
},
endDate: "$$NOW",
unit: "year"
}
},
18
]
},
"adult",
"$$REMOVE"
]
}
},
"$$this"
]
}
}
}
}
}
])
Mongo Playground
I've used some version 5+ operators like $dateDiff as it makes the code cleaner, but you could still achieve the same results without them using $subtract and a constant for 18 years, like so:
{
$lt: [
{
$toDate: "$$this.birthday"
},
{
$subtract: [
"$$NOW",
567648000000// 18 years in miliseconds
]
}
]
}
Mongo Playground
This is an update using the arrayFilters syntax.
db.collection.updateMany(
{ },
{
$set: { "citizens.$[elem].status": "adult" }
},
{
arrayFilters: [ { "elem.status": { $exists: false } , "elem.birthday": { $lt: "2004/07/27" } } ]
}
)
Note the date value "2004/07/27" is the day 18 years ago (very close approximate value). And using string values in date comparison requires that the value is formatted in "YYYY/mm/dd".
It would have worked like this if your date was already in the right format. Since you need to format it, I think you should use an aggregation pipeline with a $merge stage:
db.collection.aggregate([
{$set: {
citizens: {
$map: {
input: "$citizens",
in: {$mergeObjects: [
{status: {
$cond: [
{$lt: [{$toDate: "$$this.birthday"}, 18yearsaAgoDate]},
"adult",
"$$REMOVE"
]
}
},
"$$this"
]
}
}
}
}
},
{ $merge : { into : "collection" } }
])
See how it works on the playground example

Difference b/w using $and operator and not using it in aggregation pipeline

I am making use of aggregation in mongodb, I have a $match stage in aggregation pipeline which looks as follows.
{
'$match': {
'hiredOn': {
'$in': [
new Date(),
new Date()
] // some date array
},
'$or': [
{
'sentNotification': {
'$exists': false
}
},
{
'sentNotification': false
}
]
}
},
IS the above $match stage different than
{
'$match': {
$and: {
'hireDate': {
'$in': hireDateArr
},
'$or': [
{
'notificationSent': {
'$exists': false
}
},
{
'notificationSent': false
}
]
}
}
},
I want to get a all documents, whose hiredOn field is one of the following listed in date array and which may not have sentNotification field or sentNotification field set to false.
Would both of the above $match stages would give different documents ?.

how to check a date is less than today date in mongoose?

my collection in db is
plannedEndDate:{"2020-03-10T11:22:33.677+00:00"}
in controller var tdate= new Date(); [tdate is in the format 2020-03-10T14:28:22.687Z].
now,I need to check plannedEndDate is less than tdate in mongoose.
i tried,
plannedEndCmp: {
$cond: [{ $lt: ["$plannedEndDate", tdate] }, 1, 0]
}
but it is not returning true.
so should i need to trim the timestamp to compare dates in mangodb?or should i need to convert date into common format?
Not clear what you actually like to do but below command find the document or show indication:
db.collection.find(
{ plannedEndDate: { $lt: tdate } }
)
db.collection.aggregate([
{ $set: { plannedEndCmp: { $cond: [{ $lt: ["$plannedEndDate", tdate] }, 1, 0] } } },
{ $set: { plannedEndCmp_bool: { $lt: ["$plannedEndDate", tdate] } } }
])
In Aggregate group:
`
factualEndDate: {
"$dateToString": { "format": "%Y-%m-%d", "date": "$factualEndDate" }
},
In Aggregate project:
DelayedComplete: { $cond: [
{
$and: [
{ $cond: [{ $lte: [{ $max: "$data.factualEndDate" }, today] }, 1, 0] },
]
},
1,
0
]
},
`

MongoDB - match multiple fields same value

So I am trying to do something where I can group MongoDB fields for a check.
Given I have following data structure:
{
//Some other data fields
created: date,
lastLogin: date,
someSubObject: {
anotherDate: date,
evenAnotherDate: date
}
On these I want to do a check like this:
collection.aggregate([
{
$match: {
"created": {
$lt: lastWeekDate
},
"someSubObject.anotherDate": {
$lt: lastWeekDate
},
"lastLogin": {
$lt ...
is there a possibility to group the fields and do something like
$match: {
[field1, field2, field3]: {
$lt: lastWeekDate
}
}
You need $expr to use $map to generate an array of boolean values and then $allElementsTrue to apply AND condition
db.collection.find({
$expr: {
$allElementsTrue: {
$map: {
input: [ "$field1", "$field2", "$field3" ],
in: { $lt: [ "$$this", lastWeekDate ] }
}
}
}
})
EDIT: if you need that logic as a part of aggregation you can use $match which is an equivalent of find
db.collection.aggregate([
{
$match: {
$expr: {
$allElementsTrue: {
$map: {
input: [ "$field1", "$field2", "$field3" ],
in: { $lt: [ "$$this", lastWeekDate ] }
}
}
}
}
}
])