php – Laravel MongoDB 的 distinct 的 count() 用法

原因

官方提供的 distinct() 會在 php 取得所有列表。

$users = User::distinct()->get(['name']);

當要計算數量如果使用 count() 那會把資料全部撈到 php 然後加總,這會非常吃 php-fpm 所使用的記憶體。

$users = User::distinct()->get(['name'])->count();

改用聚合

count() 這件事情在取出資料庫之前就完成,由資料庫回傳一個數值是最好的辦法。但現階段必須透過 laravel mongodb 的聚合寫法,以下範例示範:

1. 建立範例數據

db.test_orders.insertMany( [
  { _id: 0, name: "Pepperoni", size: "small", price: 19,
  quantity: 10, date: ISODate( "2021-03-13T08:14:30Z" ) },
  { _id: 1, name: "Pepperoni", size: "medium", price: 20,
  quantity: 20, date : ISODate( "2021-03-13T09:13:24Z" ) },
  { _id: 2, name: "Pepperoni", size: "large", price: 21,
  quantity: 30, date : ISODate( "2021-03-17T09:22:12Z" ) },
  { _id: 3, name: "Cheese", size: "small", price: 12,
  quantity: 15, date : ISODate( "2021-03-13T11:21:39.736Z" ) },
  { _id: 4, name: "Cheese", size: "medium", price: 13,
  quantity:50, date : ISODate( "2022-01-12T21:23:13.331Z" ) },
  { _id: 5, name: "Cheese", size: "large", price: 14,
  quantity: 10, date : ISODate( "2022-01-12T05:08:13Z" ) },
  { _id: 6, name: "Vegan", size: "small", price: 17,
  quantity: 10, date : ISODate( "2021-01-13T05:08:13Z" ) },
  { _id: 7, name: "Vegan", size: "medium", price: 18,
  quantity: 10, date : ISODate( "2021-01-13T05:10:13Z" ) }
] )
image

2. 使用聚合做到 distinct

假設我要知道 test_orders 裡面總共出現幾個 name,那可以透過聚合的 pipeline 實現。先寫結論

db.test_orders.aggregate([

   // 產生兩個欄位 _id 與 count,_id 指定分組的目標也就是欄位 name,count 則使用 $sum 去加總出現的數量
   { $group: { _id: "$name", count: { $sum: 1 } } },

  // 將上方結果,再過濾,欄位一樣 _id 與 count,我們只需要加總出現的列表數量,即是達到我們的 distinct 需求
   { $group: { _id: 'total', count: { $sum: 1 } } }
])
image

這樣就達到我們要的目標 3 筆。

3. 肢解上述

上述 aggregate() 用了兩次處理,我們分開來看數據。第一次先算出各筆 name 的重複次數

db.test_orders.aggregate([
   { $group: { _id: "$name", count: { $sum: 1 } } },
])
image

看到這裡就知道這份數據,有 3 種 name。因此可以這麼寫

db.test_orders.aggregate([
   { $group: { _id: "$name", count: { $sum: 1 } } },
   { $group: { _id:'total', count: { $sum: 1 } } }
])
image

發表迴響