I want to create a query schema validator for my MongoDB collection, as explained here: https://www.mongodb.com/docs/manual/core/schema-validation/specify-query-expression-rules/
Two example documents could look like this:
{
_id: "1",
arr: [
{a: 10},
{a: 12, b: 15}
]
},
{
_id: "2",
arr: [
{a: 12, b: 10},
{a: 15}
]
}
I want all documents, where arr
exists, to only have arr
elements that either satisfy a > b
, or do not have b
. In the given example above, the first document would be invalid, because the second element of arr
has b
and does not satisfy a > b
.
The first query that came to my mind is the following:
{
$or: [
{
arr: {$exists: 0}
},
{
arr: {
$elemMatch: {
$or: [
{
b: {$exists: 0}
},
{
$expr: {$gt: ["$a", "$b"]}
}
]
}
}
}
]
}
Sadly, this does not work. Apparently, $expr
cannot be used inside $elemMatch
.
I cannot use aggregations, because the schema validator can only use the query operator.
What is the alternative to $expr
inside $elemMatch
?
Below is my interpretation of the validation rules:
arr
fieldarr
doesn't exist, the document passarr
is an empty array, the document passarr
contains element(s), proceed to step 2arr
,b
, this element passb
, it requires a
to be larger than b
to passFor step 2, the document only passes when all elements pass.
So, it translates to below $expr
:
{
$expr: {
$or: [
{
$eq: [
0,
{
$size: {
$ifNull: [
'$arr',
[]
]
}
}
]
},
{
$allElementsTrue: {
$map: {
input: '$arr',
as: 'elem',
'in': {
$or: [
{
$eq: [
null,
{
$ifNull: [
'$$elem.b',
null
]
}
]
},
{
$gt: [
'$$elem.a',
'$$elem.b'
]
}
]
}
}
}
}
]
}
}
Below is how it looks in my local MongoDB setup with your sample documents. You can see document 1 failed the validation while document2 passed