MongoDB 的聚合框架提供強大的資料處理能力,讓開發者能有效率地進行資料分析和轉換。文章從分組聚合開始,逐步介紹如何使用 $group、$match、$project 等運算子進行資料統計、篩選和投影。接著,文章示範如何運用布林運算元進行更複雜的條件判斷,並以平均年齡篩選和左外連線等實際案例,展現聚合框架的靈活性。除了聚合框架,文章也探討 MongoDB 的索引管理,說明如何建立不同型別的索引,例如升序、降序、雜湊索引等,以及如何根據需求選擇稀疏索引或部分索引,以最佳化查詢效能並提升資料函式庫整體運作效率。
MongoDB 聚合框架實戰應用
MongoDB 的聚合框架(Aggregation Framework)提供了一種強大的資料處理工具,能夠對資料進行複雜的分析和轉換。本文將探討聚合框架的各種操作和應用場景,並透過具體範例進行詳細解說。
1. 分組與聚合運算
分組是聚合框架中的基本操作,能夠根據指定的欄位將資料分組並進行各種聚合運算。
程式碼範例
db.employees.aggregate([
{
$group: {
"_id": "$dept",
"noOfEmployee": { $sum: 1 },
"avgExp": { $avg: "$totalExp" }
}
}
]);
內容解密:
$group階段根據dept欄位進行分組。"_id": "$dept"表示根據dept欄位進行分組。"noOfEmployee": { $sum: 1 }計算每個部門的員工數量。"avgExp": { $avg: "$totalExp" }計算每個部門員工的平均工作經驗。
2. 排序與限制結果
在聚合框架中,可以對資料進行排序和限制結果,以滿足特定的查詢需求。
程式碼範例
db.employees.aggregate([
{ $match: { dept: "Admin" } },
{ $project: { "name": 1, "dept": 1 } },
{ $sort: { name: 1 } },
{ $limit: 1 }
]);
內容解密:
$match階段篩選出dept為"Admin"的員工。$project階段選擇要顯示的欄位。$sort階段根據name欄位進行升序排序。$limit階段限制結果只傳回一筆資料。
3. 布林聚合運算元
布林聚合運算元能夠在聚合框架中進行複雜的條件判斷。
程式碼範例
db.employees.aggregate([
{ $match: { dept: "Admin" } },
{
$project: {
"name": 1,
"dept": 1,
age: { $and: [{ $gt: ["$age", 30] }, { $lt: ["$age", 36] }] }
}
}
]);
內容解密:
$and運算元用於判斷age是否大於30且小於36。$gt和$lt分別用於大於和小於的比較運算。
4. 平均年齡篩選範例
假設我們需要找出平均年齡大於或等於35歲,且薪水小於或等於70000的部門。
程式碼範例
db.employees.aggregate([
{ "$match": { "salary": { "$lte": 70000 } } },
{
"$group": {
"_id": "$dept",
"average_age": { "$avg": "$age" }
}
},
{ "$match": { "average_age": { "$gte": 35 } } }
]);
內容解密:
- 第一階段
$match篩選出薪水小於或等於70000的員工。 $group階段根據部門進行分組,並計算平均年齡。- 第二階段
$match篩選出平均年齡大於或等於35的部門。
MongoDB 聚合框架應用實務
MongoDB 的聚合框架(Aggregation Framework)是一種強大的資料處理工具,能夠對資料進行複雜的處理和分析。本文將介紹 MongoDB 聚合框架的各種應用場景和實務範例。
取得特定部門的平均年齡
假設我們有一個名為 employees 的集合,包含員工的部門和年齡資訊。我們可以使用聚合框架來計算每個部門的平均年齡。
db.employees.aggregate([
{
$group: {
_id: "$department",
average_age: { $avg: "$age" }
}
}
])
內容解密:
$group階段用於分組資料,_id欄位指定了分組的依據,這裡是department欄位。average_age欄位使用$avg聚合運算元計算每個部門的平均年齡。
取得隨機樣本資料
我們可以使用 $sample 聚合階段來取得集合中的隨機樣本資料。
db.employees.aggregate([
{ $sample: { size: 1 } }
])
內容解密:
$sample階段用於取得隨機樣本資料,size引數指定了要取得的樣本數量。
移除重複檔案
假設我們有一個名為 transactions 的集合,包含交易資料。我們可以使用聚合框架來找出重複的檔案並將其移除。
var duplicates = [];
db.transactions.aggregate([
{
$group: {
_id: { cr_dr: "$cr_dr" },
dups: { $addToSet: "$_id" },
count: { $sum: 1 }
}
},
{
$match: {
count: { $gt: 1 }
}
}
], { allowDiskUse: true })
.result
.forEach(function(doc) {
doc.dups.shift();
doc.dups.forEach(function(dupId) {
duplicates.push(dupId);
})
})
db.transactions.remove({ _id: { $in: duplicates } })
內容解密:
$group階段用於分組資料,_id欄位指定了分組的依據,這裡是cr_dr欄位。dups欄位使用$addToSet聚合運算元收集每個分組中的_id。$match階段用於篩選出重複的分組, 即count大於 1 的分組。- 將重複的
_id收集到duplicates陣列中。 - 使用
remove方法移除重複的檔案。
左外連線($lookup)
我們可以使用 $lookup 聚合階段來執行左外連線操作。
let col_1 = db.collection('col_1');
let col_2 = db.collection('col_2');
col_1.aggregate([
{ $match: { "_id": 1 } },
{
$lookup: {
from: "col_2",
localField: "id",
foreignField: "id",
as: "new_document"
}
}
])
內容解密:
$lookup階段用於執行左外連線操作,from引數指定了要連線的集合。localField和foreignField引數指定了連線的條件。as引數指定了連線結果的欄位名稱。
Java 和 Spring Data MongoDB 範例
以下是一個使用 Spring Data MongoDB 執行聚合操作的範例。
try {
MongoClient mongo = new MongoClient();
DB db = mongo.getDB("so");
DBCollection coll = db.getCollection("employees");
// 相當於 $match
DBObject matchFields = new BasicDBObject();
matchFields.put("dept", "Admin");
DBObject match = new BasicDBObject("$match", matchFields);
// 相當於 $project
DBObject projectFields = new BasicDBObject();
projectFields.put("_id", 1);
projectFields.put("name", 1);
projectFields.put("dept", 1);
projectFields.put("totalExp", 1);
projectFields.put("age", 1);
projectFields.put("languages", 1);
DBObject project = new BasicDBObject("$project", projectFields);
// 相當於 $group
DBObject groupFields = new BasicDBObject("_id", "$dept");
groupFields.put("ageSet", new BasicDBObject("$addToSet", "$age"));
DBObject employeeDocProjection = new BasicDBObject("$addToSet", new BasicDBObject("totalExp", "$totalExp").append("age", "$age").append("languages", "$languages").append("dept", "$dept").append("name", "$name"));
groupFields.put("docs", employeeDocProjection);
DBObject group = new BasicDBObject("$group", groupFields);
// 相當於 $sort
DBObject sort = new BasicDBObject("$sort", new BasicDBObject("age", 1));
List<DBObject> aggregationList = new ArrayList<>();
aggregationList.add(match);
aggregationList.add(project);
aggregationList.add(group);
aggregationList.add(sort);
AggregationOutput output = coll.aggregate(aggregationList);
for (DBObject result : output.results()) {
BasicDBList employeeList = (BasicDBList) result.get("docs");
BasicDBObject employeeDoc = (BasicDBObject) employeeList.get(0);
String name = employeeDoc.get("name").toString();
System.out.println(name);
}
} catch (Exception ex) {
ex.printStackTrace();
}
此圖示呈現了聚合框架的流程
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title MongoDB 聚合框架與索引管理實戰
package "資料庫架構" {
package "應用層" {
component [連線池] as pool
component [ORM 框架] as orm
}
package "資料庫引擎" {
component [查詢解析器] as parser
component [優化器] as optimizer
component [執行引擎] as executor
}
package "儲存層" {
database [主資料庫] as master
database [讀取副本] as replica
database [快取層] as cache
}
}
pool --> orm : 管理連線
orm --> parser : SQL 查詢
parser --> optimizer : 解析樹
optimizer --> executor : 執行計畫
executor --> master : 寫入操作
executor --> replica : 讀取操作
cache --> executor : 快取命中
master --> replica : 資料同步
note right of cache
Redis/Memcached
減少資料庫負載
end note
@enduml圖示內容解密:
此圖示展示了聚合框架的典型流程,包括篩選($match)、投影($project)、分組($group)和排序($sort)等階段,最終輸出結果。
MongoDB 索引管理詳解
MongoDB 的索引對於提升查詢效能至關重要,本文將探討索引的建立、刪除以及不同型別的索引。
索引建立基礎
首先,我們先建立一個名為 transactions 的集合,並插入一些測試資料:
db.transactions.insert({ cr_dr : "D", amount : 100, fee : 2});
db.transactions.insert({ cr_dr : "C", amount : 100, fee : 2});
db.transactions.insert({ cr_dr : "C", amount : 10, fee : 2});
db.transactions.insert({ cr_dr : "D", amount : 100, fee : 4});
db.transactions.insert({ cr_dr : "D", amount : 10, fee : 2});
db.transactions.insert({ cr_dr : "C", amount : 10, fee : 4});
db.transactions.insert({ cr_dr : "D", amount : 100, fee : 2});
使用 getIndexes() 方法可以檢視集合上的所有索引:
db.transactions.getIndexes();
內容解密:
getIndexes()方法傳回一個陣列,包含集合上的所有索引資訊。- MongoDB 預設會在
_id欄位上建立一個唯一的索引,以防止插入重複的_id值。
輸出結果顯示,transactions 集合上已經有一個預設的 _id_ 索引。
接下來,我們為 cr_dr 欄位建立一個升序索引:
db.transactions.createIndex({ cr_dr : 1 });
內容解密:
createIndex()方法用於在指定欄位上建立索引。{ cr_dr : 1 }表示在cr_dr欄位上建立升序索引。createdCollectionAutomatically表示是否自動建立了集合。numIndexesBefore和numIndexesAfter分別表示建立索引前後的索引數量。
再次執行 getIndexes() 方法,可以看到 transactions 集合上現在有兩個索引:預設的 _id_ 索引和我們剛剛建立的 cr_dr_1 索引。
我們也可以在建立索引時指定索引名稱:
db.transactions.createIndex({ cr_dr : -1 },{name : "index on cr_dr desc"})
內容解密:
{ cr_dr : -1 }表示在cr_dr欄位上建立降序索引。{name : "index on cr_dr desc"}指定了索引的名稱。
雜湊索引
MongoDB 也支援雜湊索引,適用於等值查詢,但不適用於範圍查詢。
db.transactions.createIndex({ cr_dr : "hashed" });
內容解密:
{ cr_dr : "hashed" }表示在cr_dr欄位上建立雜湊索引。- 雜湊索引在等值查詢上表現更好,但不支援範圍查詢。
索引刪除
如果知道索引的名稱,可以使用以下方法刪除索引:
db.collection.dropIndex('name_of_index');
如果不知道索引的名稱,可以使用以下方法刪除索引:
db.collection.dropIndex( { 'name_of_field' : -1 } );
內容解密:
dropIndex()方法用於刪除指定的索引。- 可以根據索引的名稱或鍵值對來刪除索引。
稀疏索引和部分索引
稀疏索引(Sparse Indexes)和部分索引(Partial Indexes)是兩種特殊的索引型別。
稀疏索引
稀疏索引會忽略不存在指定欄位的檔案,因此可以用於建立唯一索引,同時允許某些檔案缺少該欄位。
db.scores.createIndex( { nickname: 1 } , { unique: true, sparse: true } )
內容解密:
{ unique: true, sparse: true }表示建立一個唯一的稀疏索引。- 稀疏索引可以節省儲存空間,並提高查詢效能。
部分索引
部分索引是 MongoDB 3.2 版本引入的新功能,它允許根據指定的篩選條件建立索引。
db.restaurants.createIndex(
{ cuisine: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
內容解密:
{ partialFilterExpression: { rating: { $gt: 5 } } }表示只有當rating大於 5 時,才會將cuisine索引。- 部分索引提供了比稀疏索引更靈活的篩選條件。