Jak zaktualizować wiele elementów tablicy w Mongodb

182

Mam dokument Mongo, który zawiera szereg elementów.

Chciałbym zresetować .handledatrybut wszystkich obiektów w tablicy, gdzie .profile= XX.

Dokument ma następującą formę:

{
    "_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
    "user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
    "events": [{
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 20,
            "data": "....."
        }
        ...
    ]
}

więc spróbowałem:

.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)

Jednak aktualizuje tylko pierwszy dopasowany element tablicy w każdym dokumencie. (Takie jest zdefiniowane zachowanie dla $ - operator pozycyjny .)

Jak mogę zaktualizować wszystkie dopasowane elementy tablicy?

LiorH
źródło
2
Aktualizacja podzbioru lub wszystkich elementów tablicy została dodana do mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…
Jaap
koniecznie sprawdź arrayFilters i zastanów się, którego zapytania użyć, aby aktualizacja była wydajna. Sprawdź odpowiedź Neila Lunna: stackoverflow.com/a/46054172/337401
Jaap
sprawdź moją odpowiedź
Ucdemir,

Odpowiedzi:

111

W tej chwili nie jest możliwe użycie operatora pozycyjnego do aktualizacji wszystkich elementów w tablicy. Zobacz JIRA http://jira.mongodb.org/browse/SERVER-1243

W ramach pracy możesz:

  • Zaktualizuj każdy element osobno (zdarzenia.0. Obsługiwane zdarzenia. 1 obsługiwane ...) lub ...
  • Przeczytaj dokument, dokonaj edycji ręcznie i zapisz go, zastępując starszy (zaznacz „Aktualizuj, jeśli bieżący”, jeśli chcesz zapewnić aktualizacje atomowe)
Javier Ferrero
źródło
15
jeśli masz podobny problem, zagłosuj na ten problem - jira.mongodb.org/browse/SERVER-1243
LiorH
Właściwie to lubię czytać dokument i zapisywać podejście. Ale użyłem Couch przed Mongo, aby to podejście wydawało się bardziej naturalne, ponieważ nie ma interfejsu API zapytań dla Couch, tylko interfejs API REST dla całych dokumentów
Adam
1
Oba te podejścia wymagają dość dużej ilości pamięci, prawda? Jeśli jest wiele dokumentów, które należy przeszukać i załadować wszystkie (lub zagnieżdżone tablice) w celu aktualizacji ... + również trochę kłopotliwe z wdrożeniem, jeśli trzeba to zrobić asynchronicznie ...
Ixx,
13
Pomijając wszystkie trudności techniczne, zdumiewające jest to, że ta funkcja nie jest dostępna w MongoDB. To ograniczenie zabiera dużą swobodę dostosowywania schematu bazy danych.
Sung Cho
5
Neil Lunn stackoverflow.com/a/46054172/337401 odpowiedział na to pytanie w wersji 3.6. Ponieważ jest to popularne pytanie, warto zaktualizować tę zaakceptowaną odpowiedź poprzez odesłanie do odpowiedzi Neila Lunna.
Jaap,
72

Wraz z wydaniem MongoDB 3.6 (i dostępnym w gałęzi programistycznej od MongoDB 3.5.12) możesz teraz aktualizować wiele elementów tablicy w jednym żądaniu.

Korzysta z filtrowanej$[<identifier>] składni operatora aktualizacji pozycyjnej wprowadzonej w tej wersji:

