sql >> Databáze >  >> NoSQL >> MongoDB

Odebrat pole nalezené v libovolném poli mongodb

Takže jsem položil otázku v komentářích, ale zdá se, že jste odešli, takže myslím, že odpovídám jen na tři možné případy, které vidím.

Za prvé, nejsem si jistý, zda prvky zobrazené ve vnořených polích jsou jediné prvky v poli nebo ve skutečnosti if arrayToDelete je jediný pole přítomné v těchto prvcích. Takže v podstatě potřebuji abstraktovat trochu a zahrňte tento případ:

{
    field: 'value',
    field2: 'value',
    scan: [
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
    ]
}

Případ 1 – Odstraňte vnitřní prvky pole, kde je přítomno pole

To by použilo $pull operátor, protože to je to, co odstraní prvky pole zcela. V moderním MongoDB to uděláte takto:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },
  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }
)

Tím se změní všechny odpovídající dokumenty takto:

{
        "_id" : ObjectId("5ca1c36d9e31550a618011e2"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ]
        ]
}

Takže každý prvek, který toto pole obsahoval, je nyní odstraněn.

Případ 2 – Stačí odstranit odpovídající pole z vnitřních prvků

Zde použijete $unset . Je to jen trochu jiné než "pevně indexované" verze, kterou jste dělali:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[].$[].arrayToDelete": ""  } }
)

Což změní všechny odpovídající dokumenty na:

{
        "_id" : ObjectId("5ca1c4c49e31550a618011e3"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ]
        ]
}

Všechno tam tedy stále je, ale z každého dokumentu vnitřního pole byla odstraněna pouze identifikovaná pole.

Případ 3 – Ve skutečnosti jste chtěli odstranit „Vše“ v poli.

Což je ve skutečnosti jen jednoduchý případ použití $set a vymazání všeho, co tam bylo předtím:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$set": { "scan": []  } }
)

Kde lze výsledky docela dobře očekávat:

{
        "_id" : ObjectId("5ca1c5c59e31550a618011e4"),
        "field" : "value",
        "field2" : "value",
        "scan" : [ ]
}

Co tedy všichni dělají?

Úplně první věc, kterou byste měli vidět, je predikát dotazu . To je obecně dobrý nápad, abyste se ujistili, že se neshodujete a dokonce se "pokusíte" mít splněny podmínky aktualizace u dokumentů, které ani neobsahují data se vzorem, který hodláte aktualizovat. Vnořená pole jsou obtížná v nejlepším případě a kde je to praktické, měli byste se jim skutečně vyhnout, protože často "opravdu vážně" je ve skutečnosti reprezentováno v singulárním poli s dalšími atributy představujícími to, co si "myslíte" vnoření vlastně dělá pro vás.

Ale jen proto, že jsou tvrdé neznamená nemožné . Jenom musíte rozumět $elemMatch :

db.colelction.find(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  }}
)

To je základní find() příklad, který odpovídá na základě $elemMatch stav pro vnější pole používá jiný $elemMatch aby odpovídala jiné podmínce v vnitřku pole. I když se to "objeví" být predikátem v jednotném čísle. Něco jako:

"scan.arrayToDelete": { "$exists": true }

Prostě to nebude fungovat. Ani by:

"scan..arrayToDelete": { "$exists": true }

S "dvojitou tečkou" .. protože to v podstatě prostě neplatí.

To je predikát dotazu aby odpovídaly "dokumentům", které je třeba zpracovat, ale zbytek se týká skutečného určení *které části aktualizovat."

V Případu 1 za účelem $pull z vnitřního pole, nejprve musíme být schopni identifikovat, které prvky vnější pole obsahuje data k aktualizaci. To je to, co "scan.$[a]" věc se dělá pomocí pozičně filtrovaného $[<identifier>] operátor.

Tento operátor v podstatě transponuje odpovídající indexy (tak mnoho z nich ) v poli na jiný predikát který je definován ve třetí části update stylem pomocí arrayFilters sekce. Tato část v podstatě definuje podmínky, které je třeba splnit z pohledu pojmenovaného identifikátoru.

V tomto případě se "identifikátor" jmenuje a , a to je předpona použitá v arrayFilters záznam:

  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }

Vzato v kontextu se skutečným prohlášením o aktualizaci část:

  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },

Potom z pohledu "a" je identifikátor pro vnější prvek pole nejprve dovnitř z "scan" , pak platí stejné podmínky jako pro původní predikát dotazu ale z "uvnitř" první $elemMatch prohlášení. Takže si to můžete v podstatě představit jako „dotaz v dotazu“ z pohledu již „hledání dovnitř“ obsah každého vnějšího prvek.

