MongoDB查詢總結(jié)

MongoDB查詢總結(jié)


介紹

前面寫過一篇關(guān)于Mongo?db的例子——淺談MongoDB數(shù)據(jù)庫,當時使用的只是簡單的查詢辩涝,然后后面業(yè)務(wù)變的有點復(fù)雜恋技,原先沒有仔細研究過Mongodb的查詢肝集,以為就是簡單調(diào)用下find就可以了肿孵,乃衣服却盘。

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

Mongo查詢可以?分為2種:

  • 普通查詢碍讨,類似于Sql中的 select where

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

普通查詢

首先放一下官方文檔茂蚓,普通查詢主要用到db.collection.find()函數(shù)壕鹉。

定義下示例數(shù)據(jù)庫,下面是是初始化數(shù)據(jù)聋涨,可以在Mongo中的控制臺?執(zhí)行晾浴。

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
  • 條件查詢

語法格式

{ <field1>: <value1>, ... }

比如查詢statusD記錄。

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

映射Sql語句

SELECT * FROM inventory WHERE status = "D"
  • 使用操作符進行條件查詢

語法格式

{ <field1>: { <operator1>: <value1> }, ... }

比如查詢滿足status是?數(shù)組[A,D]中的記錄

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

映射Sql語句

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

直接在find函數(shù)指定多個字段滿足即可牍白,這樣就是 and 條件脊凰。

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

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

映射Sql語句

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

?OR 和 AND 就不一樣了狸涌,需要用到操作符 $or切省,如下所示。

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

類似于SQL中的

SELECT * FROM inventory WHERE status = "A" OR qty < 30
  • OR 和 AND 集合一起
db.inventory.find( {
     status: "A",
     $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )

表示這樣的意思帕胆。

SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")

查詢舉例

  • 查詢?nèi)?/li>
SELECT *
FROM people
db.people.find()
  • 指定字段
SELECT id,
       user_id,
       status
FROM people
db.people.find(
    { },
    { user_id: 1, status: 1 }
)
SELECT user_id, status
FROM people
  • 指定字段朝捆,不顯示_id
db.people.find(
    { },
    { user_id: 1, status: 1, _id: 0 }
)
  • 條件查詢?nèi)?/li>
SELECT *
FROM people
WHERE status = "A"
db.people.find(
    { status: "A" }
)
  • 條件查詢?指定字段
SELECT user_id, status
FROM people
WHERE status = "A"
db.people.find(
    { status: "A" },
    { user_id: 1, status: 1, _id: 0 }
)
  • 條件查詢不等于
SELECT *
FROM people
WHERE status != "A"
db.people.find(
    { status: { $ne: "A" } }
)
  • 條件查詢 AND
SELECT *
FROM people
WHERE status = "A"
AND age = 50
db.people.find(
    { status: "A",
      age: 50 }
)
  • 條件查詢 OR
SELECT *
FROM people
WHERE status = "A"
OR age = 50
db.people.find(
    { $or: [ { status: "A" } ,
             { age: 50 } ] }
)
  • 條件查詢 ?>
SELECT *
FROM people
WHERE age > 25
db.people.find(
    { age: { $gt: 25 } }
)
  • 條件查詢 ?<
SELECT *
FROM people
WHERE age < 25
db.people.find(
   { age: { $lt: 25 } }
)
  • ?復(fù)雜的條件查詢
SELECT *
FROM people
WHERE age > 25
AND   age <= 50
db.people.find(
   { age: { $gt: 25, $lte: 50 } }
)
  • 條件查詢 ?LIKE
SELECT *
FROM people
WHERE user_id like "%bc%"
db.people.find( { user_id: /bc/ } )

// OR

db.people.find( { user_id: { $regex: /bc/ } } )
SELECT *
FROM people
WHERE user_id like "bc%"
db.people.find( { user_id: /^bc/ } )

// OR

db.people.find( { user_id: { $regex: /^bc/ } } )
  • 排序
