How to add new field conditionally in MongoDB? - mongodb

I have an aggregation pipeline in which i want to add new field based on certain condition. My pipeline is like this
[
{ // match stage
$or:[
{
$and: [
{placement: {'$nin': [-1,-2]}},
{contract_proposal_metadata : {$exists: true}}
]
},
{
risk_info_request_metadata: {$exists: true}
}
]
}
]
Now i want to add a new field record_type based on the condition that if contract_proposal_metadata exists so record type will be 'renewal' and if risk_info_request_metadata is exists then record_type will be request.
How can i achieve this?

You are not adding new field conditionally. You are always adding the field, just with different values.
There is $cond operator which returns 1 of 2 values depending on condition in the first argument.
You already know $exist for the $match stage, and the equivalent operator to use in aggregation expression is $type
[
{ // match stage
.....
},
{ // adding the field
$addFields: {
record_type: { $cond: {
if: { $eq: [ { $type: "$contract_proposal_metadata" }, "missing" ] },
then: "request",
else: "renewal"
} }
}
}
]

You need to use aggregate update
db.collection.update({
placement: { //Your match goes here
"$nin": [
-1,
-2
]
},
},
[
{
$set: {
status: {
$switch: {
branches: [
{ //update condition goes here
case: {
$ifNull: [
"$contract_proposal_metadata",
false
]
},
then: "renewal"
},
{
case: {
$ifNull: [
"$risk_info_request_metadata",
false
]
},
then: "request"
},
],
default: ""
}
}
}
}
],
{
multi: true
})
It supported from mongo 4.2+
$exists cannot be used, hence $ifnull used
playground

Related

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 ?.

MongoDB - Update nested document based on its current content

I have a simple collection with a document like this one:
{
_id: ObjectId('62b196e43581370007773393'),
name: 'John',
jobs: [
{
company: 'ACME',
type: 'programmer',
technologies: [
{
level: 1,
name: 'Java'
},
{
level: 3,
name: 'MongoDB'
}
]
}
]
}
I'd like to collect all technologies into a new field in the "job" sub-document for "programmers" jobs only, to achieve this effect:
(...)
jobs: [
{
it_technologies : ["Java", "MongoDB"]
(...)
}
]
(...)
The problem is that I do not know how to refer to and collect proper elements of the documents with this query:
db.runCommand({update: "employees",
updates: [
{
q: {},
u: { $set: { "jobs.$[j].it_technologies": "<how to collect all technologies in this job?>" } },
upsert: false,
multi: true,
arrayFilters: [{
$and:
[
{"j.type": "programmer"},
{"j.technologies": {$exists : true, $ne: []} }
]
}]
}
] });
Is it at all possible?
I'd be grateful for any clue!
Think that you need to work the update with the aggregation pipeline.
map - Iterate with the jobs array and return a new array.
1.1. $cond - Check the condition (Match the current document of jobs type and technology is not an empty array). If fulfilled, then update the document, else remains existing.
1.1.1. $mergeObjects - Merge current document with the document with it_technologies.
db.collection.update({},
[
{
$set: {
"jobs": {
$map: {
input: "$jobs",
in: {
$cond: {
if: {
$and: [
{
$eq: [
"$$this.type",
"programmer"
]
},
{
$ne: [
"$$this.technologies",
[]
]
}
]
},
then: {
$mergeObjects: [
"$$this",
{
it_technologies: "$$this.technologies.name"
}
]
},
else: "$$this"
}
}
}
}
}
}
],
{
upsert: false,
multi: true,
})
Sample Mongo Playground

Using $exists in MongoDB $cond

For background, I have to create a field in a collection, but ONLY if another known field (an array) is not empty. I'm trying to make a boolean filter with the $and and some conditions:
$set: {
clientsAllowed: {
$cond: {
if: {
$and: [
{
businessAllowed: { $exists: true },
},
{
businessAllowed: { $type: "array" },
},
{
businessAllowed: { $ne: [] },
},
],
},
then: [...],
else: [...],
},
}
}
But I get this error:
Invalid $set :: caused by :: Unrecognized expression '$exists'
Is the $exists wrongly formatted? Or is the $and stage? Any ideas?
Type cond for businessAllowed is enough. You don't need to check exists or not.
db.collection.aggregate([
{
$set: {
clientsAllowed: {
$cond: {
if: {
$and: [
{
"$eq": [
{
$type: "$businessAllowed"
},
"array"
]
},
{
$ne: [
"$businessAllowed",
[]
]
}
]
},
then: [],
else: []
}
}
}
}
])
mongoplayground

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