Reputation: 135
I have a MongoDB aggregation query in which I have the following:
{ $match: { version: versionNumber }
The 'versionNumber' is an optional input parameter to the aggreagation. If this versionNumber is not provided, then I do not want this match to be performed.
Currently, if the versionNumber is not supplied, the match still happens and I get a blank query output.
Is there a way in Mongo to do this? Thanks!
Upvotes: 4
Views: 2988
Reputation: 2141
Mongodb playground link:
https://mongoplayground.net/p/ZyGO63f2885
We can use $let-vars
also but for I have used $addFields
instead.
db.collection.aggregate([
{
"$addFields": {
"versionNumber": null
}
},
{
$match: {
$expr: {
$cond: [
{
$ne: [
{
$ifNull: [
"$versionNumber",
null
]
},
null
]
},
{
$eq: [
"$version",
"$versionNumber"
]
},
true
]
}
}
},
{
"$project": {
"versionNumber": 0
}
}
])
Usecases :
$ifNull operator:
The $ifNull operator returns the first argument if it is not null or undefined, otherwise, it returns the second argument.
$addFields stage:
This stage is used to add new versionNumber fields to the document to check whether to apply $match condition or not.
$expr operator:
The $expr operator allows the use of aggregation expressions with $match operator.
$cond operator:
It is used check for if-else conditions.
$ne operator:
The $ne operator checks if two values are not equal.
In this case, we are checking if the result of the $ifNull operator is not equal to null.
$project
: To show/hide variables in the stage.
Upvotes: 0
Reputation: 73
You can provide optional parameter to $match in aggregation like this:
$match:{
"$and": [
versionNumber ? { "version": parseInt(versionNumber) } : {}
]
}
This will match versionNumber only if is defined.
Upvotes: 0
Reputation: 15266
Randomly stumbled upon this post and realized it is a common use case of feeding multiple, optional input values. Here is a generalized template to contruct the $match
query:
{
"$or": [
{
"$eq": [
{
"$ifNull": [
<your version number input here>,
null
]
},
null
]
},
{
"$eq": [
<your version number input here>,
"$version" // the field you want to match upon
]
}
]
}
How it works:
$ifNull
to handle the not provided case(i.e. it could be undefined or null). It will fallback to the value null
.null
. It will bypass the whole $or
block, since the first $eq
block is evaluated to true
.$eq
block. Instead, it will be used in evaluation in 2nd $eq
block and matched against the field.With this basic building block, we can chain them up in an $and
to build multiple field matching:
{
"$and": [
<basic $or block for version field>,
<basic $or block for another field 1>,
<basic $or block for another field 2>,
...
]
}
an actual example would be:
db.collection.aggregate([
{
"$match": {
"$expr": {
"$and": [
{
"$or": [
{
"$eq": [
{
"$ifNull": [
// your version number input here
1,
null
]
},
null
]
},
{
"$eq": [
// your version number input here
1,
"$version"
]
}
]
},
{
"$or": [
{
"$eq": [
{
"$ifNull": [
// your name input here
null,
null
]
},
null
]
},
{
"$eq": [
// your name input here
null,
"$name"
]
}
]
}
]
}
}
}
])
Upvotes: 0
Reputation: 36114
I am not sure what will be the value in versionNumber
when its not provided (optional), lets assume versionNumber
will be any from ""
or undefined
or null
,
versionNumebr
is not available then it will skip and when its available then it will match $eq
condition[null, "", "undefined"]
, 0
zero or anything you wanted to skip {
$match: {
$expr: {
$cond: [
{ $in: [versionNumber, [null, "", "undefined"]] },
true,
{ $eq: ["$version", versionNumber] }
]
}
}
}
versionNumebr
will be always single possible value ""
then you can use $eq
instead of $in
, {
$match: {
$expr: {
$cond: [
{ $eq: [versionNumber, ""] },
true,
{ $eq: ["$version", versionNumber] }
]
}
}
}
Upvotes: 2
Reputation: 28356
There is a way to do that, yes, but it should be done in the application code. When building the pipeline array to pass to the query, only include the $match
stage if the necessary information is provided.
var pipeline=[]
if (versionNumber) pipeline.push( {$match: {version: versionNumber }} )
pipeline.push( ... Other Stages ... )
db.collection.aggregate(pipeline)
Upvotes: 1