MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Usuń pole znalezione w dowolnej tablicy mongodb

Zadałem więc pytanie w komentarzach, ale wygląda na to, że odszedłeś, więc myślę, że po prostu odpowiem na trzy możliwe przypadki, które widzę.

Na początek nie jestem pewien, czy elementy pokazane w zagnieżdżonych tablicach są tylko elementy w tablicy lub w rzeczywistości, jeśli arrayToDelete jest tylko pole obecne w tych elementach. Więc w zasadzie muszę abstraktować trochę i uwzględnij ten przypadek:

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

Przypadek 1 – Usuń wewnętrzne elementy tablicy, w których obecne jest pole

Spowoduje to użycie $pull operatora, ponieważ to właśnie usuwa elementy tablicy całkowicie. Robisz to w nowoczesnym MongoDB za pomocą takiego oświadczenia:

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

To zmienia wszystkie pasujące dokumenty w ten sposób:

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

Więc każdy element, który zawierał to pole, jest teraz usuwany.

Przypadek 2 – Po prostu usuń dopasowane pole z wewnętrznych elementów

Tutaj używasz $unset . To tylko trochę inne niż „twarde indeksowanie” wersja, którą robiłeś:

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

Co zmienia wszystkie dopasowane 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"
                        }
                ]
        ]
}

Więc wszystko nadal tam jest, ale tylko zidentyfikowane pola zostały usunięte z każdego dokumentu tablicy wewnętrznej.

Przypadek 3 — Właściwie chciałeś usunąć „Wszystko” z tablicy.

Co jest naprawdę prostym przypadkiem użycia $set i wycieranie wszystkiego, co było tam wcześniej:

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

Gdzie należy spodziewać się wyników:

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

Więc co oni wszyscy robią?

Pierwszą rzeczą, którą powinieneś zobaczyć, jest predykat zapytania . Jest to ogólnie dobry pomysł, aby upewnić się, że nie pasujesz, a nawet „próbujesz” mieć spełnione warunki aktualizacji na dokumentach, które nawet nie zawierają danych o wzorze, który zamierzasz zaktualizować. Tablice zagnieżdżone są trudne w najlepszym razie i tam, gdzie jest to praktyczne, naprawdę powinieneś ich unikać, ponieważ często „naprawdę masz na myśli” jest w rzeczywistości reprezentowana w postaci pojedynczej tablicy z dodatkowymi atrybutami reprezentującymi to, co "myślisz" zagnieżdżanie faktycznie robi dla ciebie.

Ale tylko dlatego, że są trudne nie oznacza niemożliwe . Musisz tylko zrozumieć $elemMatch :

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

To jest podstawowe find() przykład, który pasuje na podstawie $elemMatch warunek dla zewnętrznego tablica używa innego $elemMatch w celu dopasowania innego warunku w wewnętrznym szyk. Mimo że „pojawia się” być pojedynczym predykatem. Coś takiego:

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

Po prostu nie zadziała. Ani nie:

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

Z "podwójną kropką" .. ponieważ jest to po prostu nieważne.

To jest predykat zapytania aby dopasować „dokumenty”, które należy przetworzyć, ale reszta dotyczy faktycznego określenia *które części należy zaktualizować”.

W przypadku 1 aby $pull z wewnętrznego tablicy, najpierw musimy być w stanie zidentyfikować, które elementy zewnętrznej tablica zawiera dane do aktualizacji. To właśnie "scan.$[a]" robi to za pomocą filtrowanego pozycyjnie $[<identifier>] operatora.

Ten operator zasadniczo transponuje dopasowane indeksy ( tak wiele z nich ) w tablicy do innego predykatu który jest zdefiniowany w trzeciej sekcji update styl poleceń za pomocą arrayFilters Sekcja. Ta sekcja zasadniczo definiuje warunki, które należy spełnić z perspektywy nazwanego identyfikatora.

W tym przypadku „identyfikator” nazywa się a , i jest to prefiks używany w arrayFilters wpis:

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

W kontekście aktualnego oświadczenia o aktualizacji część:

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

Następnie z perspektywy "a" będący identyfikatorem zewnętrznego element tablicy najpierw do wewnątrz od "scan" , obowiązują te same warunki, co w przypadku oryginalnego predykatu zapytania ale od „wewnątrz” pierwszy $elemMatch oświadczenie. Możesz więc zasadniczo myśleć o tym jako o „zapytanie w zapytaniu” z perspektywy już „zaglądania do środka” zawartość każdego zewnętrznego element.

