Затова зададох въпрос в коментарите, но вие изглежда сте се отдалечили, така че предполагам, че просто отговарям на трите възможни случая, които виждам.
Като начало, не съм сигурен дали елементите, показани във вложените масиви, са единствените елементи в масива или всъщност ако arrayToDelete
е самоединствен поле, присъстващо в тези елементи. Така че по принцип трябва да абстрахирам малко и включете този случай:
{
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"
},
],
]
}
Случай 1 – Премахнете елементите на вътрешния масив, където присъства полето
Това ще използва $pull
оператор, тъй като това премахва елементите на масива изцяло. Правите това в съвременния MongoDB с изявление като това:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
)
Това променя всички съответстващи документи по следния начин:
{
"_id" : ObjectId("5ca1c36d9e31550a618011e2"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
]
]
}
Така че всеки елемент, който съдържа това поле, сега се премахва.
Случай 2 – Просто премахнете съвпадащото поле от вътрешните елементи
Тук използвате $unset
. Просто е малко по-различен от „твърдо индексирания“ версия, която правехте:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[].$[].arrayToDelete": "" } }
)
Което променя всички съвпадащи документи да бъдат:
{
"_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"
}
]
]
}
Така че всичко е все още там, но само идентифицираните полета са премахнати от всеки документ за вътрешен масив.
Случай 3 – Вие всъщност искахте да премахнете „Всичко“ в масива.
Което всъщност е просто прост случай на използване на $set
и изтриване на всичко, което е било там преди:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$set": { "scan": [] } }
)
Където трябва да се очакват резултатите:
{
"_id" : ObjectId("5ca1c5c59e31550a618011e4"),
"field" : "value",
"field2" : "value",
"scan" : [ ]
}
И какво правят всички тези?
Първото нещо, което трябва да видите, е предиката на заявката . Това обикновено е добра идея, за да се уверите, че не отговаряте и дори „опитвате“ да са изпълнени условията за актуализиране на документи, които дори не съдържат данни с модела, който възнамерявате да актуализирате. Вложените масиви са трудни в най-добрия случай и когато е практично, наистина трябва да ги избягвате, тъй като често „наистина имате предвид“ всъщност е представен в единичен масив с допълнителни атрибути, представляващи това, което "мислите" гнезденето всъщност прави за вас.
Но само защото са твърди не означава невъзможно . Просто трябва да разберете $elemMatch
:
db.colelction.find(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
}}
)
Това е основният find()
пример, който съвпада въз основа на $elemMatch
условие за въна масивът използва друг $elemMatch
за да съвпадне с друго условие ввътрешно масив. Въпреки че това „се появява“ да е единствено число предикат. Нещо като:
"scan.arrayToDelete": { "$exists": true }
Просто няма да работи. Нито едно от двете:
"scan..arrayToDelete": { "$exists": true }
С "двойната точка" ..
защото това просто не е валидно.
Това е предиката на заявката за да съответства на "документи", които трябва да бъдат обработени, но останалото важи за действително определяне *кои части да се актуализират."
Вслучай 1 за да $pull
от ввътрешна масив, първо трябва да можем да идентифицираме кои елементи от външния масив съдържа данните за актуализиране. Това е, което "scan.$[a]"
нещо се прави с помощта на филтрирания по позиция $[<identifier>]
оператор.
Този оператор основно транспонира съвпадащите индекси (толкова много от тях ) в масива към друг предикат който е дефиниран в третия раздел на update
стил команди с arrayFilters
раздел. Този раздел основно дефинира условията, които трябва да бъдат изпълнени от гледна точка на посочения идентификатор.
В този случай изходният "идентификатор" се нарича a
, и това е префиксът, използван в arrayFilters
запис:
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
Взето в контекст с действителното изявление за актуализиране част:
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
След това от гледна точка на "a"
като идентификатор за външния елемент на масива първо навътре от "scan"
, тогава важат същите условия като за оригиналния предикат на заявка но от „вътре“ първият $elemMatch
изявление. Така че по принцип можете да мислите за това като за „заявка в заявка“ от гледна точка на вече „поглеждане вътре“ съдържанието на всяка външна елемент.
По същия начин $pull
действа подобно на "заявка в заявка" тъй като неговите собствени аргументи също се прилагат от гледна точка на елемента на масива. Следователно само arrayToDelete
съществуващо поле вместо:
// This would be wrong! and do nothing :(
{
"$pull": {
"scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
}
}
Но всичко това е специфично за $pull
, а други неща имат различни случаи:
Случай 2 разглежда къде искате просто да $unset
наименуваното поле. Изглежда доста лесно, тъй като просто назовавате полето, нали? Е, не точно, тъй като следното очевидно не е правилно от това, което знаем по-рано:
{ "$unset": { "scan.arrayToDelete": "" } } // Not right :(
И разбира се отбелязването на индекси на масиви за всичко е просто мъка:
{ "$unset": {
"scan.0.0.arrayToDelete": "",
"scan.0.1.arrayToDelete": "",
"scan.0.2.arrayToDelete": "",
"scan.0.3.arrayToDelete": "", // My fingers are tired :-<
} }
Това е причината за позиционното всички $[]
оператор. Този е малко по-"груба сила" отколкото филтрирания по позиция $[<identifier>]
в това вместо да съвпада с друг предикат предоставено в arrayFilters
, това просто се отнася за всичко в съдържанието на масива в този "индекс". Това всъщност е начин да се каже "всички индекси" без да пишете всеки един като ужасния случай, показан по-горе.
Така че не е за всички случаи , но със сигурност е много подходящ за $unset
тъй като това има много специфично именуване на път което, разбира се, няма значение, ако този път не съвпада с всеки един елемент от масива.
Вие можете все още използвайте arrayFilters
и филтриран по позиция $[<identifier>]
, но тук би било прекалено. Освен това не пречи да демонстрирате другия подход.
Но, разбира се, вероятно си струва да разберем как точно това изявление би изглеждало, така че:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[a].$[b].arrayToDelete": "" } },
{
"arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
{ "b.arrayToDelete": { "$exists": true } },
]
}
)
Отбелязвайки там, че "b.arrayToDelete"
може да не е това, което очаквате в началото, но предвид позиционирането в "scan.$[a].$[b]
наистина би трябвало да има смисъл от b
името на елемента ще бъде достигнато чрез "точкова нотация", както е показано. И всъщност и в двата случая. Отново, $unset
така или иначе ще се прилага само за посоченото поле, така че критериите за избор наистина не са задължителни.
Ислучай 3 . Е, това е доста просто, ако ненуждате за да запазите нещо друго в масива след премахване на това съдържание (т.е. $pull
където полетата, съответстващи на това, бяха единствените неща вътре или $unset
за този въпрос ), тогава просто не се забърквайте с нищо друго и просто избършете масива .
Това е важно разграничение, ако смятате, че според точката за изясняване на това дали документите с поименуваното поле, където само елементи във вложените масиви и наистина, че посоченият ключ е бил единственият нещо, присъстващо в документите.
С мотивите, че се използва $pull
както е показано тук и при тези условия ще получите:
{
"_id" : ObjectId("5ca321909e31550a618011e6"),
"field" : "value",
"field2" : "value",
"scan" : [
[ ],
[ ],
[ ]
]
}
Или с $unset
:
{
"_id" : ObjectId("5ca322bc9e31550a618011e7"),
"field" : "value",
"field2" : "value",
"scan" : [
[{ }, { }, { }, { }],
[{ }, { }, { }, { }],
[{ }, { }, { }, { }]
]
}
И двете явно не са желателни. Така че има причина, ако arrayToDelete
полето беше самоединствено съдържание, което изобщо е било там, тогава най-логичният начин да премахнете всички е просто да замените масива с празен. Или наистина $unset
цялата собственост на документа.
Обърнете внимание обаче, че всички тези „фантастични неща“ ( с изключение на $set
разбира се ) изискват, че трябва да имате поне MongoDB 3.6 достъпно, за да използвате тази функционалност.
В случай, че все още използвате по-стара версия на MongoDB от тази (и към датата на писане, наистина не трябва да сте, тъй като официалната ви поддръжка изтича само 5 месеца от тази дата), тогава други съществуващи отговори за Как да актуализирате няколко Елементите на масива в mongodb всъщност са за вас.