簡介
MongoDB 是一種以文件為基礎的 NoSQL 資料庫,其中的資料是以集合的方式組織,而集合是由 JSON 文件組成。如同任何資料庫,MongoDB 也有使用者可用來存取資料的語言。以 MongoDB 來說,這種語言就是 MongoDB 查詢語言,或簡稱 MQL。無論是 MQL 或 SQL,資料庫查詢一開始可能很簡單,但隨著資料庫擴展,就會出現更複雜的查詢。
MongoDB 聚合框架是一種查詢 MongoDB 文件的方式,可以分解這些更令人困惑的查詢。它將複雜的邏輯分解為循序操作。在本指南中,我們將介紹 MongoDB 聚合框架、討論常見的聚合階段,並以一個簡單的聚合管道範例作結。
如果您正在使用 MongoDB,請查看 Prisma 的 MongoDB 連接器!您可以使用 Prisma Client 來自信地管理生產環境的 MongoDB 資料庫。
若要開始使用 MongoDB 和 Prisma,請查看我們的從頭開始入門指南,或如何新增至現有專案。
MongoDB 聚合框架如何運作?
MongoDB 聚合框架的目的是設計一個由多個階段組成的管道,以處理文件。您從集合的資料開始,在管道的每個階段之後,您會更接近最終結果,也就是所需的檔案。
每個階段都會對文件執行操作。可以執行多種操作。例如,階段可以篩選、分組,甚至計算資料的值。在每個階段之後,輸出的文件會傳遞到下一個階段,依此類推,直到沒有剩餘階段為止。
透過聚合框架,可以實現多個目標。我們將使用實際的操作語法深入探討具體範例,但在理論上,書店小說部門的分析師可以設定一個框架,根據類型或作者將購買數量分組,以告知銷售樓層。他們能夠透過新增階段來迭代查詢,直到資料正是他們正在尋找的內容。無論團隊為何,都可以從資料中獲得見解,而透過聚合管道的組成,可以更輕鬆地發現這些見解。
最常見的 MongoDB 聚合操作有哪些?
在撰寫本文時,MongoDB 框架中大約有 38 個可用的聚合階段。我們不會在本指南中深入探討所有階段,但您可以在官方 MongoDB 文件中查看完整列表。我們將花一些時間重點介紹一些也將在範例管道中使用的階段。
$project
:重新塑造串流中的每個文件,例如透過新增欄位或移除現有欄位。對於每個輸入文件,輸出一個文件。$match
:篩選文件串流,僅允許符合條件的文件未經修改地傳遞到下一個管道階段。$match
使用標準 MongoDB 查詢。對於每個輸入文件,輸出一個文件(符合條件)或零個文件(不符合條件)。$group
:依指定的識別碼表達式將輸入文件分組,並將累加器表達式(如果指定)套用至每個群組。消耗所有輸入文件,並為每個不同的群組輸出一個文件。輸出文件僅包含識別碼欄位,以及累加欄位(如果指定)。$sort
:依指定的排序鍵重新排序文件串流。僅順序變更;文件保持未修改。對於每個輸入文件,輸出一個文件。$skip
:略過前n
個文件,其中n
是指定的略過數字,並將剩餘的文件未經修改地傳遞到管道。對於每個輸入文件,輸出零個文件(對於前n
個文件)或一個文件(如果在前n
個文件之後)。$limit
:將前n
個文件未經修改地傳遞到管道,其中n
是指定的限制。對於每個輸入文件,輸出一個文件(對於前n
個文件)或零個文件(在前n
個文件之後)。$unwind
:解構輸入文件中的陣列欄位,為每個元素輸出一個文件。每個輸出文件都會將陣列取代為元素值。對於每個輸入文件,輸出n
個文件,其中n
是陣列元素的數量,空陣列則為零。
實際運作的聚合管道
為了透過實際範例將聚合具體化,我們將逐步設定一個虛構書店的管道。我們將從一些庫存訂單資料開始,並建立一個管道,採用這些原始資料並輸出哪些作者有多個訂單,以及訂購了他們書籍的多少副本。
首先,我們將一些範例訂單文件插入到 bookOrders
集合中。
db.bookOrders.insertMany ( [{ _id: 0, first_name: "Fyodor", last_name: "Dostoyevsky", book_title: 'Demons', genre: 'Fiction', quantity: 10, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 1, first_name: "Fyodor", last_name: "Dostoyevsky", book_title: 'Brothers Karamosov', genre: 'Fiction', quantity: 25, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 2, first_name: "Jacques", last_name: "Derrida", book_title: 'The Politics of Friendship', genre: 'Fiction', quantity: 5, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 3, first_name: "Charles", last_name: "Dickens", book_title: 'Tale of Two Cities', genre: 'Fiction', quantity: 6, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 4, first_name: "James", last_name: "Joyce", book_title: 'Ulysses', genre: 'Fiction', quantity: 30, date: ISODate( "2021-03-13T11:19:30Z" ) },{ _id: 5, first_name: "Henry David", last_name: "Thoreau", book_title: 'Walden', genre: 'Nonfiction', quantity: 15, date: ISODate( "2021-03-13T11:19:30Z" ) },{ _id: 6, first_name: "Virginia", last_name: "Woolf", book_title: "A Room of One's Own", genre: 'Nonfiction',quantity: 18, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 7, first_name: "Virginia", last_name: "Woolf", book_title: "Mr's Dalloway", genre: 'Fiction', quantity: 14, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 8, first_name: "Zadie", last_name: "Smith", book_title: 'White Teeth', genre: 'Fiction', quantity: 8, date: ISODate( "2022-10-21T11:19:30Z" ) },{ _id: 9, first_name: "Charles", last_name: "Dickens", book_title: 'The Old Curiousity Shop', genre: 'Fiction', quantity: 6, date: ISODate( "2022-10-21T11:19:30Z" ) }] )
現在我們的集合有一些範例文件,我們可以開始查詢了。聚合管道使用 db.<collection-name>.aggregate()
方法執行。我們的目標是設計一個查詢,傳回一份作者列表,這些作者的小說書籍總副本訂購量最多。範例聚合查詢可以在下方找到,並描述了每個階段。
db.bookOrders.aggregate ( [// Stage 1: The $match operator scans the collection for documentsmatching the specified condition to pass to the next stage.{$match:{genre: "Fiction"}},// Stage 2: The $project operator specifies which fieldsin the matched documents should pass onto the next stage.{$project:{last_name : 1,quantity : 1}},// Stage 3: The $group operator groups the documents by the specified expressionand outputs a document for each unique grouping. The _id field specifies the distinct key to group by.{$group:{_id: "$last_name",totalQuantity: { $sum: "$quantity" } }},// Stage 4: The $sort operator specifies the field(s) to sort by and the order.-1 specifies a descending order and 1 specifies ascending order.{$sort:{ totalQuantity: -1 }}] )
執行我們的聚合查詢後,我們得到以下輸出
[{ _id: 'Dostoyevsky', totalQuantity: 35 },{ _id: 'Joyce', totalQuantity: 30 },{ _id: 'Woolf', totalQuantity: 14 },{ _id: 'Dickens', totalQuantity: 12 },{ _id: 'Smith', totalQuantity: 8 },{ _id: 'Derrida', totalQuantity: 5 }]
這個範例是刻意簡化的,但它示範了聚合管道如何從某些查詢中消除一些複雜性。達到所需輸出的每個步驟都清楚地分解並劃分到一個清晰的階段中。
根據集合和文件資料結構,在建置聚合管道時,有一些最佳化需要考慮。此外,此框架可能不適用於所有複雜邏輯。這取決於具體情況。
應該指出的一個小最佳化可以在我們範例的前兩個階段中看到。通常,$match
運算子用於開始大多數管道,並且是最佳實務。但是,如果您的集合充滿非常大的文件,則建議改為從 $project
運算子開始。從 $project
開始,限制了在管道中較早階段傳遞到下一個階段的欄位數量,並減少了一些不必要的負載。
結論
在本文中,我們介紹了 MongoDB 的聚合框架。我們討論了它是什麼,以及它如何成為簡化複雜邏輯和冗長查詢的工具。聚合管道的階段將邏輯分解為易於遵循和操作的區塊。
聚合管道簡化了資料存取,了解其運作方式非常重要。MongoDB 的聚合框架可以用來做比我們在書店範例中示範的更多事情,我們希望這個簡介能引導您進一步探索。
如果您正在使用 MongoDB,請查看 Prisma 的 MongoDB 連接器!您可以使用 Prisma Client 來自信地管理生產環境的 MongoDB 資料庫。
若要開始使用 MongoDB 和 Prisma,請查看我們的從頭開始入門指南,或如何新增至現有專案。