I have a collection "fruits" like this:
db={
"fruits": [
{
"name": "Banana",
"variants": [
{
"color": "green",
"props": [
{
"uid": 1,
"grade": 3
},
{
"uid": 3,
"grade": 2
}
]
},
{
"color": "yellow",
"props": [
{
"uid": 2,
"grade": 1
},
{
"uid": 1,
"grade": 2
}
]
},
{
"color": "brown",
"props": [
{
"uid": 1,
"grade": 3
},
{
"uid": 4,
"grade": 2
}
]
}
]
}
]
}
available_prop_uids = [1,4,5]
Now I want to filter the documents in an aggregation by multiple criteria in case the array variants.props is not empty:
a.) Filter array variants
by max props.grade
: variants
should contain only subdocuments that contain at least one prop with the max prop value from all variants per document. In the example, the variants with color green
and brown
have a prop with grade 3, which is the highest grade among all variants for the document Banana
. Therefore, the variants array is reduced to two subdocuments, with color green
and brown
.
b.) Filter array variants
by available_prop_uids: Furthermore, variants
should contain only subdocuments where the set of prop.uids is a subset of available_prop_uids.
Variant green
is now removed from the array since it contains a prop with uid 3
which is not in available_prop_uids
.
Desired outcome:
[{
"name": "Banana",
"variants": [
{
"color": "brown",
"props": [
{
"uid": 1,
"grade": 3
},
{
"uid": 4,
"grade": 2
}
]
}
]
}]
Thanks for your help!
here are the steps i took.
props.grade
value and save it in a temporary field.props.grade
values is a subset of the input listdb.collection.aggregate([
{
$addFields: {
max: {
$max: {
$map: {
input: "$variants",
as: "variant",
in: {
$reduce: {
input: "$$variant.props",
initialValue: 0,
in: {
$cond: [ { $gt: [ "$$this.grade", "$$value" ] }, "$$this.grade", "$$value" ]
}
}
}
}
}
}
}
},
{
$addFields: {
variants: {
$filter: {
input: "$variants",
as: "variant",
cond: {
$or: [
{ $eq: [ { $size: "$$variant.props" }, 0 ] },
{
$anyElementTrue: {
$map: {
input: "$$variant.props",
as: "prop",
in: { $eq: [ "$$prop.grade", "$max" ] }
}
}
}
]
}
}
}
}
},
{
$addFields: {
variants: {
$filter: {
input: "$variants",
as: "variant",
cond: {
$or: [
{ $eq: [ { $size: "$$variant.props" }, 0 ] },
{
$setIsSubset: [
{
$map: {
input: "$$variant.props",
as: "prop",
in: "$$prop.uid"
}
},
[ 1, 4, 5 ]
]
}
]
}
}
}
}
},
{ $unset: "max" }
])
Steps 2 and 3 can be combined to 1 if needed. will remove an extra looping step https://mongoplayground.net/p/LdmTI45VYvJ