findOneAndUpdate() în MongoDB & NoSQL

Operațiuni CRUD MongoDB 3.2

Actualizează un document și îl întoarce, atomic, într-un singur pas; există și findOneAndDelete și findOneAndReplace.

Sintaxă
db.colectie.findOneAndUpdate(filtru, update, { returnDocument: 'before' | 'after', sort, projection, upsert })
db.colectie.findOneAndDelete(filtru, { sort, projection })
db.colectie.findOneAndReplace(filtru, documentNou, optiuni)

Ce face findOneAndUpdate()

findOneAndUpdate() găsește un document, îl modifică și ți-l întoarce, fără ca alt client să poată interveni între pași. Implicit primești documentul de dinainte de modificare; cu returnDocument: 'after' primești versiunea nouă.

sort alege care document se modifică atunci când mai multe se potrivesc filtrului, iar projection limitează câmpurile întoarse. Modelul clasic este contorul de numere de factură: $inc + upsert + returnDocument: 'after'. findOneAndDelete() șterge documentul și ți-l întoarce, util pentru cozi simple.

Parametri

Parametru Descriere
returnDocument 'before' (implicit) sau 'after'.
sort Ordinea în care se alege documentul, dacă se potrivesc mai multe.
projection Câmpurile întoarse.
upsert Creează documentul dacă nu există.

Ce returnează

Documentul (înainte sau după modificare) sau null dacă nu s-a găsit nimic.

Exemple

Exemplele de pe această pagină pornesc de la datele de mai jos. Rulează mai întâi acest cod, apoi exemplul.

use magazin
db.comenzi.insertMany([
  { _id: 1, client: 'Ana', oras: 'Iași', total: 120, status: 'livrata', produse: ['caiet', 'pix'] },
  { _id: 2, client: 'Dan', oras: 'Cluj', total: 85, status: 'noua', produse: ['carte'] },
  { _id: 3, client: 'Ioana', oras: 'Iași', total: 240, status: 'livrata', produse: ['rucsac', 'caiet', 'pix'] },
  { _id: 4, client: 'Mihai', oras: 'Brașov', total: 60, status: 'anulata', produse: [] }
])

Exemplul 1: Preluarea celei mai vechi comenzi noi

Comanda primește statusul nou și ne este întoarsă deja modificată.

db.comenzi.findOneAndUpdate({ status: 'noua' }, { $set: { status: 'in-livrare' } }, { sort: { _id: 1 }, returnDocument: 'after' })

Ieșire în mongosh

{
  _id: 2,
  client: 'Dan',
  oras: 'Cluj',
  total: 85,
  status: 'in-livrare',
  produse: [ 'carte' ]
}

Exemplul 2: Următorul număr de factură

Contorul crește atomic, deci doi clienți nu primesc niciodată același număr. Aici contorul era deja la 1042.

db.contoare.findOneAndUpdate({ _id: 'factura' }, { $inc: { valoare: 1 } }, { upsert: true, returnDocument: 'after' })

Ieșire în mongosh

{ _id: 'factura', valoare: 1043 }

Exemplul 3: Ștergere cu întoarcerea documentului

findOneAndDelete întoarce documentul șters, ca să-l poți arhiva sau afișa.

db.comenzi.findOneAndDelete({ status: 'anulata' }, { projection: { client: 1, total: 1 } })

Ieșire în mongosh

{ _id: 4, client: 'Mihai', total: 60 }

Greșeli frecvente

  • Uiți returnDocument: 'after' și te miri că documentul întors are valorile vechi.
  • Fără sort, nu știi care document va fi ales când filtrul se potrivește mai multora.
  • Driverele mai vechi întorceau un obiect { value, ok }; mongosh și driverele actuale întorc direct documentul.

Comenzi înrudite

  • upsert: true și $setOnInsert: Cu upsert: true, updateOne actualizează documentul găsit sau, dacă nu există, îl creează; $setOnInsert scrie câmpuri doar la creare.
  • replaceOne(): Înlocuiește complet primul document găsit cu un document nou; câmpurile care nu apar în documentul nou dispar.
  • bulkWrite(): Trimite mai multe operații de scriere (inserări, actualizări, ștergeri) către o colecție într-o singură cerere.
  • $currentDate: Operatorul $currentDate pune în câmp data și ora serverului în momentul actualizării, ca Date sau ca Timestamp.