Tym samym $pull działa podobnie jak "zapytanie w zapytaniu" w tym, że jego własne argumenty są również stosowane z perspektywy elementu tablicy. Dlatego po prostu arrayToDelete pole istniejące zamiast:

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

Ale to wszystko jest specyficzne dla $pull , a inne rzeczy mają różne przypadki:

Przypadek 2 patrzy, gdzie chcesz po prostu $unset nazwane pole. Wydaje się całkiem proste, ponieważ po prostu nazwałeś pole, prawda? Cóż, niezupełnie, ponieważ poniższe informacje wyraźnie nie zgadzają się z tym, co wiemy wcześniej:

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

I oczywiście notowanie indeksów tablicy dla wszystkiego jest tylko uciążliwe:

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

To jest powód, dla którego pozycyjne wszystkie $[] operator. Ten jest trochę bardziej „brutalną siłą” niż filtrowany pozycyjnie $[<identifier>] w tym zamiast dopasowywać inny orzecznik dostarczane w arrayFilters , to po prostu ma zastosowanie do wszystko w zawartości tablicy w tym „indeksie”. W zasadzie jest to sposób na powiedzenie „wszystkie indeksy” bez wpisywania każdego z nich, jak straszne przypadek pokazany powyżej.

Więc to nie jest dla wszystkich przypadków , ale z pewnością dobrze pasuje do $unset ponieważ ma to bardzo specyficzne nazewnictwo ścieżki co oczywiście nie ma znaczenia, jeśli ta ścieżka nie pasuje do każdego elementu tablicy.

możesz nadal używaj arrayFilters i filtrowany pozycyjnie $[<identifier>] , ale tutaj byłaby to przesada. Poza tym nie zaszkodzi zademonstrować inne podejście.

Ale oczywiście prawdopodobnie warto zrozumieć, jak dokładnie tak wyglądałoby stwierdzenie, więc:

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

Zauważając, że "b.arrayToDelete" może nie być tym, czego oczekujesz na początku, ale biorąc pod uwagę położenie w "scan.$[a].$[b] to naprawdę powinno mieć sens, jak od b nazwa elementu zostanie osiągnięta za pomocą „notacji kropkowej”, tak jak pokazano. I faktycznie w obu przypadkach. Po raz kolejny $unset i tak miałoby zastosowanie tylko do nazwanego pola, więc kryteria wyboru naprawdę nie są wymagane.

I Przypadek 3 . Cóż, jest to dość proste, jeśli nie potrzebujesz aby zachować cokolwiek innego w tablicy po usunięciu tej zawartości (np. $pull gdzie pasujące do tego pola były jedyne rzeczy tam, lub $unset jeśli o to chodzi ), to po prostu nie zadzieraj z niczym innym i po prostu wyczyść tablicę .

Jest to ważne rozróżnienie, jeśli weźmiesz pod uwagę, że zgodnie z punktem wyjaśniającym, czy dokumenty z nazwanym polem zawierają tylko elementy w zagnieżdżonych tablicach i rzeczywiście, nazwany klucz był jedynym rzecz obecna w dokumentach.

Ponieważ rozumowanie jest takie, że użycie $pull jak pokazano tutaj i w tych warunkach otrzymasz:

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

Lub za pomocą $unset :

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

Oba są wyraźnie niepożądane. Więc masz powód, jeśli arrayToDelete pole było tylko treści, które w ogóle tam były, wtedy najbardziej logiczny sposób na usunięcie wszystkich jest po prostu zastąpienie tablicy pustą. Lub rzeczywiście $unset cała właściwość dokumentu.

Pamiętaj jednak, że wszystkie te "wymyślne rzeczy" ( z wyjątkiem $set oczywiście ) wymaga co najmniej MongoDB 3.6 dostępne w celu korzystania z tej funkcji.

Jeśli nadal korzystasz ze starszej wersji MongoDB niż ta (i w dniu pisania tego tekstu, naprawdę nie powinieneś, ponieważ twoje oficjalne wsparcie kończy się w ciągu zaledwie 5 miesięcy od tej daty), a następnie inne istniejące odpowiedzi na temat Jak zaktualizować wiele Elementy tablicy w mongodb są w rzeczywistości dla Ciebie.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Słownik Pythona:usuwanie u' chars

  2. MongoDB $binarySize

  3. Jak uzyskać schemat bazy danych mongoose zdefiniowanej w innym modelu?

  4. Wskazówki dotyczące planowania schematu MongoDB

  5. MongoDB na komputerze z systemem Windows 7:nie można nawiązać połączenia