db.collection.update(
  { "events.profile":10 },
  { "$set": { "events.$[elem].handled": 0 } },
  { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
)

"arrayFilters"Jak przeszedł do opcji .update()lub nawet .updateOne(), .updateMany(), .findOneAndUpdate()lub .bulkWrite()metoda określa warunki do meczu na identyfikatorze podanym w instrukcji aktualizacji. Wszelkie elementy spełniające podany warunek zostaną zaktualizowane.

Zauważając, że "multi"jak podano w kontekście pytania, zastosowano w oczekiwaniu, że to „zaktualizuje wiele elementów”, ale tak nie było i nadal tak nie jest. Jego użycie tutaj odnosi się do „wielu dokumentów”, jak to zawsze miało miejsce lub teraz określono inaczej jako obowiązkowe ustawienie .updateMany()w nowoczesnych wersjach API.

UWAGA Jak na ironię, ponieważ jest to określone w argumencie „options” dla .update()podobnych metod, składnia jest ogólnie zgodna ze wszystkimi najnowszymi wersjami sterowników wersji.

Jednak nie jest to prawdą w przypadku mongopowłoki, ponieważ sposób, w jaki metoda została tam zaimplementowana („ironicznie dla kompatybilności wstecznej”), arrayFiltersargument nie jest rozpoznawany i usuwany przez wewnętrzną metodę, która analizuje opcje w celu zapewnienia „kompatybilności wstecznej” z wcześniejszym Wersje serwera MongoDB i „starsza” .update()składnia wywołania API.

Jeśli więc chcesz używać polecenia w mongopowłoce lub innych produktach opartych na powłoce (zwłaszcza Robo 3T), potrzebujesz najnowszej wersji z działu rozwoju lub wersji produkcyjnej od wersji 3.6 lub nowszej.

Zobacz także, positional all $[]która aktualizuje również „wiele elementów tablicy”, ale bez zastosowania się do określonych warunków i dotyczy wszystkich elementów w tablicy, w których jest to pożądane działanie.

Zobacz także Aktualizowanie zagnieżdżonej tablicy za pomocą MongoDB, aby dowiedzieć się, jak te nowe operatory pozycyjne stosują się do „zagnieżdżonych” struktur tablicowych, gdzie „tablice znajdują się w innych tablicach”.

WAŻNE - Uaktualnione instalacje z poprzednich wersji „mogły” nie włączyć funkcji MongoDB, co może również powodować błędy instrukcji. Powinieneś upewnić się, że procedura aktualizacji jest kompletna ze szczegółami, takimi jak aktualizacje indeksu, a następnie uruchomić

   db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )

Lub wyższa wersja, która ma zastosowanie do zainstalowanej wersji. tj. "4.0"dla wersji 4 i nowszych obecnie. Umożliwiło to takie funkcje, jak nowe operatory aktualizacji pozycji i inne. Możesz również sprawdzić za pomocą:

   db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

Aby przywrócić bieżące ustawienie

Neil Lunn
źródło
9
Przyjęta odpowiedź powinna zostać zaktualizowana i odnosić się do tej odpowiedzi.
Jaap,
2
Co to jest elem?
user1063287,
1
To jest poprawne. Pamiętaj, że RoboMongo nie obsługuje arrayFiltersjeszcze, więc uruchom aktualizację przez CLI. stackoverflow.com/questions/48322834/…
drlff 28.09.2018
dziękuję Neil, szczególnie za
WAŻNĄ
ten kod zwraca BŁĄD w pymongo. jest błąd: podnieść TypeError („% s musi być prawdziwy lub fałszywy”% (opcja,)) TypeError: upsert musi być prawdziwy lub fałszywy
Vagif
67

Dla mnie zadziałało to:

db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
  .forEach(function (doc) {
    doc.events.forEach(function (event) {
      if (event.profile === 10) {
        event.handled=0;
      }
    });
    db.collection.save(doc);
  });

Myślę, że jest to bardziej zrozumiałe dla początkujących mongo i każdego, kto zna JQuery i przyjaciół.

