MongoDB查詢總結

westwolf發表於2021-09-09

MongoDB查詢總結


介紹

前面寫過一篇關於Mongo�db的例子——,當時使用的只是簡單的查詢,然後後面業務變的有點複雜,原先沒有仔細研究過Mongodb的查詢,以為就是簡單呼叫下find就可以了,乃衣服。

所以今天特地舉例說明一下Mongo中查詢問題。

Mongo查詢可以�分為2種:

  • 普通查詢,類似於Sql中的 select where

  • 聚合查詢,類似於Sql中的 group by

普通查詢

首先放一下,普通查詢主要用到db.collection.find()函式。

定義下示例資料庫,下面是是初始化資料,可以在Mongo中的控制檯�執行。

db.inventory.insertMany([
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
  • 查詢所有

db.inventory.find( {} )

對映Sql語句

SELECT * FROM inventory
  • 條件查詢

語法格式

, ... }

比如查詢statusD記錄。

db.inventory.find( { status: "D" } )

對映Sql語句

SELECT * FROM inventory WHERE status = "D"
  • 使用運算子進行條件查詢

語法格式

: {  }, ... }

比如查詢滿足status是�陣列[A,D]中的記錄

db.inventory.find( { status: { $in: [ "A", "D" ] } } )

對映Sql語句

SELECT * FROM inventory WHERE status in ("A", "D")
  • AND 條件查詢

直接在find函式指定多個欄位滿足即可,這樣就是 and 條件。

比如下面語句就是 statusAqty 小於 30

db.inventory.find( { status: "A", qty: { $lt: 30 } } )

對映Sql語句

SELECT * FROM inventory WHERE status = "A" AND qty 
  • OR 條件查詢

�OR 和 AND 就不一樣了,需要用到運算子 $or,如下所示。

db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )

類似於SQL中的

SELECT * FROM inventory WHERE status = "A" OR qty 
  • OR 和 AND 集合一起

