Ogólny zakres i wyjaśnienie
Jest kilka rzeczy nie tak z tym, co tutaj robisz. Po pierwsze warunki zapytania. Odwołujesz się do kilku _id
wartości, których nie powinieneś potrzebować, a przynajmniej jedna z nich nie znajduje się na najwyższym poziomie.
Aby dostać się do "zagnieżdżonej" wartości, a także zakładając, że _id
wartość jest unikalna i nie pojawia się w żadnym innym dokumencie, formularz zapytania powinien wyglądać tak:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
To by faktycznie zadziałało, ale tak naprawdę to tylko przypadek, że tak się dzieje, ponieważ istnieją bardzo dobre powody, dla których to nie powinno działać.
Ważna lektura znajduje się w oficjalnej dokumentacji pozycyjnego $
operator w temacie „Zagnieżdżone tablice”. To co mówi to:
Pozycyjnego operatora $ nie można używać w zapytaniach, które przechodzą przez więcej niż jedną tablicę, takich jak zapytania, które przechodzą przez tablice zagnieżdżone w innych tablicach, ponieważ zamiennik symbolu zastępczego $ jest pojedynczą wartością
W szczególności oznacza to, że element, który zostanie dopasowany i zwrócony w pozycyjnym symbolu zastępczym, jest wartością indeksu z pierwszego pasująca tablica. Oznacza to w twoim przypadku pasujący indeks w tablicy „najwyższego” poziomu.
Jeśli więc spojrzysz na pokazaną notację zapytania, „zakodowaliśmy” na stałe pierwszy ( lub 0 index ) pozycji w tablicy najwyższego poziomu i tak się składa, że pasujący element w "array2" jest również wpisem indeksu zerowego.
Aby to zademonstrować, możesz zmienić pasujący _id
wartość na "124", a wynik będzie $push
nowy wpis do elementu z _id
„123”, ponieważ oba znajdują się we wpisie indeksu zerowego „array1” i jest to wartość zwracana do symbolu zastępczego.
To jest ogólny problem z zagnieżdżaniem tablic. Możesz usunąć jeden z poziomów i nadal będziesz mógł $push
do właściwego elementu w tablicy „top”, ale nadal będzie wiele poziomów.
Staraj się unikać zagnieżdżania tablic, ponieważ napotkasz problemy z aktualizacją, jak pokazano.
Ogólnym przypadkiem jest „spłaszczenie” rzeczy, o których „myślisz”, że są „poziomami” i nadanie im „atrybutów” ostatecznym elementom szczegółów. Na przykład „spłaszczona” forma struktury w pytaniu powinna wyglądać mniej więcej tak:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Lub nawet jeśli zaakceptowanie wewnętrznej tablicy to $push
tylko i nigdy nie aktualizowane:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Które obie nadają się do atomowych aktualizacji w zakresie pozycyjnego $
operator
MongoDB 3.6 i nowsze
Od MongoDB 3.6 dostępne są nowe funkcje do pracy z zagnieżdżonymi tablicami. Używa filtrowanego pozycyjnie $[<identifier>]
składnia w celu dopasowania określonych elementów i zastosowania różnych warunków za pomocą arrayFilters
w oświadczeniu o aktualizacji:
Model.update(
{
"_id": 1,
"array1": {
"$elemMatch": {
"_id": "12","array2._id": "123"
}
}
},
{
"$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
},
{
"arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }]
}
)
"arrayFilters"
przekazane do opcji .update()
lub nawet.updateOne()
, .updateMany()
, .findOneAndUpdate()
lub .bulkWrite()
Metoda określa warunki dopasowania identyfikatora podanego w instrukcji aktualizacji. Wszelkie elementy pasujące do podanego warunku zostaną zaktualizowane.
Ponieważ struktura jest „zagnieżdżona”, w rzeczywistości używamy „wielu filtrów”, jak określono w „tablicy” definicji filtrów, jak pokazano. Oznaczony „identyfikator” jest używany w dopasowaniu do filtrowanego pozycyjnie $[<identifier>]
składnia faktycznie używana w bloku aktualizacji instrukcji. W tym przypadku inner
i outer
są identyfikatorami używanymi dla każdego warunku określonego w zagnieżdżonym łańcuchu.
To nowe rozszerzenie umożliwia aktualizację zawartości zagnieżdżonej tablicy, ale tak naprawdę nie pomaga w praktycznym „przeszukiwaniu” takich danych, więc obowiązują te same zastrzeżenia, co wyjaśniono wcześniej.
Zazwyczaj naprawdę „masz na myśli” wyrażanie jako „atrybuty”, nawet jeśli twój mózg początkowo myśli „zagnieżdżanie”, jest to po prostu reakcja na to, jak wierzysz, że „poprzednie części relacyjne” łączą się. W rzeczywistości naprawdę potrzebujesz więcej denormalizacji.
Zobacz także Jak zaktualizować wiele elementów tablicy w mongodb, ponieważ te nowe operatory aktualizacji faktycznie dopasowują i aktualizują „wiele elementów tablicy”, a nie tylko pierwszy , który był poprzednią akcją aktualizacji pozycyjnych.
UWAGA Trochę ironicznie, ponieważ jest to określone w argumencie „opcje” dla .update()
i podobnie jak metody, składnia jest ogólnie zgodna ze wszystkimi najnowszymi wersjami sterowników.
Nie dotyczy to jednak mongo
powłoki, ponieważ sposób, w jaki metoda jest tam zaimplementowana ( "jak na ironię dla wstecznej kompatybilności"), arrayFilters
argument nie jest rozpoznawany i usuwany przez wewnętrzną metodę, która analizuje opcje w celu zapewnienia „zgodności wstecznej” z wcześniejszymi wersjami serwera MongoDB i „starszego” .update()
Składnia wywołań API.
Więc jeśli chcesz użyć polecenia w mongo
shell lub inne produkty "oparte na powłoce" (zwłaszcza Robo 3T), potrzebujesz najnowszej wersji z gałęzi rozwojowej lub wydania produkcyjnego w wersji 3.6 lub nowszej.
Zobacz także positional all $[]
który aktualizuje również „wiele elementów tablicy”, ale bez stosowania określonych warunków i dotyczy wszystkich elementy w tablicy, gdzie jest to pożądane działanie.