Daniel Cerecedo
źródło
Używam db.posts.find({ 'permalink':permalink }).forEach( function(doc) {...i dostajęOops.. TypeError: Object # has no method 'forEach'
Squirrl
3
@Squirrl może być nieaktualną wersją mongodb? Dokument wyjaśnia, jak zastosować funkcję „Każda” na kursorze, ale nie określa, która wersja jest obsługiwana. docs.mongodb.org/manual/reference/method/cursor.forEach
Daniel Cerecedo
@Squirrl trydb.posts.find(...).toArray().forEach(...)
marmor 12.12.16
Czy nie możemy tego zrobić bez użycia Javascript? Chcę wykonać tę aktualizację bezpośrednio z powłoki mongo bez użycia JavaScript API.
Meliodas,
1
Czy możesz napisać to zapytanie w sterowniku Mongodb dla Javy lub w Spring-Data-Mongodb? Dzięki, kris
chiku,
18

Można to również osiągnąć za pomocą pętli while, która sprawdza, czy pozostały jakieś dokumenty, które nadal mają poddeklarowane dokumenty, które nie zostały zaktualizowane. Ta metoda zachowuje atomowość twoich aktualizacji (czego nie ma wiele innych rozwiązań tutaj).

var query = {
    events: {
        $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
        }
    }
};

while (db.yourCollection.find(query).count() > 0) {
    db.yourCollection.update(
        query,
        { $set: { "events.$.handled": 0 } },
        { multi: true }
    );
}

Liczba przypadków wykonania pętli będzie równa maksymalnej liczbie przypadków, w których dokumenty podrzędne o wartości profilerównej 10, a handlednie równej 0, wystąpią w dowolnym dokumencie w kolekcji. Jeśli więc masz w swojej kolekcji 100 dokumentów, a jeden z nich ma trzy pasujące dokumenty podrzędne, querya wszystkie pozostałe dokumenty mają mniej pasujących dokumentów podrzędnych, pętla zostanie wykonana trzy razy.

Ta metoda pozwala uniknąć ryzyka zablokowania innych danych, które mogą być aktualizowane przez inny proces podczas wykonywania tego skryptu. Minimalizuje również ilość danych przesyłanych między klientem a serwerem.

Sean
źródło
13

W rzeczywistości odnosi się to do długotrwałego problemu na stronie http://jira.mongodb.org/browse/SERVER-1243, gdzie w rzeczywistości istnieje wiele wyzwań dla jasnej składni, która obsługuje „wszystkie przypadki”, w których możliwe jest dopasowanie wielu tablic znaleziony. W rzeczywistości istnieją już metody „pomocy” w rozwiązaniu tego problemu, takie jak operacje masowe, które zostały wdrożone po tym oryginalnym poście.

Nadal nie jest możliwe zaktualizowanie więcej niż jednego dopasowanego elementu tablicy w pojedynczej instrukcji aktualizacji, więc nawet przy aktualizacji „wielu” wszystko, co kiedykolwiek będziesz w stanie zaktualizować, to tylko jeden dopasowany element w tablicy dla każdego dokumentu w tym pojedynczym komunikat.

Obecnie najlepszym możliwym rozwiązaniem jest znalezienie i zapętlenie wszystkich dopasowanych dokumentów oraz przetworzenie aktualizacji zbiorczych, co pozwoli przynajmniej na wysłanie wielu operacji w jednym żądaniu z pojedynczą odpowiedzią. Opcjonalnie możesz użyć, .aggregate()aby zmniejszyć zawartość tablicy zwróconej w wyniku wyszukiwania do tych, które pasują do warunków wyboru aktualizacji:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$project": {
        "events": {
            "$setDifference": [
               { "$map": {
                   "input": "$events",
                   "as": "event",
                   "in": {
                       "$cond": [
                           { "$eq": [ "$$event.handled", 1 ] },
                           "$$el",
                           false
                       ]
                   }
               }},
               [false]
            ]
        }
    }}
]).forEach(function(doc) {
    doc.events.forEach(function(event) {
        bulk.find({ "_id": doc._id, "events.handled": 1  }).updateOne({
            "$set": { "events.$.handled": 0 }
        });
        count++;

        if ( count % 1000 == 0 ) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();
        }
    });
});

if ( count % 1000 != 0 )
    bulk.execute();

.aggregate()Część nie będzie działać, gdy jest „Unikalny” identyfikator tablicy lub całej zawartości każdego elementu tworzy „niepowtarzalną” samego elementu. Wynika to z operatora „set” $setDifferenceużywanego do filtrowania wszelkich falsewartości zwracanych z $mapoperacji użytej do przetworzenia tablicy pod kątem dopasowań.

Jeśli zawartość tablicy nie zawiera unikalnych elementów, możesz spróbować zastosować alternatywne podejście z $redact:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$redact": {
        "$cond": {
            "if": {
                "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
            },
            "then": "$$DESCEND",
            "else": "$$PRUNE"
        }
    }}
])

Ograniczeniem jest to, że jeśli „obsłużone” było w rzeczywistości polem przeznaczonym do obecności na innych poziomach dokumentu, prawdopodobnie uzyskasz nieoczekiwane wyniki, ale jest dobrze, gdy to pole pojawia się tylko w jednej pozycji dokumentu i jest równe.

Przyszłe wersje (po 3.1 MongoDB) w momencie pisania będą miały $filterprostszą operację:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$project": {
        "events": {
            "$filter": {
                "input": "$events",
                "as": "event",
                "cond": { "$eq": [ "$$event.handled", 1 ] }
            }
        }
    }}
])

We wszystkich wersjach, które obsługują, .aggregate()można stosować następujące podejście $unwind, ale użycie tego operatora sprawia, że ​​jest to najmniej wydajne podejście ze względu na rozszerzenie tablicy w potoku:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$unwind": "$events" },
    { "$match": { "events.handled": 1 } },
    { "$group": {
        "_id": "$_id",
        "events": { "$push": "$events" }
    }}        
])