db.inventory.find( {     status: "A",
     $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )

表示這樣的意思。

SELECT * FROM inventory WHERE status = "A" AND ( qty 

查詢舉例

  • 查詢全部

SELECT *FROM people
db.people.find()
  • 指定欄位

SELECT id,
       user_id,       statusFROM people
db.people.find(
    { },
    { user_id: 1, status: 1 }
)
SELECT user_id, statusFROM people
  • 指定欄位,不顯示_id

db.people.find(
    { },
    { user_id: 1, status: 1, _id: 0 }
)
  • 條件查詢全部

SELECT *FROM peopleWHERE status = "A"
db.people.find(
    { status: "A" }
)
  • 條件查詢�指定欄位

SELECT user_id, statusFROM peopleWHERE status = "A"
db.people.find(
    { status: "A" },
    { user_id: 1, status: 1, _id: 0 }
)
  • 條件查詢不等於

SELECT *FROM peopleWHERE status != "A"
db.people.find(
    { status: { $ne: "A" } }
)
  • 條件查詢 AND

SELECT *FROM peopleWHERE status = "A"AND age = 50
db.people.find(
    { status: "A",
      age: 50 }
)
  • 條件查詢 OR

SELECT *FROM peopleWHERE status = "A"OR age = 50
db.people.find(
    { $or: [ { status: "A" } ,
             { age: 50 } ] }
)
  • 條件查詢 �>

SELECT *FROM peopleWHERE age > 25
db.people.find(
    { age: { $gt: 25 } }
)
  • 條件查詢 �

SELECT *FROM peopleWHERE age 
db.people.find(
   { age: { $lt: 25 } }
)
  • �複雜的條件查詢

SELECT *FROM peopleWHERE age > 25AND   age 
db.people.find(
   { age: { $gt: 25, $lte: 50 } }
)
  • 條件查詢 �LIKE

SELECT *FROM peopleWHERE user_id like "%bc%"
db.people.find( { user_id: /bc/ } )// ORdb.people.find( { user_id: { $regex: /bc/ } } )
SELECT *FROM peopleWHERE user_id like "bc%"
db.people.find( { user_id: /^bc/ } )// ORdb.people.find( { user_id: { $regex: /^bc/ } } )
  • 排序

SELECT *FROM peopleWHERE status = "A"ORDER BY user_id ASC
db.people.find( { status: "A" } ).sort( { user_id: 1 } )
SELECT *FROM peopleWHERE status = "A"ORDER BY user_id DESC
db.people.find( { status: "A" } ).sort( { user_id: -1 } )
  • 統計數量

SELECT COUNT(*)FROM people
db.people.count()// ordb.people.find().count()
SELECT COUNT(user_id)FROM people
db.people.count( { user_id: { $exists: true } } )or
db.people.find( { user_id: { $exists: true } } ).count()
SELECT COUNT(*)FROM peopleWHERE age > 30
db.people.count( { age: { $gt: 30 } } )// ordb.people.find( { age: { $gt: 30 } } ).count()
  • 去除重複distinct

SELECT DISTINCT(status)FROM people
db.people.distinct( "status" )
SELECT *FROM peopleLIMIT 1
  • 限制數量

db.people.findOne()

// or

db.people.find().limit(1)
SELECT *FROM peopleLIMIT 5SKIP 10
db.people.find().limit(5).skip(10)
  • EXPLAIN

EXPLAIN SELECT *FROM peopleWHERE status = "A"
db.people.find( { status: "A" } ).explain()

聚合查詢

上面�普通查詢使用find函式即可,但是聚合查詢使用另外一個函式aggregate,這裡是。

初始化資料如下,有2個表 ordersorder_lineitem �,外來鍵關聯order_lineitem.order_id and the orders.id

{
  cust_id: "abc123",
  ord_date: ISODate("2012-11-02T17:04:11.102Z"),
  status: 'A',
  price: 50,
  items: [ { sku: "xxx", qty: 25, price: 1 },
           { sku: "yyy", qty: 25, price: 1 } ]
}
  • 統計數量

db.orders.aggregate( [
   {
     $group: {
        _id: null,
        count: { $sum: 1 }
     }
   }
] )

對映Sql語句

SELECT COUNT(*) AS countFROM orders
  • 計算總和

db.orders.aggregate( [
   {     $group: {
        _id: null,
        total: { $sum: "$price" }
     }
   }
] )

對映Sql語句

SELECT SUM(price) AS totalFROM orders
  • 分組計算總和

db.orders.aggregate( [
   {     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   }
] )

對映Sql語句

SELECT cust_id,       SUM(price) AS totalFROM ordersGROUP BY cust_id
  • 分組計算總和並排序

db.orders.aggregate( [
   {     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   },
   { $sort: { total: 1 } }
] )

對映Sql語句

SELECT cust_id,       SUM(price) AS totalFROM ordersGROUP BY cust_idORDER BY tota
  • 多個欄位分組

db.orders.aggregate( [
   {     $group: {
        _id: {
           cust_id: "$cust_id",
           ord_date: {
               month: { $month: "$ord_date" },
               day: { $dayOfMonth: "$ord_date" },
               year: { $year: "$ord_date"}
           }
        },
        total: { $sum: "$price" }
     }
   }
] )

對映Sql語句

SELECT cust_id,
       ord_date,       SUM(price) AS totalFROM ordersGROUP BY cust_id,
         ord_date
  • 條件分組——HAVING

db.orders.aggregate( [
   {     $group: {
        _id: "$cust_id",
        count: { $sum: 1 }
     }
   },
   { $match: { count: { $gt: 1 } } }
] )

對映Sql語句

SELECT cust_id,       count(*)FROM ordersGROUP BY cust_idHAVING count(*) > 1
  • 複雜條件分組統計

db.orders.aggregate( [
   {     $group: {
        _id: {
           cust_id: "$cust_id",
           ord_date: {
               month: { $month: "$ord_date" },
               day: { $dayOfMonth: "$ord_date" },
               year: { $year: "$ord_date"}
           }
        },
        total: { $sum: "$price" }
     }
   },
   { $match: { total: { $gt: 250 } } }
] )

對映Sql語句

SELECT cust_id,
       ord_date,       SUM(price) AS totalFROM ordersGROUP BY cust_id,
         ord_dateHAVING total > 250
  • 複雜條件分組統計示例1

db.orders.aggregate( [
   { $match: { status: 'A' } },
   {     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   }
] )

對映Sql語句

SELECT cust_id,       SUM(price) as totalFROM ordersWHERE status = 'A'GROUP BY cust_id
  • 複雜條件分組統計示例2

db.orders.aggregate( [
   { $match: { status: 'A' } },
   {     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   },
   { $match: { total: { $gt: 250 } } }
] )

對映Sql語句

SELECT cust_id,       SUM(price) as totalFROM ordersWHERE status = 'A'GROUP BY cust_idHAVING total > 250
  • 表關聯

db.orders.aggregate( [
   { $unwind: "$items" },
   {     $group: {
        _id: "$cust_id",
        qty: { $sum: "$items.qty" }
     }
   }
] )

對映Sql語句

SELECT cust_id,       SUM(li.qty) as qtyFROM orders o,
     order_lineitem liWHERE li.order_id = o.idGROUP BY cust_id
  • 巢狀查詢

db.orders.aggregate( [
   {     $group: {
        _id: {
           cust_id: "$cust_id",
           ord_date: {
               month: { $month: "$ord_date" },
               day: { $dayOfMonth: "$ord_date" },
               year: { $year: "$ord_date"}
           }
        }
     }
   },
   {     $group: {
        _id: null,
        count: { $sum: 1 }
     }
   }
] )

對映Sql語句

SELECT COUNT(*)FROM (SELECT cust_id,
             ord_date      FROM orders      GROUP BY cust_id,
               ord_date)      as DerivedTable

Map-Reduce

Mongo中聚合查詢還有一種叫Map-Reduce,官方文件在,在思想上它跟Hadoop一樣,從一個單一集合中輸入資料,然後將結果輸出到一個集合中。通常在使用類似SQL中Group By操作時,Map/Reduce會是一個好的工具。

圖片描述

Map-Reduce

介面方法定義

db.collection.mapReduce(    ,    ,
    {
        out: ,
        query: ,
        sort: ,
        limit: ,
        finalize: ,
        scope: ,
        jsMode: ,
        verbose: ,
        bypassDocumentValidation: 
    }
)

引數說明

  • mapReduce: 要執行Map/Reduce集合的名字

  • map: map 函式     (下面會詳細介紹)

  • reduce: reduce函式(下面會詳細介紹)

  • out: 存放結果的集合 (下面會詳細介紹)

  • query: 設定查詢條件  

  • sort: 按某個鍵來排序

  • limit: 指明從集合檢索文件個數的最大值

  • finalize: 對reduce結果做進一步處理  

  • scope: 指明透過map/reduce/finalize可以訪問到的變數

  • jsMode: 指明Map/Reduce執行過程中文件保持JSON狀態  

  • verbose: 提供關於任務執行的統計資料  

示例說明

�舉例說明Map-Reduce的用途,�雖然程式碼比較多,也行用上面的聚合查詢,一下子就搞定了,但是這裡只是舉例。

比如有個訂單表,如下所示,我們需要計算每個人的訂單總價。

{
     _id: ObjectId("50a8240b927d5d8b5891743c"),
     cust_id: "abc123",
     ord_date: new Date("Oct 04, 2012"),
     status: 'A',
     price: 25,
     items: [ { sku: "mmm", qty: 5, price: 2.5 },
              { sku: "nnn", qty: 5, price: 2.5 } ]
}

首先定義Map方法,就說我們後面的聚合計算需要哪些欄位,�由於需要計算每個人的訂單總結,那麼個人資訊和加個肯定是我們需要的。

var mapFunction1 = function() {
    emit(this.cust_id, this.price);
};

然後定義reduce方法,計算每個人的訂單價格。

var reduceFunction1 = function(keyCustId, valuesPrices) {    return Array.sum(valuesPrices);
};

然後儲存最後的計算結果。

db.orders.mapReduce(    mapFunction1,    reduceFunction1,
    { out: "map_reduce_example" }
)

這樣一個簡單的Map-Reduce例項就完成了,結果放在map_reduce_example中。

上面示例比較簡單,那麼我們來一個複雜一點的例子。

一條訂單記錄中,有sdk的名稱、數量、價格,那麼要查詢出日期大於01/01/2012,所有訂單的總數,以及�平均sdk價格。

首先還是定義個map函式。

var mapFunction2 = function() {    for (var idx = 0; idx 

然後算出sku的數量,和總價格。

var reduceFunction2 = function(keySKU, countObjVals) {
    reducedVal = { count: 0, qty: 0 };    for (var idx = 0; idx 

總價格出來後,還要計算出平均價格。

var finalizeFunction2 = function (key, reducedVal) {
    reducedVal.avg = reducedVal.qty / reducedVal.count;    return reducedVal;
};

還有日期的條件過濾,最後得出完整的map-reduce。

db.orders.mapReduce(    mapFunction2,    reduceFunction2,
    {        out: { merge: "map_reduce_example" },        query: {            ord_date:{ $gt: new Date('01/01/2012') }
        },        finalize: finalizeFunction2
    }
)

總結

以上就是我對MongoDB的示例總結,本人是一個初學者,也有很多地方不懂,如果有錯誤的地方,歡迎指出。



作者:流水不腐小夏
連結:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4662/viewspace-2810585/,如需轉載,請註明出處,否則將追究法律責任。

相關文章