Nie istnieje nie jest tak naprawdę lepszym rozwiązaniem, więc może z wyjaśnieniem.
Załóżmy, że mamy dokument w miejscu, które ma strukturę jak pokazać:
{
"name": "foo",
"bars": [{
"name": "qux",
"somefield": 1
}]
}
Jeśli robisz update jak to
db.foo.update(
{ "name": "foo", "bars.name": "qux" },
{ "$set": { "bars.$.somefield": 2 } },
{ "upsert": true }
)
Wtedy wszystko jest w porządku, ponieważ dopasowanie dokument był uznany. Ale jeśli zmienisz wartość "bars.name":
db.foo.update(
{ "name": "foo", "bars.name": "xyz" },
{ "$set": { "bars.$.somefield": 2 } },
{ "upsert": true }
)
Wtedy otrzymasz błąd. Jedyną rzeczą, która zmieniła się o to, że w MongoDB 2.6 i wyżej błędu jest nieco bardziej zwięzła:
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16836,
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: bars.$.somefield"
}
})
To jest lepsze pod pewnymi względami, ale naprawdę nie chce „upsert” tak. To, co chcesz zrobić, to dodać element do tablicy, w której "nazwa" obecnie nie istnieje.
Więc co naprawdę chcesz to „wynik” z próbą aktualizację bez flagi „upsert” aby sprawdzić, czy zostały naruszone jakiekolwiek dokumenty:
db.foo.update(
{ "name": "foo", "bars.name": "xyz" },
{ "$set": { "bars.$.somefield": 2 } }
)
Plonowanie w odpowiedzi:
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
Jeśli więc zmodyfikowane dokumenty to 0
, to wiesz, że chcesz wydać następującą aktualizację:
db.foo.update(
{ "name": "foo" },
{ "$push": { "bars": {
"name": "xyz",
"somefield": 2
}}
)
Naprawdę nie ma innego sposobu, aby zrobić dokładnie to, co chcesz. Ponieważ dodatki do tablicy nie są ściśle "ustawionym" typem operacji, nie można użyć funkcji $addToSet
w połączeniu z funkcjonalnością "bulk update", dzięki czemu można "kaskadować" żądania aktualizacji.
W tym przypadku wydaje się, że trzeba sprawdzić wynik lub w inny sposób zaakceptować czytanie całego dokumentu i sprawdzanie, czy zaktualizować lub wstawić nowy element tablicy w kodzie.
Tak, to właśnie mam obecnie - '$ set', po którym następuje' $ push', jeśli nie znaleziono pasujących dokumentów ... Zastanawiałem się, czy istnieje lepszy sposób na zrobienie tego. Dziękuję za wyjaśnienie! – shesek
Jak zrobić to atomowe, jeśli masz wiele procesów próbujących aktualizować w tym samym czasie. Wygląda na to, że może wystąpić warunek wyścigowy i możesz wywołać funkcję $ push więcej niż jeden raz, a skończy się na więcej niż jednym rekordzie w tablicy. Czy istnieje sposób na wstawienie nowej wartości w sposób atomowy? –
@MichaelMoser Dopiero co zobaczyłem komentarz. Ale dodając test nierówności typu '{" name ":" foo "," bars.name ": {" $ ne ":" xyz "}}' jako zapytanie, upewniasz się, że nie powielasz elementów "pushed" . –