MongoDB conditional if else if with exists - mongodb

I am stuck in a query,
my case is
if user id exists then user id
else if user name exists then user name
else if user email exists then user email
else ''
Here, is my code.
$project: {
..... other fields,
'userDetail': {
$cond: {
if: {
$exists: ['$user.id']
},
then: '$user.id',
else: {
$cond: {
if: {
$exists: ['$user.name']
},
then: '$user.name',
else: {
$cond: {
if: {
$exists: ['$user.email']
},
then: '$user.email',
else: '',
}
},
}
}
}
}
}
Since, $exists don't work on this. Can anybody, help me finding the solution to this problem?
Also, if user.id is '' (empty string) & user.name is "Marcus". I want the result to return user.name.

Query
if id is false or empty string, try the name, else if name false or empty string, try the email, else ""
false are all not-exists/null/false
the types of those fields are not booleans, so you are safe to use this way
*in case you really needed the $exist for example you wanted to keep the null or the false values, you could use this {"$eq":[{"$type":"$user.id"}, "missing"]}
Test code here
aggregate(
[{"$set":
{"userInfo":
{"$switch":
{"branches":
[{"case":{"$and":["$user.id", {"$ne":["$user.id", ""]}]},
"then":"$user.id"},
{"case":{"$and":["$user.name", {"$ne":["$user.name", ""]}]},
"then":"$user.name"},
{"case":{"$and":["$user.email", {"$ne":["$user.email", ""]}]},
"then":"$user.email"}],
"default":""}}}}])

If I've understood correctly (and based on your example) you can use a query like this:
The trick here is to use ifNull instead of $exists.
db.collection.aggregate([
{
"$project": {
"userDetail": {
"$cond": [
{
"$ifNull": [
"$user.id",
false
]
},
// user id exists
"$user.id",
// user id no exists
{
"$cond": [
{
"$ifNull": [
"$user.name",
false
]
},
// user name exists
"$user.name",
// user name no exists
{
"$cond": [
{
"$ifNull": [
"$user.email",
false
]
},
// user email exists
"$user.email",
// user email no exists
""
]
}
]
}
]
}
}
}
])
Example here

You can use the nested $ifNull operator, by checking nested condition,
{
$project: {
userDetail: {
$ifNull: [
"$user.id",
{
$ifNull: [
"$user.name",
{ $ifNull: ["$user.email", ""] }
]
}
]
}
}
}
Playground

nested if else
tasks: {
$addToSet: {
$cond: {
if: {
$eq: [
{
$ifNull: ['$tasks.id', ''],
},
'',
],
},
then: '$$REMOVE',
else: {
id: '$tasks.id',
description: '$tasks.description',
assignee: {
$cond: {
if: {
$eq: [
{
$ifNull: ['$tasks.assignee._id', ''],
},
'',
],
},
then: undefined,
else: {
id: '$tasks.assignee._id',
name: '$tasks.assignee.name',
thumbnail: '$tasks.assignee.thumbnail',
status: '$tasks.assignee.status',
},
},
},
},
},
},
}

Related

Update element in array with mongoose

I have an array with multiple objects in it (there can be more than two). Now I need to update an element (change verified to true) inside the object (e.g. an object with method = "app"). If the object doesn't exist yet, it should be recreated. Is there any way to handle this with Mongoose?
I have found a solution for updating, but it does not solve the problem when no object exists
const result = await User.updateOne({email},
{ $set: { "multifactors.$[elem].verified" : true } },
{ arrayFilters: [ { "elem.method": "app" } ] }
)
This is one way of doing it, using a pipelined update.
a. Check if the array contains the required object using $filter and $size.
b. If it's there, updates the matching object $map.
c. Else, append the new object to the array.
db.collection.update({},
[
{
"$set": {
"multifactors": {
$cond: {
if: {
$gt: [
{
$size: {
$filter: {
input: "$multifactors",
as: "factor",
cond: {
$eq: [
"$$factor.method",
"app"
]
}
}
}
},
0
]
},
then: {
$map: {
input: "$multifactors",
as: "factor",
in: {
$cond: {
if: {
$eq: [
"$$factor.method",
"app"
]
},
then: {
$mergeObjects: [
"$$factor",
{
verified: true
}
]
},
else: "$$factor"
}
}
}
},
else: {
$concatArrays: [
"$multifactors",
[
{
method: "app",
verified: true
}
]
]
}
}
}
}
}
])
Playground link.

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

MongoDB Aggregation project field to be conditional value