We wszystkich przypadkach, w których wersja MongoDB obsługuje „kursor” ze zbiorczych danych wyjściowych, jest to tylko kwestia wyboru podejścia i iteracji wyników z tym samym blokiem kodu pokazanym w celu przetworzenia instrukcji aktualizacji zbiorczej. Operacje zbiorcze i „kursory” z agregowanych danych wyjściowych są wprowadzane w tej samej wersji (MongoDB 2.6), a zatem zwykle działają równolegle do przetwarzania.

W nawet wcześniejszych wersjach prawdopodobnie najlepiej użyć po prostu, .find()aby zwrócić kursor i odfiltrować wykonanie instrukcji tylko tyle razy, ile element tablicy jest dopasowany do .update()iteracji:

db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
    doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
        db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
    });
});

Jeśli jesteś bezwzględnie zdeterminowany do wykonywania „wielu” aktualizacji lub uważasz, że jest to ostatecznie bardziej wydajne niż przetwarzanie wielu aktualizacji dla każdego dopasowanego dokumentu, zawsze możesz określić maksymalną liczbę możliwych dopasowań tablic i po prostu wykonać aktualizację „wielu” razy, aż w zasadzie nie ma już żadnych dokumentów do zaktualizowania.

Prawidłowe podejście do wersji MongoDB 2.4 i 2.2 może również posłużyć .aggregate()do znalezienia tej wartości:

var result = db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$unwind": "$events" },
    { "$match": { "events.handled": 1 } },
    { "$group": {
        "_id": "$_id",
        "count": { "$sum": 1 }
    }},
    { "$group": {
        "_id": null,
        "count": { "$max": "$count" }
    }}
]);

var max = result.result[0].count;

while ( max-- ) {
    db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}

Niezależnie od przypadku, istnieją pewne rzeczy, których nie chcesz robić w ramach aktualizacji:

  1. Nie „jednym strzałem” aktualizuj tablicę: jeśli uważasz, że bardziej wydajna może być aktualizacja całej zawartości tablicy w kodzie, a następnie tylko $setcałej tablicy w każdym dokumencie. Może się to wydawać szybsze w przetwarzaniu, ale nie ma gwarancji, że zawartość tablicy nie zmieniła się od czasu jej odczytania i przeprowadzenia aktualizacji. Chociaż $setnadal jest operatorem atomowym, zaktualizuje tablicę tylko o to, co „uważa” za poprawne dane, a zatem prawdopodobnie zastąpi wszelkie zmiany zachodzące między odczytem a zapisem.

  2. Nie obliczaj wartości indeksu do aktualizacji: tam, gdzie jest to podobne do podejścia „jednego strzału”, po prostu ustal, że pozycja 0i pozycja 2(i tak dalej) są elementami do aktualizacji i zakodowania ich za pomocą i ewentualne stwierdzenie takie jak:

    { "$set": {
        "events.0.handled": 0,
        "events.2.handled": 0
    }}
    

    Ponownie problemem jest tutaj „domniemanie”, że wartości indeksu znalezione podczas odczytu dokumentu są tymi samymi wartościami indeksu w tablicy w momencie aktualizacji. Jeśli nowe elementy zostaną dodane do tablicy w sposób zmieniający kolejność, pozycje te nie będą już ważne, a niewłaściwe elementy zostaną w rzeczywistości zaktualizowane.

Tak więc, dopóki nie zostanie ustalona rozsądna składnia umożliwiająca przetwarzanie wielu dopasowanych elementów tablicy w pojedynczej instrukcji aktualizacji, wówczas podstawowym podejściem jest albo aktualizacja każdego dopasowanego elementu tablicy w pojedynczej instrukcji (najlepiej luzem), albo zasadniczo wypracowanie maksymalnych elementów tablicy aktualizować lub aktualizować, dopóki nie zostaną zwrócone żadne zmodyfikowane wyniki. W każdym razie powinieneś „zawsze” przetwarzać aktualizacje pozycyjne$ na dopasowanym elemencie tablicy, nawet jeśli aktualizuje to tylko jeden element na instrukcję.

Operacje zbiorcze są w rzeczywistości „uogólnionym” rozwiązaniem przetwarzania wszelkich operacji, które okazują się być „wieloma operacjami”, a ponieważ jest do tego więcej aplikacji niż tylko aktualizowanie elementów tablicy o tej samej wartości, to oczywiście zostało ono zaimplementowane już teraz i jest to obecnie najlepsze podejście do rozwiązania tego problemu.

