$out / $merge în MongoDB & NoSQL

Agregare & Analytics MongoDB 2.6

Etapele $out și $merge salvează rezultatul unei agregări într-o colecție: $out o înlocuiește complet, $merge o actualizează.

Sintaxă
{ $out: <colectie> }
{ $merge: { into: <colectie>, on: <camp>, whenMatched: 'replace' | 'merge' | 'keepExisting' | 'fail' | [ ... ], whenNotMatched: 'insert' | 'discard' | 'fail' } }

Ce face $out / $merge

$out (MongoDB 2.6+) scrie rezultatul agregării într-o colecție, înlocuind complet conținutul ei. Este util pentru rapoarte recalculate integral, de exemplu o dată pe noapte.

$merge (MongoDB 4.2+) combină rezultatul cu o colecție existentă, document cu document: pe baza câmpului on (implicit _id) decide ce face când găsește un document (whenMatched) și când nu găsește (whenNotMatched). Așa poți actualiza incremental statistici, fără să recalculezi tot. Ambele trebuie să fie ultima etapă din pipeline și nu întorc documente în mongosh.

Parametri

Parametru Descriere
$out Numele colecției care va fi înlocuită.
into Colecția destinație pentru $merge (poate fi și în altă bază: { db, coll }).
on Câmpul (sau câmpurile) de potrivire; are nevoie de index unic.
whenMatched 'merge' (implicit), 'replace', 'keepExisting', 'fail' sau un pipeline.
whenNotMatched 'insert' (implicit), 'discard', 'fail'.

Ce returnează

Nimic în mongosh (cursor gol); rezultatul se citește apoi din colecția destinație.

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: Raport complet recalculat cu $out

Prima comandă nu afișează nimic; raportul se citește din colecția nouă.

db.comenzi.aggregate([{ $group: { _id: '$oras', total: { $sum: '$total' } } }, { $out: 'vanzari_pe_oras' }])
db.vanzari_pe_oras.find().sort({ total: -1 })

Ieșire în mongosh

magazin> db.comenzi.aggregate([{ $group: { _id: '$oras', total: { $sum: '$total' } } }, { $out: 'vanzari_pe_oras' }])
magazin> db.vanzari_pe_oras.find().sort({ total: -1 })
[
  { _id: 'Iași', total: 360 },
  { _id: 'Cluj', total: 85 },
  { _id: 'Brașov', total: 60 }
]

Exemplul 2: Statistici pe client actualizate cu $merge

Documentele existente sunt îmbinate, cele noi sunt inserate; restul colecției rămâne.

db.comenzi.aggregate([{ $group: { _id: '$client', totalCumparat: { $sum: '$total' } } }, { $merge: { into: 'statistici_clienti', whenMatched: 'merge', whenNotMatched: 'insert' } }])
db.statistici_clienti.findOne({ _id: 'Ana' })

Ieșire în mongosh

magazin> db.comenzi.aggregate([{ $group: { _id: '$client', totalCumparat: { $sum: '$total' } } }, { $merge: { into: 'statistici_clienti', whenMatched: 'merge', whenNotMatched: 'insert' } }])
magazin> db.statistici_clienti.findOne({ _id: 'Ana' })
{ _id: 'Ana', totalCumparat: 120 }

Greșeli frecvente

  • $out șterge tot ce era în colecția destinație. Dacă vrei să păstrezi datele vechi, folosește $merge.
  • La $merge cu alt câmp decât _id în on, colecția destinație trebuie să aibă un index unic pe acel câmp.

Comenzi înrudite

  • $group: Etapa $group grupează documentele după o cheie și calculează totaluri: $sum, $avg, $min, $max, $push, $addToSet.
  • $addFields / $set: Etapa $addFields (alias $set) adaugă câmpuri calculate sau suprascrie câmpuri existente, păstrând restul documentului.
  • createIndex() cu unique: true: Un index unic garantează că două documente nu pot avea aceeași valoare într-un câmp, de exemplu același email.