mongodb $or always return false (const) - mongodb

I make an aggregation pipeline to retrive documents and add a new field "expired" (boolean), its value is based on the field "expireAt" (Date)
the expired field considerd true if:
expiredAt is missing or null or empty
expiredAt < now
this is my trial:
{project:
expired: {
$not: {
$or: [{ expireAt: null }, { $gte: ['$expireAt', new Date()] }]
}
}
}
this code always give me false even if the document has an expireAt value whitch is < now
by performing explain:
{ '$project': { _id: true, expireAt: true, expired: { '$cons
t': false } } }
query result:
note the expireAt field and the corresponding expired value
today is: 27/11/2018
{ expired: false },
{ expireAt: 2019-07-15T17:18:11.000Z, expired: false },
{ expireAt: 2017-07-16T17:18:11.000Z, expired: false },
also {expireAt:{$exists:false}} give error:
MongoError: Unrecognized expression '$exists'at queryCallback
any idea to check the existence of the field (weather its value is null or not)

Use $cond operator to achieve
Below query help you :
I have used date in my field.
db.getCollection('chat').aggregate([
{
$project: {
'date': 1,
"expired": {
$cond: {
if: { $gte: ['$date', new Date()] },
then: false,
else: true
}
}
}
}
])
With $or
db.getCollection('chat').aggregate(
[
{
$project:
{
date: 1,
expired: { $or: [ { $lt: [ "$date", new Date() ] }, { $eq: [ "$date", null ] } ] }
}
}
]
)

the following code worked with me:
expired:{
$and: ['$expireAt', { $lt: ['$expireAt', new Date()] }]
}

Related

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

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

$or query in mongodb

I need find field create_date, but if create_date is not defined, then find field create_time:
Sources_Timings.find({
create_date: {
$or: [
{
$gte: req.body.date.starting,
$lte: req.body.date.ending
},
{
$exists: false
}
]
},
create_time: {
$or: [
{
$gte: 0,
$lt: 86400000
},
{
$exists: false
}
]
}
},
function(err, timings) {
...
})
My code don't working.
$or is a top level operator and perform operation on an array of two or more expressions and selects the documents that satisfy at least one of the expressions.
So you query should be some thing like this
Sources_Timings.find({
"$or": [
{
"create_date": {
"$gte": req.body.date.starting,
"$lte": req.body.date.ending
}
},
{
"create_time": {
"$gte": 0,
"$lt": 86400000
}
}
]
})

Mongo project test if value is in array

In a project step of a mongo aggregation, I'd like to create a boolean field like:
{
$project: {
isInArray: { $cond: [
{ $in: ['$_id', ids] },
{ $const: true },
{ $const: false },
] },
},
},
but this is failing with
invalid operator $in
I could not find documentation on the correct syntax
You can use $setIsSubset operator
db.people.aggregate([
{ $project: {
isInArray: { $cond: [ {$setIsSubset: [['$_id'], ids]}, true, false ] }
}}
])

MongoDB update with condition

I'm trying to update some field in my collection depending on a condition.
I want to set field active to true if the condition is true and to false otherwise
This is update without condition
db.consent.update(
{}, //match all
{
$set: {
"active": true
}
},
{
multi: true,
}
)
I would like to add a condition to update like this:
db.consent.update(
{},
$cond: {
if: {
$eq: ["_id", ObjectId("5714ce0a4514ef3ef68677fd")]
},
then: {
$set: {
"active": true
}
},
else: {
$set: {
"active": false
}
}
},
{
multi: true,
}
)
According to https://docs.mongodb.org/manual/reference/operator/update-field/ there is no $cond operator for update.
What are my options here to execute this update as a single command?
Starting Mongo 4.2, db.collection.update() can accept an aggregation pipeline, finally allowing the update/creation of a field based on another field:
// { a: "Hello", b: "World" }
// { a: "Olleh", b: "Dlrow" }
db.collection.updateMany(
{},
[ { $set: { active: { $eq: [ "$a", "Hello" ] } } } ]
)
// { a: "Hello", b: "World", active: true }
// { a: "Olleh", b: "Dlrow", active: false }
The first part {} is the match query, filtering which documents to update (in our case all documents).
The second part [ { $set: { active: { $eq: [ "$a", "Hello" ] } } } ] is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline). $set is a new aggregation operator and an alias of $addFields. Then any aggregation operator can be used within the $set stage; in our case a conditional equality check on which depends the value to use for the new active field.
We can do it using aggregation pipeline. Here i am updating male to female and female to male.
db.customer.updateMany(
{ },
[
{ $set: { gender: { $switch: {
branches: [
{ case: { $eq: [ "$gender", 'male' ] }, then: "female" },
{ case: { $eq: [ "$gender", 'female' ] }, then: "male" }
],
default: ""
} } } }
]
)
You can't.
Mongo doesn't support combining fields, conditionals etc. in the update statement.
See https://stackoverflow.com/a/56551655/442351 below.
You can update MongoDB document conditionally using findAndModify() or findOneAndUpdate() if you have MongoDB version 3.2+
Of course you can....
by running 2 queries
db.collection.update({condition}, { $set: { state } }, { multi: true });
db.collection.update({!condition}, { $set: { state } }, { multi: false });
for your example case
db.consent.update(
{"_id": ObjectId("5714ce0a4514ef3ef68677fd")},
{ $set: { "active": true } });
db.consent.update(
{"_id": {$ne: ObjectId("5714ce0a4514ef3ef68677fd")}},
{ $set: { "active": false } },
{ multi: true });
db.consent.update(
{
//YOUR CONDITIONAL HERE TO APLLAY UPDATE ONLY IF CONDITIONAL HAPPEN, SO THE "active": true WILL BE APPLY.
},
{
$set: {
"active": true,
"active2": FALSE,
}
},
{
multi: true,
}
)