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.