sql >> Database >  >> NoSQL >> MongoDB

Object uit geneste array verwijderen op basis van meerdere criteria

Je kunt $pull de "eerste match" van de "outer array" met het verwijderen van "alle innerlijke elementen" door simpelweg te doen:

db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$.DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Dat is prima als je maar één item hebt in de "Distributions" array of ten minste slechts één van die items heeft onderliggende array-items die overeenkomen met de voorwaarde. Dit is hoe de positionele $ operator werkt met alle versies van MongoDB.

Als de gegevens "meerdere" overeenkomsten zouden hebben in de "buitenste" "Distributions" array en als je MongoDB 3.6 hebt, kun je de positioneel gefilterde $[<identifier>] toepassen operator om alle overeenkomende items te wijzigen:

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 dat geval de arrayFilters optie definieert een voorwaarde waarmee we items in de "buitenste" array matchen, zodat dit in feite van toepassing kan zijn op alles wat overeenkomt.

Of inderdaad sinds $pull heeft in wezen die voorwaarden zelf, dan kun je ook gewoon de positionele all $[] . gebruiken operator in dit geval:

db.Event.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

In beide gevallen verandert het document in de vraag door het binnenste item met alle null . te verwijderen toetsen:

{
        "_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")
                        ]
                }
        ]
}

De "query"-voorwaarden gebruiken allemaal $elemMatch voor documentselectie. Dit is eigenlijk vereist voor de positionele $ operator om de "positie-index" te verkrijgen die voor de "eerste match" wordt gebruikt. Hoewel dit niet echt een "vereiste" is voor de positioneel gefilterde $[<identifier>] of de positionele all $[] operator, het is nog steeds nuttig, zodat u zelfs geen documenten voor update in overweging neemt die niet voldoen aan de latere updatevoorwaarden van de $pull of de arrayFilters opties.

Wat betreft de $pull zelf, zijn de voorwaarden hier feitelijk van toepassing op "elk" array-element, dus er is geen noodzaak voor de $elemMatch in die bewerking omdat we al naar het "element" -niveau kijken.

Het derde voorbeeld laat zien dat de positionele all $[] operator kan gewoon die $pull . gebruiken voorwaarden met betrekking tot elk "binnenste" array-element en is alleen van toepassing op ALLE "buitenste" array-elementen. Dus het werkelijke punt van de positioneel gefilterde $[<identifier>] expression is om "alleen" die "buitenste" array-elementen te verwerken die daadwerkelijk overeenkomen met de "binnenste" voorwaarde. Daarom gebruiken we $elemMatch in de overweging voor het matchen van elk "binnen" array-element.

Als u in ieder geval MongoDB 3.6 niet hebt, gebruikt u het eerste formulier en herhaalt u dat waarschijnlijk totdat de updates uiteindelijk geen gewijzigde documenten meer retourneren die aangeven dat er geen elementen meer zijn die aan de voorwaarde voldoen.

Er is een veel gedetailleerdere beschrijving van de "alternatieven" als benaderingen bij Hoe meerdere array-elementen in mongodb te updaten, maar zolang uw gegevens ofwel in het eerste geval passen of u daadwerkelijk MongoDB 3.6 beschikbaar heeft, dan is dit de juiste benader hier.

Als u het volledige effect van de nieuwe syntaxis voor MongoDB 3.6. dit is de wijziging in het document in de vraag die ik heb gebruikt om de update-statements hier te verifiëren:

{
    "_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")
                    ]
            }
    ]
}

Wat in feite een aantal items dupliceert, zowel "outer" als "inner" om te laten zien hoe de instructie alle null verwijdert waarden.

OPMERKING arrayFilters worden gespecificeerd in het argument "opties" voor .update() en zoals methoden, is de syntaxis over het algemeen compatibel met alle recente release-stuurprogrammaversies en zelfs die voorafgaand aan de release van MongoDB 3.6.

Dit geldt echter niet voor de mongo shell, aangezien de manier waarop de methode daar is geïmplementeerd ("ironisch genoeg voor achterwaartse compatibiliteit") de arrayFilters argument wordt niet herkend en verwijderd door een interne methode die de opties parseert om "achterwaartse compatibiliteit" te leveren met eerdere MongoDB-serverversies en een "legacy" .update() API-aanroepsyntaxis.

Dus als je het commando in de mongo . wilt gebruiken shell of andere "shell-gebaseerde" producten (met name Robo 3T) je hebt een laatste versie nodig van de ontwikkelingstak of de productierelease vanaf 3.6 of hoger.

Robo 3T is met name hier nog steeds gebonden aan het feit dat het gebaseerd is op een MongoDB 3.4-shell. Dus zelfs wanneer verbinding wordt gemaakt met een capabele MongoDB 3.6-instantie, worden deze opties niet door dit programma aan de server doorgegeven. Het wordt aangeraden om alleen bij de shell en ondersteunde producten te blijven, hoewel er enkele andere aanbiedingen zijn die niet dezelfde beperking hebben.




  1. Wat is Hadoop Mapper Class in MapReduce?

  2. Een HASH opslaan in Redis op een rails-app

  3. (node:3341) Afschrijving Waarschuwing:Mongoose:mpromise

  4. MongoDB pull-element van array twee niveaus diep