consdata.com
Blog techniczny Blog biznesowy Dział HR
EN
mongodb

Czy wiesz, że wynikiem zapytania w MongoDB możesz zasilić bezpośrednio inną kolekcję?

author Piotr Stachowiak
5 kwietnia 2024

MongoDB w wersji 4.2 udostępnił nowy etap potoku agregacji (ang. aggregation pipeline) - $merge. Pozwala on na zapisanie wyniku agregacji do określonej kolekcji.

Etap $merge:

  • Może wyprowadzać dane do kolekcji w tej samej lub innej bazie danych,
  • utworzyć nową kolekcję, jeśli kolekcja jeszcze nie istnieje,
  • może merge’ować wyniki do istniejącej kolekcji.
{ 
    $merge: {
        into: <collection> -or- { db: <db>, coll: <collection> },
        on: <identifier field> -or- [ <identifier field1>, ...],
        let: <variables>,
        whenMatched: <replace|keepExisting|merge|fail|pipeline>,
        whenNotMatched: <insert|discard|fail>
    }
}

Etap $merge zawsze musi być ostatnim etapem agregacji. Możemy określić następujące parametry:

  • into - kolekcja, do której chcemy zapisać wynik; może to być kolekcja w innej bazie np.: into: { db:"myDB", coll:"myOutput" },
  • on - pole lub pola, które działają jako unikalny identyfikator dokumentu. Identyfikator określa, czy dokument wynikowy pasuje do istniejącego dokumentu w kolekcji wyjściowej. Domyślnie _id, jeśli wskażemy inne pola, to musi być na nich unikalny indeks,
  • let - zmienne wykorzystywane w whenMatched,
  • whenMatched - rodzaj zachowania, gdy w kolekcji wyjściowej znajduje się taki sam dokument (dopasowany po on),
  • whenNotMatched - rodzaj zachowania, gdy w kolekcji wyjściowej nie znaleziono dokumentu o takim samym identyfikatorze.

Dzięki $merge możemy tworzyć zmaterializowane widoki na żądanie (ang. On-Demand Materialized View). Ciekawy przykład wprost z dokumentacji MongoDB:

Mamy 2 kolekcje:

db.purchaseorders.insertMany([
   {_id: 1, quarter: "2019Q1", region: "A", qty: 200, reportDate: new Date("2019-04-01")},
   {_id: 2, quarter: "2019Q1", region: "B", qty: 300, reportDate: new Date("2019-04-01")},
   {_id: 3, quarter: "2019Q1", region: "C", qty: 700, reportDate: new Date("2019-04-01")},
   {_id: 4, quarter: "2019Q2", region: "B", qty: 300, reportDate: new Date("2019-07-01")},
   {_id: 5, quarter: "2019Q2", region: "C", qty: 1000, reportDate: new Date("2019-07-01")},
   {_id: 6, quarter: "2019Q2", region: "A", qty: 400, reportDate: new Date("2019-07-01")}
])

db.reportedsales.insertMany([
   {_id: 1, quarter: "2019Q1", region: "A", qty: 400, reportDate: new Date("2019-04-02")},
   {_id: 2, quarter: "2019Q1", region: "B", qty: 550, reportDate: new Date("2019-04-02")},
   {_id: 3, quarter: "2019Q1", region: "C", qty: 1000, reportDate: new Date("2019-04-05")},
   {_id: 4, quarter: "2019Q2", region: "B", qty: 500, reportDate: new Date("2019-07-02")}
])

Potrzebujemy jednak raportu, który przedstawi nam sumaryczną sprzedaż i kupno dla poszczególnych kwartałów. Dane te będą nam często potrzebne, więc dobrze będzie je zapisać. Wykorzystamy do tego operator $merge.

db.purchaseorders.aggregate([
   {$group: {_id: "$quarter", purchased: {$sum: "$qty"}}},
   {$merge: {into: "quarterlyreport", on: "_id",  whenMatched: "merge", whenNotMatched: "insert"}}
])

Grupujemy wartości po czym zapisujemy je w kolekcji quarterlyreport. Po takiej operacji otrzymujemy kolekcję quarterlyreport:

{"_id": "2019Q2", "purchased": 1700}
{"_id": "2019Q1", "purchased": 1200}

Następnie przeprowadzamy analogiczną operację dla sprzedaży:

db.reportedsales.aggregate([
   {$group: {_id: "$quarter", sales: {$sum: "$qty"}}},
   {$merge: {into: "quarterlyreport", on: "_id", whenMatched: "merge", whenNotMatched: "insert"}}
])

W wyniku otrzymujemy kolekcję, która służy nam za widok ze zagregowanymi danymi. Warto zauważyć, że w wynikowej kolekcji dokumenty zostały zmergowane, a nie zastąpione:

{"_id": "2019Q1", "sales": 1950, "purchased": 1200}
{"_id": "2019Q2", "sales": 500, "purchased": 1700}

Przydatne linki:

  • https://docs.mongodb.com/manual/reference/operator/aggregation/merge/
  • https://docs.mongodb.com/manual/aggregation/
Najnowsze wpisy

  • Dostępność w PDF - dokumenty bez barier
  • Czy wiesz, że z pomocą @starting-style można animować elementy z display: none za pomocą samego CSS?
  • Czy wiesz, że w Angular 17 została wprowadzona alternatywa dla *ngSwitch?
Dołącz do nas

  • SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
    B2B 19 680 - 27 220 PLN netto
  • REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
    B2B 14 950 - 21 000 PLN netto
  • ZOBACZ WSZYSTKIE OGŁOSZENIA

newsletter

techniczny

Zapisz się

Podobne wpisy

post-image
WCAG

Dostępność w PDF - dokumenty bez barier

author
Kacper Hoffman 28 kwi 2025
post-image
angular

Czy wiesz, że z pomocą @starting-style można animować elementy z display: none za pomocą samego CSS?

author
Piotr Tatarski 7 kwi 2025
post-image
angular

Czy wiesz, że w Angular 17 została wprowadzona alternatywa dla *ngSwitch?

author
Dorian Mejer 10 mar 2025
Dołącz do nas

  • SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
    B2B 19 680 - 27 220 PLN netto
  • REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
    B2B 14 950 - 21 000 PLN netto
  • ZOBACZ WSZYSTKIE OGŁOSZENIA

Zapisz się na

newsletter

techniczny

consdata.com
  • Kontakt

    • sales@consdata.com
    • +48 61 41 51 000

  • Biuro

    • K9Office
      Krysiewicza 9/14
      61-825 Poznań
      Polska

  • Rozwiązania

    • Eximee
    • Kouncil
  • Blog Dołącz do nas
Copyright © 2024 Consdata. All rights reserved. Privacy Policy & Cookies
Chcemy używać plików cookie oraz skryptów podmiotów trzecich do polepszania funkcjonowania tej strony Zgadzam się