SELECT *
FROM people
WHERE status = "A"
ORDER BY user_id ASC
db.people.find( { status: "A" } ).sort( { user_id: 1 } )
SELECT *
FROM people
WHERE status = "A"
ORDER BY user_id DESC
db.people.find( { status: "A" } ).sort( { user_id: -1 } )
  • 統(tǒng)計數(shù)量
SELECT COUNT(*)
FROM people
db.people.count()

// or

db.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 people
WHERE age > 30
db.people.count( { age: { $gt: 30 } } )

// or

db.people.find( { age: { $gt: 30 } } ).count()
  • 去除重復(fù)distinct
SELECT DISTINCT(status)
FROM people
db.people.distinct( "status" )
SELECT *
FROM people
LIMIT 1
  • 限制數(shù)量
db.people.findOne()

// or

db.people.find().limit(1)
SELECT *
FROM people
LIMIT 5
SKIP 10
db.people.find().limit(5).skip(10)
  • EXPLAIN
EXPLAIN SELECT *
FROM people
WHERE status = "A"
db.people.find( { status: "A" } ).explain()

聚合查詢

上面?普通查詢使用find函數(shù)即可,但是聚合查詢使用另外一個函數(shù)aggregate懒豹,這里是官方文檔芙盘。

初始化數(shù)據(jù)如下,有2個表 ordersorder_lineitem ?脸秽,外鍵關(guān)聯(lián)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 } ]
}
  • 統(tǒng)計數(shù)量
db.orders.aggregate( [
   {
     $group: {
        _id: null,
        count: { $sum: 1 }
     }
   }
] )

映射Sql語句

SELECT COUNT(*) AS count
FROM orders
  • 計算總和
db.orders.aggregate( [
   {
     $group: {
        _id: null,
        total: { $sum: "$price" }
     }
   }
] )

映射Sql語句

SELECT SUM(price) AS total
FROM orders
  • 分組計算總和
db.orders.aggregate( [
   {
     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   }
] )

映射Sql語句

SELECT cust_id,
       SUM(price) AS total
FROM orders
GROUP BY cust_id
  • 分組計算總和并排序
db.orders.aggregate( [
   {
     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   },
   { $sort: { total: 1 } }
] )

映射Sql語句

SELECT cust_id,
       SUM(price) AS total
FROM orders
GROUP BY cust_id
ORDER 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 total
FROM orders
GROUP 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 orders
GROUP BY cust_id
HAVING count(*) > 1
  • 復(fù)雜條件分組統(tǒng)計
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 total
FROM orders
GROUP BY cust_id,
         ord_date
HAVING total > 250
  • 復(fù)雜條件分組統(tǒng)計示例1
db.orders.aggregate( [
   { $match: { status: 'A' } },
   {
     $group: {
        _id: "$cust_id",
        total: { $sum: "$price" }
     }
   }
] )

映射Sql語句

SELECT cust_id,
       SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
  • 復(fù)雜條件分組統(tǒng)計示例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 total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
HAVING total > 250
  • 表關(guān)聯(lián)
db.orders.aggregate( [
   { $unwind: "$items" },
   {
     $group: {
        _id: "$cust_id",
        qty: { $sum: "$items.qty" }
     }
   }
] )

映射Sql語句

SELECT cust_id,
       SUM(li.qty) as qty
FROM orders o,
     order_lineitem li
WHERE li.order_id = o.id
GROUP 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一樣驮樊,從一個單一集合中輸入數(shù)據(jù),然后將結(jié)果輸出到一個集合中片酝。通常在使用類似SQL中Group By操作時囚衔,Map/Reduce會是一個好的工具。

Map-Reduce
Map-Reduce

接口方法定義

db.collection.mapReduce(
    <map>,
    <reduce>,
    {
        out: <collection>,
        query: <document>,
        sort: <document>,
        limit: <number>,
        finalize: <function>,
        scope: <document>,
        jsMode: <boolean>,
        verbose: <boolean>,
        bypassDocumentValidation: <boolean>
    }
)

