Общ обхват и обяснение
Има няколко нередни неща в това, което правите тук. Първо условията на вашата заявка. Имате предвид няколко _id
стойности, където не би трябвало, и поне една от които не е на най-високо ниво.
За да влезе в "вложена" стойност и също така да се предположи, че _id
стойността е уникална и няма да се появи в нито един друг документ, формуляра ви за заявка трябва да бъде така:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Това всъщност би проработило, но наистина е само случайност, тъй като има много основателни причини, поради които не трябва да работи за вас.
Важният прочит е в официалната документация за позиционния $
оператор под темата "Вложени масиви". Това казва:
Позиционният оператор $ не може да се използва за заявки, които преминават през повече от един масив, като например заявки, които преминават през масиви, вложени в други масиви, тъй като заместването на заместващия $ е една стойност
По-конкретно това означава, че елементът, който ще бъде съпоставен и върнат в позиционния заместител, е стойността на индекса от първия съвпадащ масив. Това означава във вашия случай съвпадащия индекс на масива на "горното" ниво.
Така че, ако погледнете нотацията на заявката, както е показано, ние сме "твърдо кодирали" първия (или 0 index) позиция в масива от най-високо ниво и се случва така, че съответстващият елемент в „array2“ също е записът с нулев индекс.
За да демонстрирате това, можете да промените съответстващия _id
стойност на "124" и резултатът ще $push
нов запис в елемента с _id
„123“, тъй като и двете са в записа с нулев индекс на „array1“ и това е стойността, върната на заместителя.
Така че това е общият проблем с вложените масиви. Можете да премахнете едно от нивата и пак ще можете да $push
към правилния елемент във вашия масив "top", но пак ще има няколко нива.
Опитайте се да избягвате вложени масиви, тъй като ще срещнете проблеми с актуализирането, както е показано.
Общият случай е да „изгладите“ нещата, които „мислите“ за „нива“ и всъщност да направите тези „атрибути“ на крайните детайли. Например, "сплескана" форма на структурата във въпроса трябва да бъде нещо като:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Или дори когато приемате вътрешния масив е $push
само и никога не се актуализира:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Които и двете се поддават на атомни актуализации в обхвата на позиционния $
оператор
MongoDB 3.6 и по-нови версии
От MongoDB 3.6 има нови функции за работа с вложени масиви. Това използва филтрирания по позиция $[<identifier>]
синтаксис, за да се съпоставят конкретните елементи и да се прилагат различни условия чрез arrayFilters
в изявлението за актуализиране:
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"
както се предава на опциите за .update()
или дори.updateOne()
, .updateMany()
, .findOneAndUpdate()
или .bulkWrite()
метод определя условията, които да съответстват на идентификатора, даден в изявлението за актуализиране. Всички елементи, които отговарят на даденото условие, ще бъдат актуализирани.
Тъй като структурата е "вложена", ние всъщност използваме "множество филтри", както е посочено с "масив" от дефиниции на филтри, както е показано. Маркираният "идентификатор" се използва при съпоставяне с филтрирания по позиция $[<identifier>]
синтаксис, който действително се използва в блока за актуализиране на израза. В този случай inner
и outer
са идентификаторите, използвани за всяко условие, както е посочено с вложената верига.
Това ново разширение прави възможно актуализирането на съдържанието на вложен масив, но всъщност не помага за практичността на „запитване“ на такива данни, така че важат същите предупреждения, както беше обяснено по-рано.
Обикновено наистина „искате“ да изразите като „атрибути“, дори ако мозъкът ви първоначално мисли за „гнездене“, обикновено това е реакция на това как вярвате, че „предишните релационни части“ се събират. В действителност наистина имате нужда от повече денормализация.
Вижте също как да актуализирате множество елементи на масив в mongodb, тъй като тези нови оператори за актуализиране всъщност съвпадат и актуализират „множествени елементи на масива“, а не само първия , което е било предишното действие на позиционни актуализации.
ЗАБЕЛЕЖКА Донякъде иронично, тъй като това е посочено в аргумента "опции" за
.update()
и подобно на методите, синтаксисът обикновено е съвместим с всички последни версии на драйвери.Това обаче не е вярно за
mongo
shell, тъй като начина, по който методът е внедрен там ( "по ирония на съдбата за обратна съвместимост" ),arrayFilters
аргументът не се разпознава и премахва от вътрешен метод, който анализира опциите, за да осигури "обратна съвместимост" с предишни версии на сървъра на MongoDB и "наследен".update()
Синтаксис на извикване на API.Така че, ако искате да използвате командата в
mongo
shell или други „базирани на обвивка“ продукти (по-специално Robo 3T) имате нужда от най-новата версия от клона за разработка или от производствената версия от 3.6 или по-нова версия.
Вижте също positional all $[]
който също актуализира "множество елементи на масива", но без да се прилага към определени условия и се прилага за всички елементи в масива, където това е желаното действие.