Blakes Seven
źródło
8

Dziwię się, że wciąż nie rozwiązano tego problemu w mongo. Ogólnie mongo nie wydaje się świetne, gdy mamy do czynienia z pod-tablicami. Na przykład nie można liczyć pod-tablic.

Użyłem pierwszego rozwiązania Javiera. Wczytaj tablicę do zdarzeń, następnie zapętl i zbuduj zestaw exp:

var set = {}, i, l;
for(i=0,l=events.length;i<l;i++) {
  if(events[i].profile == 10) {
    set['events.' + i + '.handled'] = 0;
  }
}

.update(objId, {$set:set});

Można to przekształcić w funkcję za pomocą wywołania zwrotnego dla testu warunkowego

lukenof Ponadto
źródło
Dzięki za to! Nie mogę uwierzyć, że ta funkcja nie jest natywnie obsługiwana! Służyło to do zwiększania każdego elementu podtablicy, dla innych czytających ... aby zaktualizować każdy element, wystarczy usunąć instrukcję if.
Zaheer,
9
To nie jest bezpieczne rozwiązanie. Jeśli rekord zostanie dodany podczas uruchamiania aktualizacji, nastąpi uszkodzenie danych.
Merc
4

Szukałem rozwiązania tego problemu przy użyciu najnowszego sterownika dla C # 3.6 i oto poprawka, na którą ostatecznie się zdecydowałem. Kluczem jest tutaj użycie „$ []”, co według MongoDB jest nowe od wersji 3.6. Zobacz https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up. S [] po więcej informacji.

Oto kod:

{
   var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
   var update = Builders<Scene>.Update.Unset("area.$[].discoveredBy");
   var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
}

Aby uzyskać więcej informacji, zobacz mój oryginalny post tutaj: Usuń element tablicy ze WSZYSTKICH dokumentów za pomocą sterownika MongoDB C #

C0d3 0n3
źródło
4

Wątek jest bardzo stary, ale przyszedłem tu poszukać odpowiedzi, dzięki czemu dostarczyłem nowe rozwiązanie.

W MongoDB w wersji 3.6+ możliwe jest teraz użycie operatora pozycyjnego do aktualizacji wszystkich elementów w tablicy. Zobacz oficjalną dokumentację tutaj .

Poniższe zapytanie będzie działać dla zadanego tutaj pytania. Sprawdziłem również sterownik Java-MongoDB i działa on pomyślnie.

.update(   // or updateMany directly, removing the flag for 'multi'
   {"events.profile":10},
   {$set:{"events.$[].handled":0}},  // notice the empty brackets after '$' opearor
   false,
   true
)

Mam nadzieję, że to pomaga komuś jak ja.

ersnh
źródło
1

Próbowałem następujące i działa dobrze.

.update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);

// funkcja zwrotna w przypadku nodejs

Pranay Saha
źródło
Ten kod po prostu aktualizuje pierwszy dopasowany element w tablicy. Zła odpowiedź.
Hamlett,
Działa dla v2.6. Możesz być w starszej wersji? Oto dokument docs.mongodb.com/manual/reference/method/db.collection.update/…
Jialin Zou
1

Możesz zaktualizować wszystkie elementy w MongoDB

db.collectioname.updateOne(
{ "key": /vikas/i },
{ $set: { 
 "arr.$[].status" : "completed"
} }
)

Zaktualizuje całą wartość „status” do „complete” w tablicy „arr”

Jeśli tylko jeden dokument

db.collectioname.updateOne(
 { key:"someunique", "arr.key": "myuniq" },
 { $set: { 
   "arr.$.status" : "completed", 
   "arr.$.msgs":  {
                "result" : ""
        }
   
 } }
)

Ale jeśli nie jeden, a także nie chcesz, aby wszystkie dokumenty w tablicy były aktualizowane, musisz zapętlić element i wewnątrz bloku if

db.collectioname.find({findCriteria })
  .forEach(function (doc) {
    doc.arr.forEach(function (singlearr) {
      if (singlearr check) {
        singlearr.handled =0
      }
    });
    db.collection.save(doc);
  });
VIKAS KOHLI
źródło
0

W rzeczywistości polecenie Zapisz działa tylko na instancji klasy Dokument. Mają wiele metod i atrybutów. Możesz więc użyć funkcji lean (), aby zmniejszyć obciążenie pracą. Zobacz tutaj. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j

