1. 程式人生 > >【Mongodb】聚合查詢 && 固定集合

【Mongodb】聚合查詢 && 固定集合

概述

資料儲存是為了可查詢,統計。若資料只需儲存,不需要查詢,這種資料也沒有多大價值

本篇介紹Mongodb

  • 聚合查詢(Aggregation)
  • 固定集合(Capped Collections)

 

準備工作

準備10000條資料

var orders = new Array();
for (var i = 10000; i < 20000; i++) {
    orders[i] = {
        orderNo: i + Math.random().toString().substr(3, 3),
        price: Math.round(Math.random() * 10000) / 100,
        qty: Math.floor(Math.random() * 10) + 1,
        orderTime: new Date(new Date().setSeconds(Math.floor(Math.random() * 10000)))
    };
}
db.order.insert(orders);

 

 

聚合查詢

Mongodb的聚合函式操作都在db.collection.aggregate,通過定義聚合管道(一組規則),達到分組,統計等功能,下面介紹常用的幾種聚合函式

 

分組管道($group)

格式

{
  $group:
    {
      _id: <expression>, // Group By Expression
      <field1>: { <accumulator1> : <expression1> },
      ...
    }
 }

_id 是分組欄位,若指定_id = null 或常量欄位,就是將整個結果集分組。

分組統計欄位格式{ <accumulator1> : <expression1> }

累計器操作(Accumulator Operator)參考Accumulator Operator

 

假設現在需要統計每天每個小時的訂單總價格,平均價格,最大,最小,總訂單數等

db.order.aggregate([
    {
        $group: {
            //分組欄位,這裡用到$dateToString格式化,這裡按小時統計
            _id: { $dateToString: { format: "%Y-%m-%d %H", date: "$orderTime" } },
            //總價格
            totalPrice: { $sum: "$price" },
            //分組第一個訂單
            firstOrder: { $first: "$orderNo" },
            //分組最後一個訂單
            lastOrder: { $last: "$orderNo" },
            //平均價格
            averagePrice: { $avg: "$price" },
            //最大價格
            maxPrice: { $max: "$price" },
            //最小价格
            minPrice: { $min: "$price" },
            //總訂單數
            totalOrders: { $sum: 1 },
        }
    }
])

返回結果

{ "_id" : "2020-04-12 15", "totalPrice" : 172813.68, "firstOrder" : "10000263", "lastOrder" : "19999275", "averagePrice" : 49.20662870159453, "maxPrice" : 99.94, "minPrice" : 0.01, "totalOrders" : 3512 }
{ "_id" : "2020-04-12 13", "totalPrice" : 80943.98, "firstOrder" : "10004484", "lastOrder" : "19991554", "averagePrice" : 50.780414052697616, "maxPrice" : 99.81, "minPrice" : 0.08, "totalOrders" : 1594 }
{ "_id" : "2020-04-12 14", "totalPrice" : 181710.15, "firstOrder" : "10001745", "lastOrder" : "19998830", "averagePrice" : 49.76996713229252, "maxPrice" : 99.93, "minPrice" : 0.01, "totalOrders" : 3651 }
{ "_id" : "2020-04-12 16", "totalPrice" : 63356.12, "firstOrder" : "10002711", "lastOrder" : "19995793", "averagePrice" : 50.97032984714401, "maxPrice" : 99.95, "minPrice" : 0.01, "totalOrders" : 1243 }

 

篩選管道($match)

格式

{ $match: { <query> } }

這個比較簡單,就是篩選資料

假設我現在需要篩選金額在(10,15)之間的

db.orders.aggregate([
    {
        $match: {
            "price": { $gt: 10, $lt: 15 }
        }
    }
])

 

排序管道($sort)

格式

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

指定欄位排序,1:升序,-1:倒序

 

限制條數($limit)

格式

{ $limit: <positive integer> }

 

Mongodb的聚合管道有很多,具體不一一列出,參考Aggregation Pipeline Stages — MongoDB Manual

 

 

帶有(aggregation)就是都可以用於聚合管道

說了那麼多,其實都沒有使用Mongodb聚合函式最強大的功能,就是組合管道使用,查詢我們需要資料,因為Mongodb提供的聚合管道函式非常多,所以組合起來使用是非常強大。

值得注意是管道的順序,Mongodb是按你定義的順序,將每一步執行的結果集傳給下一個管道處理,輸出是最後一個管道的結果集,所以不同的管道順序會有可能得到不是預期的結果,甚至報錯(這種情況報錯甚至比得到不是預期的結果可能還好) 假設現在按每天小時統計符合下列條件的訂單
  • 訂單金額大於10元 小於 50元 && 數量小於等於5 and
  • 去掉金額最小的50條訂單 && 去掉金額最大的50條訂單 and
  • 統計每個小時內訂單數量,訂單金額
  • 按訂單金額升序輸出