Stejným způsobem $pull funguje podobně jako "dotaz v dotazu" v tom, že jeho vlastní argumenty jsou také aplikovány z pohledu prvku pole. Proto stačí arrayToDelete existující pole namísto:

  // This would be wrong! and do nothing :(
  {
    "$pull": {
      "scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
    }
  }

Ale to vše je specifické pro $pull , a další věci mají různé případy:

Případ 2 podívá se na místo, kde chcete pouze $unset pojmenované pole. Zdá se to docela snadné, když pojmenujete pole, že? Tedy ne přesně, protože následující zjevně není správné z toho, co víme dříve:

  { "$unset": { "scan.arrayToDelete": ""  } } // Not right :(

A samozřejmě zaznamenávání indexů pole pro všechno je jen utrpení:

  { "$unset": { 
    "scan.0.0.arrayToDelete": "",
    "scan.0.1.arrayToDelete": "",
    "scan.0.2.arrayToDelete": "",
    "scan.0.3.arrayToDelete": "",  // My fingers are tired :-<
  } }

To je důvod pro poziční vše $[] operátor. Tohle je trochu více "hrubá síla" než pozičně filtrovaný $[<identifier>] v tom místo shody s jiným predikátem poskytnuto v rámci arrayFilters , to se jednoduše týká všeho v obsahu pole na tomto "indexu". Je to v podstatě způsob, jak ve skutečnosti říci "všechny indexy" aniž byste všechny vypsali jako strašné případ zobrazený výše.

Není to tedy pro všechny případy , ale určitě se dobře hodí pro $unset protože má velmi specifické pojmenování cesty což samozřejmě nevadí, pokud tato cesta neodpovídá každému jednotlivému prvku pole.

Mohli byste stále používejte arrayFilters a pozičně filtrovaný $[<identifier>] , ale tady by to bylo přehnané. Navíc neuškodí předvést jiný přístup.

Ale samozřejmě stojí za to pochopit jak přesně takové prohlášení by vypadalo, takže:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[a].$[b].arrayToDelete": ""  } },
  {
    "arrayFilters": [
      { "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
      { "b.arrayToDelete": { "$exists": true } },
    ]
  }
)

Všimněte si, že "b.arrayToDelete" nemusí být to, co zpočátku očekáváte, ale vzhledem k umístění v "scan.$[a].$[b] opravdu by to mělo dávat smysl jako z b název prvku by byl dosažen pomocí "tečkové notace", jak je znázorněno. A vlastně v obou případech. Opět $unset by se stejně vztahovalo pouze na pojmenované pole, takže kritéria výběru skutečně nejsou vyžadována.

A Případ 3 . No, je to docela jednoduché v tom, že pokud to nepotřebujete aby po odstranění tohoto obsahu zůstalo v poli cokoli jiného (tj. $pull kde pole odpovídající tomuto byla jediná věci tam nebo $unset když na to přijde ), pak se prostě s ničím jiným nehrabejte a prostě vymažte pole .

Toto je důležitý rozdíl, pokud uvážíte, že podle bodu, abyste objasnili, zda dokumenty s pojmenovaným polem obsahují pouze prvků ve vnořených polích a skutečně, že pojmenovaný klíč byl jediným věc přítomná v dokumentech.

S odůvodněním, že pomocí $pull jak je uvedeno zde a za těchto podmínek byste získali:

{
        "_id" : ObjectId("5ca321909e31550a618011e6"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [ ],
            [ ],
            [ ]
        ]
}

Nebo pomocí $unset :

{
        "_id" : ObjectId("5ca322bc9e31550a618011e7"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }]
        ]
}

Obojí zjevně není žádoucí. Je tedy logické, že arrayToDelete pole bylo jediné obsah, který tam vůbec byl, pak nejlogičtější způsob odstranit vše je jednoduše nahradit pole prázdným. Nebo skutečně $unset vlastnost celého dokumentu.

Všimněte si však, že všechny tyto "fantastické věci" (s výjimkou $set samozřejmě ) vyžadují, abyste museli mít alespoň MongoDB 3.6 k dispozici pro použití této funkce.

V případě, že stále používáte starší verzi MongoDB, než je ta (a k datu psaní byste opravdu neměli být, protože vaše oficiální podpora vyprší za pouhých 5 měsíců od tohoto data), pak další existující odpovědi na téma Jak aktualizovat více Prvky pole v mongodb jsou vlastně pro vás.



  1. Mongodb dotaz založený na položce na konkrétní pozici v poli

  2. MongoDB forEach()

  3. MongoDB odhadovanýDocumentCount()

  4. Jak zvýšit výkon Redis při 100% CPU? Sdílení? Nejrychlejší .Net klient?