分享到

簡介

MongoDB是一種基於文件的NoSQL資料庫,資料以JSON文件組成的集合形式組織。與任何資料庫一樣,MongoDB也有一種使用者可以用來訪問資料的語言。在MongoDB中,這種語言就是MongoDB查詢語言,簡稱MQL。無論是MQL還是SQL,資料庫查詢都可以從簡單開始,但隨著資料庫規模的擴大,會出現更復雜的查詢。

MongoDB聚合框架中,查詢MongoDB文件的方式分解了這些更復雜的查詢。它將複雜邏輯分離為順序操作。在本指南中,我們將介紹MongoDB聚合框架,討論常見的聚合階段,並透過一個簡單的聚合管道示例作為結尾。

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 documents
matching the specified condition to pass to the next stage.
{
$match:
{
genre: "Fiction"
}
},
// Stage 2: The $project operator specifies which fields
in 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 expression
and 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的聚合框架可以實現比我們在書店示例中展示的更多功能,我們希望本介紹能引導你進一步探索。

關於作者
Alex Emerich

Alex Emerich

亞歷克斯是一個典型的愛觀鳥、愛嘻哈的“書呆子”,也喜歡撰寫關於資料庫的文章。他目前住在柏林,在那裡你可以看到他像利奧波德·布魯姆一樣漫無目的地在城市中漫步。
© . This site is unofficial and not affiliated with Prisma Data, Inc.