You can $pull
the "first match" from the "outer array" with removing "all the inner elements" simply by doing:
db.Events.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$.DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
}
)
That is fine if you only ever have one entry in the "Distributions"
array or at least only one of those entries has child array entries which would match the condition. This is how the positional $
operator works with all versions of MongoDB.
If the data would have "multiple" matches in the "outer" "Distributions"
array then if you have MongoDB 3.6 you can apply the positional filtered $[<identifier>]
operator to modify all matched entries:
db.Events.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$[element].DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"arrayFilters": [
{ "element.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}}
]
}
)
In that case the arrayFilters
option defines a condition by which we match entries in the "outer" array so that this can in fact apply to everything which is matched.
Or indeed since $pull
essentially has those conditions itself, then you can alternately just use the positional all $[]
operator in this case:
db.Event.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$[].DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
}
)
Either case changes the document in the question by removing the inner item with all null
keys:
{
"_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
}
]
}
The "query" conditions all use $elemMatch
for document selection. This is actually required for the positional $
operator in order to obtain the "position index" used for the "first match". Whilst this is not actually a "requirement" for either the positional filtered $[<identifier>]
or the positional all $[]
operator, it is still useful so you don't even consider documents for update which will not match the later update conditions of either the $pull
or the arrayFilters
options.
As for the $pull
itself, the conditions here actually apply to "each" array element, so there is no need for the $elemMatch
in that operation since we are already looking at the "element" level.
The third example shows that the positional all $[]
operator can simply use those $pull
conditions in consideration of each "inner" array element and will just apply to ALL "outer" array elements. So the actual point of the positional filtered $[<identifier>]
expression is to "only" process those "outer" array elements which actually match the "inner" condition. Hence why we use $elemMatch
in the consideration for matching each "inner" array element.
If you do not actually have MongoDB 3.6 at least then you are using the first form and likely repeating that until the updates finally return no more modified documents indicating that there are no more elements left that match the condition.
There is a much more detailed write up on the "alternatives" as approaches at How to Update Multiple Array Elements in mongodb, but as long as your data either suits the initial case or you actually do have MongoDB 3.6 available, then this is the correct approach here.
If you want to see the full effect of the new syntax for MongoDB 3.6. this is the alteration to the document in the question I used to verify the update statements here:
{
"_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
},
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
}
]
}
Which basically duplicates some entries both "outer" and "inner" to show how the statement removes all the null
values.
NOTE arrayFilters
are specified in the "options" argument for .update()
and like methods, the syntax is generally compatible with all recent release driver versions and even those prior to the release of MongoDB 3.6.
However this is not true of the mongo
shell, s