I try to project my latest aggregation artifact, into a new one, with an additional field, whose value should be conditional.
My latest artifact of the aggregation pipeline is:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
__v: 0
}
]
So basically I want to project the additional artifact with an additional field whose name is bucketName.
I want to check in the variables array, whether there is an item, whose key is pre-known of value aKey. If so, the result of the projected bucketName would be the value field: aVal. If could not find, value would be NOT_FOUND.
So the result of the sample above would be:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
bucketName: 'aVal',
__v: 0
}
]
My try was:
{
$project: {
bucketName: {
$cond: {
if: {
$eq : ['$server.variables.key', 'aKey'],
},
then: '$server.variables.value',
else: 'NOT_FOUND',
},
},
},
},
But that just didn't do the query correctly.
You should use $in operator because this variables key is array:
Then, use also $filter within the then to extract that value.
db.collection.aggregate([
{
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": {
"$filter": {
"input": "$server.variables",
"as": "variable",
"cond": {
"$eq": [
"$$variable.key",
"aKey"
]
}
},
},
"else": "NOT_FOUND"
}
}
},
},
{
"$unwind": "$bucketName",
},
{
"$project": {
"bucketName": "$bucketName.value",
"server": 1
}
}
])
you can check $in condition because server.variables.key will return an array of string
$indexOfArray to get index of matching key element from array server.variables.key
$arrayElemAt to select value from server.variables.value by about returned index
db.collection.aggregate([
{
$project: {
bucketName: {
$cond: {
if: { $in: ["aKey", "$server.variables.key"] },
then: {
$arrayElemAt: [
"$server.variables.value",
{ $indexOfArray: ["$server.variables.key", "aKey"] }
]
},
else: "NOT_FOUND"
}
}
}
}
])
Playground
Your $server.variables.key is an array, is that why $eq fails, you are comparing an string with an array. You need to use $in. Check this example.
But also there is a problem, as $server.variables.key is an array with all keys values, the bucketName variable will be an array too.
You have to use the string aKey into then stage like this
db.collection.aggregate({
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": "aKey",
"else": "NOT_FOUND"
}
}
}
})

How to use $addFields in mongo to add elements to just existing documents?

I am trying to add a new field to an existing document by using a combination of both $ifnull and $cond but an empty document is always added at the end.
Configuration:
[
{
line: "car",
number: "1",
category: {
FERRARI: {
color: "blue"
},
LAMBORGHINI: {
color: "red"
}
}
},
{
line: "car",
number: "2",
category: {
FERRARI: {
color: "blue"
}
}
}
]
Query approach:
db.collection.aggregate([
{
$match: {
$and: [
{ line: "car" },
{ number: { $in: ["1", "2"] } }
]
}
},
{
"$addFields": {
"category.LAMBORGHINI.number": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
"$number",
"$$REMOVE"
]
}
}
},
{
$group: {
_id: null,
CATEGORIES: {
$addToSet: "$category.LAMBORGHINI"
}
}
}
])
Here is the link to the mongo play ground:
https://mongoplayground.net/p/RUnu5BNdnrR
I tried the mentioned query but I still get that ugly empty set added at the end.
$$REMOVE will remove last field/key, from your field category.LAMBORGHINI.number the last field is number that is why it is removing number from the end, you can try another approach,
specify just category.LAMBORGHINI, if condition match then it will return object of current category.LAMBORGHINI and number object after merging using $mergeObjects
{
"$addFields": {
"category.LAMBORGHINI": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
{
$mergeObjects: [
"$category.LAMBORGHINI",
{ number: "$number" }
]
},
"$$REMOVE"
]
}
}
}
Playground

How to use $cond in mongoDB without an else expression?

I want to add the values of $site to a variable if site!= 'undefined', else I have to skip that document and move on to the next.
I used
{$addToSet: { $cond: { if: { $ne: [ "$site", 'undefined' ] }, then: "$site"}}}
But it returns "Missing 'else' parameter to $cond"
and if I add an else statement
1) {$addToSet: { $cond: { if: { $ne: [ "$site", 'undefined' ] }, then: "$site", else: {} }}}
it returns the value to the addset {Object Object}
2) {$addToSet: { $cond: { if: { $ne: [ "$site", 'undefined' ] }, then: "$site", else: null }}}
it returns null to the set like ["sample1", "sample2", ]
3) {$addToSet: { $cond: { if: { $ne: [ "$site", 'undefined' ] }, then: "$site", else: "" }}}
it returns null to the set like ["sample1", "sample2", "" ]
I don't want anything to be added to the set if it does not satisfy the condition.
Starting in MongoDB 3.6, you can use the $$REMOVE variable as the ELSE argument of a $cond operator to prevent anything from being added to the set if the condition fails.
See here for an example from Mongo Docs.
In your case, we can get it done with,
{
$group: {
//_id: <group-by-expression>
// other fields (if any)..
sites: {
$addToSet: {
$cond: {
if: { $ne: ["$site", 'undefined'] },
then: "$site",
else: "$$REMOVE"
}
}
}
}
}
OR
{
$group: {
//_id: <group-by-expression>
// other fields (if any)..
sites: {
$addToSet: {
$cond: [
{ $ne: ["$site", 'undefined'] },
"$site",
"$$REMOVE"
]
}
}
}
}
You can add null on else brach(I used the simpliefied cond see here):
{
$group: {
_id: null,
A: {
$addToSet: {
$cond: [{ $ne: ["$site", 'undefined'] }, "$site", null]
}
}
}
}
and then:
{
"$project": {
"A": {
"$setDifferrence": ["$A", [null]]
},
}
}