Chương này giới thiệu một giải thích chi tiết về phân tích thống kê MongoDB, chủ yếu được thực hiện thông qua Ống dẫn Tập lệnh. Nó tương tự như câu lệnh "group by" trong SQL. Trong tập lệnh MongoDB, phân tích thống kê được thực hiện bằng cách sử dụng hàm db.collection.aggregate().

Hướng dẫn tiên quyết

Ống dẫn Tập lệnh Tập lệnh MongoDB

Bước tổng quát

  • Sử dụng $match để lọc dữ liệu mục tiêu
  • Sử dụng $group để nhóm và tính toán dữ liệu
  • Sử dụng $sort để sắp xếp kết quả (tùy chọn)

Dữ liệu Kiểm tra

Dữ liệu trong bộ sưu tập orders như sau

{ _id: 1, cust_id: "abc1", ord_date: ISODate("2012-11-02T17:04:11.102Z"), status: "A", amount: 50 }
{ _id: 2, cust_id: "xyz1", ord_date: ISODate("2013-10-01T17:04:11.102Z"), status: "A", amount: 100 }
{ _id: 3, cust_id: "xyz1", ord_date: ISODate("2013-10-12T17:04:11.102Z"), status: "D", amount: 25 }
{ _id: 4, cust_id: "xyz1", ord_date: ISODate("2013-10-11T17:04:11.102Z"), status: "D", amount: 125 }
{ _id: 5, cust_id: "abc1", ord_date: ISODate("2013-11-12T17:04:11.102Z"), status: "A", amount: 25 }

Hàm tổng hợp

db.collection.aggregate(pipeline) Giải thích:

  • pipeline nhận một tham số mảng, trong đó mỗi phần tử đại diện cho một bước xử lý.

Ví dụ

db.orders.aggregate([
                     { $match: { status: "A" } },  // Bước đầu tiên
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },   // Bước thứ hai
                     { $sort: { total: -1 } }  // Bước thứ ba
                   ])

Tương đương SQL

select sum(amount) as total from orders 
        where status="A" 
        group by cust_id 
        order by total desc

Bước $match

Định dạng:

{ $match: { <query> } }

Giải thích:

  • <query> Điều kiện truy vấn MongoDB

Được sử dụng để thiết lập các điều kiện truy vấn. Nếu bỏ qua $match, nó ngụ ý truy vấn tất cả dữ liệu.

Mẹo: Nếu bạn không quen với cú pháp truy vấn MongoDB, vui lòng tham khảo các chương trước đó.

Bước $group

Tương tự như mệnh đề group by trong SQL, nó được sử dụng để nhóm dữ liệu và sau đó thực hiện một loạt các phép tính thống kê trên dữ liệu đã nhóm.

Sử dụng Cơ bản của $group

Cú pháp:

{
  $group:
    {
      _id: <biểu thức>, // Điều kiện nhóm, ví dụ: nhóm theo trường nào
      <field1>: { <accumulator1> : <biểu thức1> },  // Phép toán tổng hợp, bạn có thể thêm N phép toán tổng hợp
      ...
    }
 }

Giải thích:

  • - Tên chỉ số thống kê tùy chỉnh, có thể là N tổng cộng
  • - Hàm tổng hợp, tương tự như tổng, trung bình và các hàm tổng hợp khác trong SQL, khác biệt là các hàm tổng hợp của MongoDB được đặt tên với $ ở tiền tố, ví dụ: $sum, $avg
  • <biểu thức1> - Tham số của hàm tổng hợp, thường là giá trị trường cần đếm, trích dẫn trường tài liệu bằng định dạng "$tên trường"

Ví dụ:

db.orders.aggregate([
                     {
					 	$group: { 
							_id: "$cust_id",
							total: { $sum: "$amount" }, // Thêm chỉ số thống kê đầu tiên total, sử dụng $sum toán tử tổng cộng
							amount_avg: {$avg: "$amount"}  // Thêm chỉ số thống kê thứ hai avg, sử dụng $avg toán tử tính trung bình
						} 
					}
               ])

Kết quả:

{ "_id" : "abc1", "total" : 75, "amount_avg" : 37.5 }
{ "_id" : "xyz1", "total" : 250, "amount_avg" : 83.33333333333333 }

Tương đương SQL:

select 
	sum(amount) as  total,
	avg(amount) as amount_avg
from orders 
group by cust_id

Các hàm Tính Tổng nhóm $group

Các hàm tính tổng thường được sử dụng cho $group như sau:

Toán tử Mô tả Ví dụ
$avg Tính trung bình {$avg: "$amount"}
$sum Tính tổng {$sum: "$amount"}
$max Giá trị lớn nhất {$max: "$amount"}
$min Giá trị nhỏ nhất {$min: "$amount"}
$first Trả về dữ liệu sau khi nhóm, nội dung của tài liệu đầu tiên {$first: "$amount"}
$last Trả về dữ liệu sau khi nhóm, nội dung của tài liệu cuối cùng {$last: "$amount"}
$push Trả về dữ liệu sau khi nhóm { $push: { ord_date: "$ord_date", amount: "$amount" }
$addToSet Trả về dữ liệu sau khi nhóm, khác biệt $push vì nó loại bỏ các bản sao { $addToSet: "$amount" }

Ví dụ về $push

db.orders.aggregate(
   [
     {
       $group:
         {
           _id: "$cust_id",
           all: { $push: { ord_date: "$ord_date", amount: "$amount" } } // Giá trị của trường ord_date và amount
         }
     }
   ]
)

Kết quả

{ "_id" : "abc1", "all" : [ { "ord_date" : "2021-04-18 00:00:00", "amount" : 50 }, { "ord_date" : "2021-04-21 00:00:00", "amount" : 25 } ] }
{ "_id" : "xyz1", "all" : [ { "ord_date" : "2021-04-18 00:00:00", "amount" : 100 }, { "ord_date" : "2021-04-20 00:00:00", "amount" : 25 }, { "ord_date" : "2021-04-21 00:00:00", "amount" : 125 } ] }

Ví dụ về $addToSet

db.orders.aggregate(
   [
     {
       $group:
         {
           _id: "$cust_id",
           all_amount: { $addToSet: "$amount" } // Trả về tất cả các giá trị amount phân biệt
         }
     }
   ]
)

Kết quả

{ "_id" : "abc1", "all_amount" : [ 25, 50 ] }
{ "_id" : "xyz1", "all_amount" : [ 100, 25, 125 ] }

$sort:

Các giai đoạn $sort thường được đặt ở cuối để sắp xếp dữ liệu đã nhóm.

Định dạng:

{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }

Giải thích:

  • , - Tên các trường cần sắp xếp, hỗ trợ nhiều trường.
  • - Hướng của việc sắp xếp, -1 cho giảm dần, 1 cho tăng dần.

Ví dụ:

db.orders.aggregate([
                     { $match: { status: "A" } },
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
                     { $sort: { total: -1 } }
                   ])

Phân trang Tổng hợp

Chúng ta có thể thực hiện phân trang bằng cách sử dụng các toán tử $limit và $skip.

Ví dụ:

db.orders.aggregate([
                     { $match: { status: "A" } },
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
                     { $sort: { total: -1 } },
					 { $limit: 5 }, // Giới hạn số bản ghi trả về, tương tự như kích thước trang trong phân trang.
					 { $skip: 1 } // Bỏ qua một số bản ghi, tương tự như độ lệch trong SQL.
                   ])