db.order.aggregate([
    {
        $match: { "price": { $gt: 10, $lt: 50 }, "qty": { $lte: 5 } }
    },
    {
        $sort: {
            "price": -1
        }
    },
    {
        $skip: 50
    },
    {
        $sort: {
            "price": 1
        }
    },
    {
        $skip: 50
    },
    {
        $group: {
            _id: { $dateToString: { format: "%Y-%m-%d %H", date: "$orderTime" } },
            totalPrice: { $sum: "$price" },
            totalOrders: { $sum: 1 }

        }
    },
    {
        $sort: {
            "totalPrice": 1
        }
    }
])
View Code

解決思路

  1. 篩選符合條件的記錄($match)
  2. 按金額倒序($sort:-1)
  3. 跳過金額最大的50條記錄($skip:50)
  4. 按金額升序($sort:1)
  5. 跳過金額最小的50條記錄($skip:50)
  6. 按每天每小時統計($group)
  7. 統計結果總金額升序($sort:1)

 

固定集合

概述

capped-collection are fixed-size collections that support high-throughput operations that insert and retrieve documents based on insertion order. Capped collections work in a way similar to circular buffers: once a collection fills its allocated space, it makes room for new documents by overwriting the oldest documents in the collection.

從上面定義可以看出固定集合具有幾個特性

  • 固定大小
  • 高吞吐量
  • 根據插入順序檢索文件
  • 超過限制大小覆蓋舊的文件

根據固定集合特性,固定集合適合用於以下場景

  • 只需保留最近的日誌查詢系統
  • 快取資料(熱點資料)
  • 等等

固定集合限制

  • 固定集合的大小建立之後不能修改
  • 不能刪除固定集合裡的文件,只能刪除集合再重新建固定集合
  • 固定集合不能使用固定分割槽
  • 聚合管道$out不能使用在固定集合

固定集合使用

 1. 建立固定集合

db.createCollection("log", { capped : true, size : 4096, max : 5000 } )
欄位必須說明
capped 是       是否建立固定集合
size 固定集合大小,單位:位元組
max 文件數量大小限制

size 和 max 是或關係,超出其中一個限制都會覆蓋舊文件

 

2. 檢查集合是否固定集合

db.collection.isCapped()

 

3. 將一個非固定的集合轉換固定集合

db.runCommand({"convertToCapped": "mycoll", size: 100000});

 

 

測試固定集合

1. 超過限制文件數

// 1. 建立固定集合,大小1M,最大文件數量10
db.createCollection("log", { capped: true, size: 1024 * 1024, max: 10 });

// 2. 插入200條資料
for (var i = 0; i < 200; i++) {
    db.log.insertOne({
        "_id": i + 1,
        "userId": Math.floor(Math.random() * 1000),
        "content": "登入" + ("0000" + i).slice(-4),
        "createTime": new Date(),
    });
}

 

再查詢現在Mongodb儲存情況

db.log.stats()

 

可以看出每個物件都是佔有78個位元組,因為欄位都是定長的

 

2. 驗證操作儲存大小

If the size field is less than or equal to 4096, then the collection will have a cap of 4096 bytes. Otherwise, MongoDB will raise the provided size to make it an integer multiple of 256.

如果size的欄位設定小於4096,Mongodb將會提供一個256的倍數的資料儲存大小

假設256的大小,256 / 78 = 3.282051282051282,應該能存3個文件

// 1. 刪除之前固定集合
db.log.drop();

// 2. 建立固定集合,size < 78 , 驗證是否建立一個256的大小
db.createCollection("log", { capped: true, size: 78 });

// 2. 插入200條資料
for (var i = 0; i < 200; i++) {
    db.log.insertOne({
        "_id": i + 1,
        "userId": Math.floor(Math.random() * 1000),
        "content": "登入" + ("0000" + i).slice(-4),
        "createTime": new Date(),
    });
}

檢視集合統計

db.log.stats()

可以看出log集合使用了234個位元組(78 * 3),也即3個文件的大小,最大能使用大小是256

 

3. 查詢固定集合

Mongodb若沒指定排序欄位,是按存入順序檢索,可以使用.sort( { $natural: -1 } )改變輸出順序

db.log.find({}).sort( { $natural: -1 } )

 

 4. 將非固定集合轉換固定集合

將order轉換試試

db.runCommand({"convertToCapped": "order", size: 8096});

檢視order集合統計

 

 

 只剩下90條資料

 

轉發請標明出處:https://www.cnblogs.com/WilsonPan/p/12692642.html

參考文章

Aggregation — MongoDB Manual
Capped Collections — MongoDB Manual