Kolejny problem z funkcją zapisu, która spowoduje, że dane konfliktowe będą jednocześnie zapisywane w trybie wielokrotnego zapisu. Model.Update sprawi, że dane będą spójne. Aby zaktualizować wiele elementów w szeregu dokumentów. Użyj swojego znanego języka programowania i spróbuj czegoś takiego, używam mangusty w tym:

User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
  .then(usr =>{
    if(!usr)  return
    usr.events.forEach( e => {
      if(e && e.profile==10 ) e.handled = 0
    })
    User.findOneAndUpdate(
      {'_id': '4d2d8deff4e6c1d71fc29a07'},
      {$set: {events: usr.events}},
      {new: true}
    ).lean().exec().then(updatedUsr => console.log(updatedUsr))
})
użytkownik3176403
źródło
0

Operator $ [] wybiera wszystkie zagnieżdżone tablice. Możesz zaktualizować wszystkie elementy tablicy za pomocą „$ []”

.update({"events.profile":10},{$set:{"events.$[].handled":0}},false,true)

Odniesienie

Ucdemir
źródło
Czy możesz wyjaśnić, dlaczego umieściłeś tutaj „fałsz, prawda” na końcu? Nie mogę tego znaleźć w dokumentacji.
Garson
Niepoprawna odpowiedź: operator wszystkich pozycji $[] tylko aktualizuje wszystkie pola w podanej tablicy. Działa filtrowany operator pozycyjny, $[identifier]który operuje na polach tablic spełniających określone warunki. Należy używać z RefrencearrayFilters : docs.mongodb.com/manual/release-notes/3.6/#arrayfilters i docs.mongodb.com/manual/reference/operator/update/…
Lawrence Eagles
0

Należy pamiętać, że niektóre odpowiedzi w tym wątku sugerujące użycie $ [] są NIEPRAWIDŁOWE.

db.collection.update(
   {"events.profile":10},
   {$set:{"events.$[].handled":0}},
   {multi:true}
)

Powyższy kod zaktualizuje „obsługiwane” do 0 dla wszystkich elementów w tablicy „zdarzeń”, niezależnie od wartości „profilu”. Zapytanie {"events.profile":10}służy tylko do filtrowania całego dokumentu, a nie dokumentów w tablicy. W tej sytuacji należy użyć $[elem]z, arrayFiltersaby określić stan elementów tablicy, aby odpowiedź Neila Lunna była poprawna.

Wenda Hu
źródło
0

Zaktualizuj pole tablicy w wielu dokumentach w bazie danych Mongo.

Użyj $ pull lub $ push z aktualizacją wielu zapytań, aby zaktualizować elementy tablicy w mongoDb.

Notification.updateMany(
    { "_id": { $in: req.body.notificationIds } },
    {
        $pull: { "receiversId": req.body.userId }
    }, function (err) {
        if (err) {
            res.status(500).json({ "msg": err });
        } else {
            res.status(200).json({
                "msg": "Notification Deleted Successfully."
            });
        }
    });
Irfan
źródło
0

Po pierwsze: twój kod nie działał, ponieważ korzystałeś z operatora pozycyjnego, $który identyfikuje tylko element do aktualizacji w tablicy, ale nawet nie określa wprost jego pozycji w tablicy.

Potrzebujesz filtrowanego operatora pozycyjnego $[<identifier>]. Zaktualizuje wszystkie elementy, które pasują do warunku filtru tablicowego.

Rozwiązanie:

db.collection.update({"events.profile":10}, { $set: { "events.$[elem].handled" : 0 } },
   {
     multi: true,
     arrayFilters: [ { "elem.profile": 10 } ]
})

Odwiedź dokument mongodb tutaj

Co robi kod:

  1. {"events.profile":10} filtruje Twoją kolekcję i zwraca dokumenty pasujące do filtra

  2. $setOperator zmiana: modyfikuje dopasowanie pól dokumentów działa dalej.

  3. {multi:true}Dokonuje .update()modyfikacji wszystkich dokumentów zgodnych z filtrem, a tym samym zachowuje się jakupdateMany()

  4. { "events.$[elem].handled" : 0 } and arrayFilters: [ { "elem.profile": 10 } ] Ta technika wymaga użycia filtrowanej tablicy pozycyjnej z arrayFilters. filtrowana tablica pozycyjna $[elem]działa tutaj jako symbol zastępczy dla wszystkich elementów w polach tablicy, które spełniają warunki określone w filtrze tablicy.

Filtry tablicowe

Lawrence Eagles
źródło