Obecný rozsah a vysvětlení
Na tom, co tady děláte, je několik věcí špatně. Nejprve podmínky vašeho dotazu. Máte na mysli několik _id
hodnoty, kde byste to neměli potřebovat a z nichž alespoň jedna není na nejvyšší úrovni.
Abychom se dostali do "vnořené" hodnoty a také za předpokladu, že _id
hodnota je jedinečná a neobjeví se v žádném jiném dokumentu, formulář dotazu by měl vypadat takto:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Teď by to skutečně fungovalo, ale ve skutečnosti je to jen náhoda, protože existují velmi dobré důvody, proč by to nemělo fungovat u vás.
Důležitý údaj je v oficiální dokumentaci pro poziční $
operátora pod předmětem "Vnořená pole". Toto říká:
Poziční operátor $ nelze použít pro dotazy, které procházejí více než jedním polem, jako jsou dotazy, které procházejí pole vnořená do jiných polí, protože zástupný symbol $ je nahrazen jedinou hodnotou
Konkrétně to znamená, že prvek, který bude spárován a vrácen v pozičním zástupném symbolu, je hodnota indexu z prvního odpovídající pole. To ve vašem případě znamená odpovídající index na poli "nejvyšší" úrovně.
Pokud se tedy podíváte na zobrazený zápis dotazu, „napevno“ jsme zakódovali první (nebo 0 index ) pozici v poli nejvyšší úrovně a náhodou se stane, že odpovídající prvek v "array2" je také nulovou položkou indexu.
Chcete-li to demonstrovat, můžete změnit odpovídající _id
hodnotu na "124" a výsledek bude $push
nový záznam do prvku s _id
"123", protože oba jsou v nulové položce indexu "array1" a to je hodnota vrácená zástupnému symbolu.
Takže to je obecný problém s vnořením polí. Můžete odstranit jednu z úrovní a stále budete moci $push
na správný prvek ve vašem „top“ poli, ale stále by existovalo několik úrovní.
Pokuste se vyhnout vnoření polí, protože narazíte na problémy s aktualizací, jak je znázorněno.
Obecným případem je „zploštit“ věci, o kterých si „myslíte“, že jsou „úrovněmi“, a ve skutečnosti vytvořit „atributy“ tezí na konečných položkách detailů. Například „zploštělá“ forma struktury v otázce by měla být něco jako:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Nebo dokonce i při přijetí vnitřního pole je $push
pouze a nikdy neaktualizováno:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Což se oba hodí k atomovým aktualizacím v rámci pozičního $
operátor
MongoDB 3.6 a vyšší
Od MongoDB 3.6 jsou k dispozici nové funkce pro práci s vnořenými poli. Toto používá pozičně filtrovaný $[<identifier>]
syntaxi, aby odpovídaly konkrétním prvkům a aplikovaly různé podmínky prostřednictvím arrayFilters
v prohlášení o aktualizaci:
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"
jak je předáno možnostem pro .update()
nebo dokonce .updateOne()
, .updateMany()
, .findOneAndUpdate()
nebo .bulkWrite()
metoda určuje podmínky pro shodu s identifikátorem uvedeným v příkazu aktualizace. Všechny prvky, které odpovídají dané podmínce, budou aktualizovány.
Protože je struktura "vnořená", ve skutečnosti používáme "více filtrů", jak je specifikováno u "pole" definic filtrů, jak je znázorněno. Označený "identifikátor" se používá při porovnávání s pozičně filtrovaným $[<identifier>]
syntaxe skutečně použitá v bloku aktualizace příkazu. V tomto případě inner
a outer
jsou identifikátory používané pro každou podmínku, jak je uvedeno u vnořeného řetězce.
Toto nové rozšíření umožňuje aktualizaci obsahu vnořených polí, ale ve skutečnosti nepomáhá s praktičností „dotazování“ takových dat, takže platí stejná upozornění, jak bylo vysvětleno dříve.
Obvykle skutečně „chcete“ vyjádřit jako „atributy“, i když si váš mozek zpočátku myslí „vnoření“, je to obvykle jen reakce na to, jak si myslíte, že se „předchozí vztahové části“ spojují. Ve skutečnosti opravdu potřebujete více denormalizace.
Podívejte se také na Jak aktualizovat více prvků pole v mongodb, protože tyto nové aktualizační operátory ve skutečnosti odpovídají a aktualizují „více prvků pole“ spíše než jen první , což byla předchozí akce aktualizací polohy.
POZNÁMKA Poněkud ironicky, protože je to uvedeno v argumentu "options" pro
.update()
a podobně jako u metod je syntaxe obecně kompatibilní se všemi nejnovějšími verzemi ovladačů.To však neplatí pro
mongo
shell, protože způsob, jakým je tam metoda implementována ( "ironicky pro zpětnou kompatibilitu"),arrayFilters
argument není rozpoznán a odstraněn interní metodou, která analyzuje možnosti, aby byla zajištěna "zpětná kompatibilita" s předchozími verzemi serveru MongoDB a "starší".update()
Syntaxe volání API.Pokud tedy chcete použít příkaz v
mongo
shell nebo jiné "shell based" produkty (zejména Robo 3T) potřebujete nejnovější verzi buď z vývojové větve nebo z produkčního vydání od 3.6 nebo vyšší.
Viz také positional all $[]
který také aktualizuje "multiple array elements", ale bez použití na zadané podmínky a platí pro všechny prvků v poli, kde je to požadovaná akce.