參數(shù)說明

  • mapReduce: 要執(zhí)行Map/Reduce集合的名字

  • map: map 函數(shù) (下面會詳細介紹)

  • reduce: reduce函數(shù)(下面會詳細介紹)

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

  • query: 設(shè)置查詢條件 <可選>

  • sort: 按某個鍵來排序 <可選>

  • limit: 指明從集合檢索文檔個數(shù)的最大值 <可選>

  • finalize: 對reduce結(jié)果做進一步處理 <可選>

  • scope: 指明通過map/reduce/finalize可以訪問到的變量 <可選>

  • jsMode: 指明Map/Reduce執(zhí)行過程中文檔保持JSON狀態(tài) <可選>

  • verbose: 提供關(guān)于任務(wù)執(zhí)行的統(tǒng)計數(shù)據(jù) <可選>

示例說明

?舉例說明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方法,就說我們后面的聚合計算需要哪些字段稳衬,?由于需要計算每個人的訂單總結(jié)霞捡,那么個人信息和加個肯定是我們需要的。

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

然后定義reduce方法薄疚,計算每個人的訂單價格碧信。

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

然后存儲最后的計算結(jié)果。

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

這樣一個簡單的Map-Reduce實例就完成了街夭,結(jié)果放在map_reduce_example中砰碴。

上面示例比較簡單,那么我們來一個復(fù)雜一點的例子板丽。

一條訂單記錄中呈枉,有sdk的名稱、數(shù)量、價格猖辫,那么要查詢出日期大于01/01/2012酥泞,所有訂單的總數(shù),以及?平均sdk價格啃憎。

首先還是定義個map函數(shù)芝囤。

var mapFunction2 = function() {
    for (var idx = 0; idx < this.items.length; idx++) {
        var key = this.items[idx].sku;
        var value = {
                        count: 1,
                        qty: this.items[idx].qty
                    };
        emit(key, value);
    }
};

然后算出sku的數(shù)量,和總價格辛萍。

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

    for (var idx = 0; idx < countObjVals.length; idx++) {
        reducedVal.count += countObjVals[idx].count;
        reducedVal.qty += countObjVals[idx].qty;
    }

    return reducedVal;
};

總價格出來后凡人,還要計算出平均價格。

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
    }
)

總結(jié)

以上就是我對MongoDB的示例總結(jié)传睹,本人是一個初學(xué)者耳幢,也有很多地方不懂,如果有錯誤的地方欧啤,歡迎指出睛藻。

相關(guān)資料

淺談MongoDB數(shù)據(jù)庫

普通查詢官方文檔

Sql和Mongo隱射表

聚合官方文檔

Map-Reduce官方文檔

Map-Reduce API

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市邢隧,隨后出現(xiàn)的幾起案子店印,更是在濱河造成了極大的恐慌,老刑警劉巖倒慧,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件按摘,死亡現(xiàn)場離奇詭異,居然都是意外死亡纫谅,警方通過查閱死者的電腦和手機炫贤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來付秕,“玉大人兰珍,你說我怎么就攤上這事⊙猓” “怎么了掠河?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猛计。 經(jīng)常有香客問我唠摹,道長,這世上最難降的妖魔是什么有滑? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任跃闹,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘望艺。我一直安慰自己苛秕,他們只是感情好,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布找默。 她就那樣靜靜地躺著艇劫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惩激。 梳的紋絲不亂的頭發(fā)上店煞,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音风钻,去河邊找鬼顷蟀。 笑死,一個胖子當著我的面吹牛骡技,可吹牛的內(nèi)容都是我干的鸣个。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼布朦,長吁一口氣:“原來是場噩夢啊……” “哼囤萤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起是趴,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤涛舍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后唆途,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體富雅,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年窘哈,在試婚紗的時候發(fā)現(xiàn)自己被綠了吹榴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡滚婉,死狀恐怖图筹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情让腹,我是刑警寧澤远剩,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站骇窍,受9級特大地震影響瓜晤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹纳,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一痢掠、第九天 我趴在偏房一處隱蔽的房頂上張望驱犹。 院中可真熱鬧,春花似錦足画、人聲如沸雄驹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽医舆。三九已至,卻和暖如春象缀,著一層夾襖步出監(jiān)牢的瞬間蔬将,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工央星, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霞怀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓莉给,卻偏偏與公主長得像里烦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子禁谦,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容