MongoDB 快速入門實(shí)戰(zhàn)教程基礎(chǔ)篇 一 :文檔的 CR操作

在本文中我們將學(xué)習(xí)流式聚合操作,并深入了解語句的執(zhí)行效率。然后深入學(xué)習(xí)能夠提高數(shù)據(jù)服務(wù)可用性的復(fù)制集。接著了解 MongoDB 的水平擴(kuò)展能力,學(xué)習(xí) MongoDB 數(shù)據(jù)的備份與還原方法,并為數(shù)據(jù)服務(wù)開啟訪問控制。

基礎(chǔ)篇 一 文檔的 CRUD 操作

CRUD 操作指的是對文檔進(jìn)行 createread,update and delete 操作,即增刪改查。文檔 CRUD 操作的內(nèi)容將分為 Create Operations, Read Operations, Update Operations, Delete OperationsCursor 等 5 個(gè)部分進(jìn)行介紹。

Create Operations

創(chuàng)建操作或者插入操作會(huì)向集合添加新的文檔。之前有提到過,如果插入時(shí)集合不存在,插入操作會(huì)創(chuàng)建對應(yīng)的集合。MongoDB 提供了 3 個(gè)插入文檔的方法:

插入單個(gè)文檔

其中,db.collection.insertOne() 用于向集合插入單個(gè)文檔。而 db.collection.insertMany()db.collection.insert() 可以向集合插入多個(gè)文檔。db.collection.insertOne() 示例如下:

> db.zenrust.insertOne({
... nickname: "Rust 之禪",
... name: "zenrust",
... types: "訂閱號(hào)",
... descs:"超酷人生,我用 Rust"
... })

自動(dòng)命令執(zhí)行后會(huì)返回一個(gè)結(jié)果文檔,文檔輸出如下:

{
    "acknowledged" : true,
    "insertedId" : ObjectId("5d157fe26fcb85935e9cb786")
}

這說明文檔插入成功。其中,acknowledged 代表本次操作的操作狀態(tài),狀態(tài)值包括 truefalseinsertedId 即該文檔的 _id。

提示:示例中的省略號(hào)是 MongoShell 的換行標(biāo)識(shí)符。換行標(biāo)識(shí)符對命令輸入和執(zhí)行并沒有影響,所以本文也不會(huì)注重風(fēng)格的統(tǒng)一,即示例中有時(shí)會(huì)帶有換行符,有時(shí)則不帶有換行符。

插入多個(gè)文檔

db.collection.insertMany() 示例如下:

> db.zenrust.insertMany([
... {nickname: "Rust 之禪", name: "zenrust", types: "訂閱號(hào)", descs: "超酷人生,我用 Rust"},
... {nickname: "進(jìn)擊的 Coder", name: "FightingCoder", types: "訂閱號(hào)", descs: "分享爬蟲技術(shù)和機(jī)器學(xué)習(xí)方面的編程經(jīng)驗(yàn)"}
... ])

由于本次插入了 2 個(gè)文檔,所以返回的結(jié)果文檔會(huì)顯示兩個(gè) _id。返回文檔內(nèi)容如下:

{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("5d1582136fcb85935e9cb787"),
        ObjectId("5d1582136fcb85935e9cb788")
    ]
}

db.collection.insert() 示例如下:

> db.zenrust.insert({title: "全面認(rèn)識(shí) RUST,掌控未來的雷電"})

示例演示的是單個(gè)文檔的插入,實(shí)際上插入多個(gè)文檔也是沒問題的。db.collection.insert() 插入單個(gè)文檔時(shí)返回的是一個(gè)帶有操作狀態(tài)的 WriteResult 對象:WriteResult({ "nInserted" : 1 }) 。其中,nInserted 表明了插入文檔的總數(shù)。但如果插入操作遇到錯(cuò)誤,那么 WriteResult 對象將包含錯(cuò)誤提示信息。

db.collection.insert() 插入多個(gè)文檔的示例如下:

> db.zenrust.insert([{nickname: "進(jìn)擊的 Coder"}, {nickname: "Rust 之禪"}])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 2,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})

可以看到,db.collection.insert() 插入多個(gè)文檔和插入單個(gè)文檔得到的返回結(jié)果是不同的。

Read Operations

MongoDB 提供了 db.collection.find() 方法從集合中讀取文檔。在開始練習(xí)之前,需要準(zhǔn)備用于練習(xí)的基礎(chǔ)數(shù)據(jù)。在 MongoShell 中執(zhí)行以下文檔插入操作:

> db.inven.insertMany([
   { name: "詹姆斯", number: 6, attribute: { h: 203, w: 222, p: "前鋒" }, status: "A" },
   { name: "韋德", number: 3, attribute: { h: 193, w: 220, p: "得分后衛(wèi)" }, status: "R" },
   { name: "科比", number: 24, attribute: { h: 198, w: 212, p: "得分后衛(wèi)" }, status: "R" },
   { name: "姚明", number: 11, attribute: { h: 226, w: 308, p: "中鋒" }, status: "R" },
   { name: "喬丹", number: 23, attribute: { h: 198, w: 216, p: "得分后衛(wèi)" }, status: "R" }
])

查詢文檔

將一個(gè)空位當(dāng)作為查詢過濾器參數(shù)傳遞給 db.collection.find() 方法就可以得到所有文檔,對應(yīng)示例如下:

> db.inven.find({})

或者什么都不傳,直接使用 find(),對應(yīng)示例如下:

> db.inven.find()

這等效于 SQL 中的 SELECT * FROM inven。

指定等式條件

如果要指定相等條件,可以使用 {<field1>: <value1>, ...} 這樣的過濾表達(dá)式,例如過濾出已退役球員("R" 代表退役)的查詢語句如下:

> db.inven.find({status: "R"})
{ "_id" : ObjectId("5d159e794d3d891430a2512e"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a2512f"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25130"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25131"), "name" : "喬丹", "number" : 23, "attribute" : { "h" : 198, "w" : 216, "p" : "得分后衛(wèi)" }, "status" : "R" }

這等效于 SQL 中的 SELECT * FROM inven WHERE status = "R"。

根據(jù)嵌套文檔字段查詢

我們還可以根據(jù)嵌入式文檔中的字段進(jìn)行查詢,例如過濾出球員屬性中身高為 193 的球員,對應(yīng)示例如下:

> db.inven.find({"attribute.h": 193})
{ "_id" : ObjectId("5d159e794d3d891430a2512e"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }

要注意的是,訪問嵌入式文檔中的字段時(shí)使用的并不是 attribute.h,而是使用 "attribute.h"

查詢與投影操作

查詢的情況非常復(fù)雜,MongoDB 提供了多種查詢操作符來應(yīng)對這些問題。MongoDB 提供的查詢操作符分為以下幾類:

  • 比較查詢操作符
  • 邏輯查詢操作符
  • 元素查詢操作符
  • 評估查詢操作符
  • 地理空間查詢操作符
  • 數(shù)組查詢操作符
  • 按位查詢操作符

接下來,我們將學(xué)習(xí)每一種查詢操作符的規(guī)則和語法。

比較查詢操作符

比較是最常見的操作之一,它分為同類型比較和非同類型比較。在面對不同的 BSON 類型值時(shí),比較的并不是值的大小,而是值的類型,即按類比較。MongoDB 使用以下比較順序,順序從低到高:

  • MinKey (internal type)
  • Null
  • Numbers (ints, longs, doubles, decimals)
  • Symbol, String
  • Object
  • Array
  • BinData
  • ObjectId
  • Boolean
  • Date
  • Timestamp
  • Regular Expression
  • MaxKey (internal type)

同類型比較的情況則稍微復(fù)雜一些。數(shù)字類型比較的是值的大小,例如 5 大于 3。字符串類型比較的是其值的二進(jìn)制,例如 R 大于 A 是因?yàn)?R 的二進(jìn)制值 0101 0010 大于 A 的二進(jìn)制值 0100 0001。數(shù)組的小于比較或者升序排序比較的是數(shù)組中的最小元素,大于比較或降序排序比較的是數(shù)組中的最大元素。我們可以通過一個(gè)例子來了解這些知識(shí)。準(zhǔn)備如下數(shù)據(jù):

> db.arrs.insertMany([ 
... {name: "James", attr: [5, 6, 7]}, 
... {name: "Wade", attr: [1, 7, 8]}, 
... {name: "Kobe", attr: [1, 9, 9]}, 
... {name: "Bosh", attr: [2, 9, 9]}
... ])

假設(shè)要將文檔按 name 升序排序,即字符串升序排序。對應(yīng)示例如下:

> db.arrs.find().sort({name: 1})
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4f"), "name" : "Bosh", "attr" : [ 2, 9, 9 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4c"), "name" : "James", "attr" : [ 5, 6, 7 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4e"), "name" : "Kobe", "attr" : [ 1, 9, 9 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4d"), "name" : "Wade", "attr" : [ 1, 7, 8 ] }

排序結(jié)果為 Bosh- James - Kobe - Wade,那么字符串降序排序的結(jié)果一定是 Wade - Kobe - James - Bosh。對應(yīng)示例如下:

> db.arrs.find().sort({name: -1})
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4d"), "name" : "Wade", "attr" : [ 1, 7, 8 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4e"), "name" : "Kobe", "attr" : [ 1, 9, 9 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4c"), "name" : "James", "attr" : [ 5, 6, 7 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4f"), "name" : "Bosh", "attr" : [ 2, 9, 9 ] }

現(xiàn)有:[ 5, 6, 7 ], [ 1, 7, 8 ], [ 1, 9, 9 ], [ 2, 9, 9 ] 4 個(gè)數(shù)組,上面提到,數(shù)組升序排序比較的是最小元素。4 個(gè)數(shù)組中最小的值分別是 5, 1, 1, 2,其中數(shù)組 [1, 7, 8] 和數(shù)組 [1, 9, 9] 的最小值相同,則比較第二小的值,即 7, 9。那么正確的升序排序結(jié)果因該是 [ 1, 7, 8 ], [ 1, 9, 9 ], [ 2, 9, 9 ], [5, 6, 7]。數(shù)組升序排序命令如下:

> db.arrs.find().sort({attr: 1})
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4d"), "name" : "Wade", "attr" : [ 1, 7, 8 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4e"), "name" : "Kobe", "attr" : [ 1, 9, 9 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4f"), "name" : "Bosh", "attr" : [ 2, 9, 9 ] }
{ "_id" : ObjectId("5d1eb7fef91d329d7e731d4c"), "name" : "James", "attr" : [ 5, 6, 7 ] }

排序結(jié)果與分析結(jié)果一致。數(shù)組降序排序比較的是最大元素。4 個(gè)數(shù)組中最大的值分別是 7, 8, 9, 9,其中數(shù)組 [1, 9, 9][2, 9, 9] 的最大值和第二大的值相同,則比較第三大的值,即 1, 2。那么正確的降序排序結(jié)果應(yīng)該是 [2, 9, 9], [1, 9, 9], [1, 7, 8], [5, 6, 7]。數(shù)組降序排序命令如下:

> db.els.find().sort({attr: -1})
{ "_id" : ObjectId("5d1edd28eb81ddef9df74fff"), "name" : "Kobe", "attr" : [ 1, 9, 9 ] }
{ "_id" : ObjectId("5d1edd28eb81ddef9df75000"), "name" : "Bosh", "attr" : [ 2, 9, 9 ] }
{ "_id" : ObjectId("5d1edd28eb81ddef9df74ffe"), "name" : "Wade", "attr" : [ 1, 7, 8 ] }
{ "_id" : ObjectId("5d1edd28eb81ddef9df74ffd"), "name" : "James", "attr" : [ 5, 6, 7 ] }

排序結(jié)果和分析結(jié)果并不同,這是為什么呢?難道不是按最大元素比較大小嗎?

文檔中并沒有提到,但我們可以通過例子尋找答案。準(zhǔn)備以下數(shù)據(jù):

> db.parts.insertMany([
... {name: 1, attr: [9, 9, 0, 5]},
... {name: 2, attr: [9, 9, 0, 1]},
... {name: 3, attr: [9, 0, 9, 0]},
... {name: 4, attr: [9, 8, 7, 6]},
... {name: 5, attr: [5, 2, 3, 6]},
... {name: 6, attr: [9, 0, 0, 0]},
... {name: 7, attr: [30, 0]},
... {name: 8, attr: [22, 0]}, 
... {name: 9, attr: [30, 5]},
... {name: 10, attr: [30, 3]}
... ])

數(shù)組降序排序示例如下:

> db.parts.find().sort({attr: -1})
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb121"), "name" : 7, "attr" : [ 30, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb123"), "name" : 9, "attr" : [ 30, 5 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb124"), "name" : 10, "attr" : [ 30, 3 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb122"), "name" : 8, "attr" : [ 22, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11b"), "name" : 1, "attr" : [ 9, 9, 0, 5 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11c"), "name" : 2, "attr" : [ 9, 9, 0, 1 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11d"), "name" : 3, "attr" : [ 9, 0, 9, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11e"), "name" : 4, "attr" : [ 9, 8, 7, 6 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb120"), "name" : 6, "attr" : [ 9, 0, 0, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11f"), "name" : 5, "attr" : [ 5, 2, 3, 6 ] }

數(shù)組升序排序示例如下:

> db.parts.find().sort({attr: 1})
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11b"), "name" : 1, "attr" : [ 9, 9, 0, 5 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11c"), "name" : 2, "attr" : [ 9, 9, 0, 1 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11d"), "name" : 3, "attr" : [ 9, 0, 9, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb120"), "name" : 6, "attr" : [ 9, 0, 0, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb121"), "name" : 7, "attr" : [ 30, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb122"), "name" : 8, "attr" : [ 22, 0 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11f"), "name" : 5, "attr" : [ 5, 2, 3, 6 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb124"), "name" : 10, "attr" : [ 30, 3 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb123"), "name" : 9, "attr" : [ 30, 5 ] }
{ "_id" : ObjectId("5d1eef27882bacc4ec4cb11e"), "name" : 4, "attr" : [ 9, 8, 7, 6 ] }

根據(jù)以上結(jié)果,我們可以推測出排序規(guī)律:如果被比較的值相同,那么就按照插入順序(即 ObjectId)排序。降序排序比較的是最大元素,即 30 - 30 - 30 - 22 -9 - 9 - 9 - 9 - 9 - 6,其中 309 均有重復(fù)。可以發(fā)現(xiàn):

  • 第一個(gè) 30 對應(yīng)的 name 值為 7,第二個(gè) 30 對應(yīng)的 name 值為 9,第三個(gè) 30 對應(yīng)的 name 值為 10;
  • 第一個(gè) 9 對應(yīng)的 name 值為 1,第二個(gè) 9 對應(yīng)的 name 值為 2,第三個(gè) 9 對應(yīng)的 name 值為 3,第四個(gè) 9 對應(yīng)的 name 值為 4,第五個(gè) 9 對應(yīng)的 name 值為 6;

升序排序比較的是最小元素,即 0 - 0 - 0 - 0 - 0 - 0 - 2 - 3 - 5 - 6,其中重復(fù)的只有 00 對應(yīng)的 name 值依次為 1, 2, 3, 6, 7, 8 。無論是升序排序還是降序排序,實(shí)際得到的結(jié)果與我們推測出來的規(guī)律相同,這說明我們推測出來的規(guī)律是正確的。其他類型的比較或排序規(guī)則可查閱官方文檔 comparison-sort-order

MongoDB 提供了一系列用于比較的比較符,它們分別是:

名稱 描述
$eq 匹配等于指定值的值
$gt 匹配大于指定值的值
$gte 匹配大于或等于指定值的值
$in 匹配數(shù)組中指定的任何值
$lt 匹配小于指定值的值
$lte 匹配小于或等于指定值的值
$ne 匹配所有不等于指定值的值
$nin 不匹配數(shù)組中指定的任何值

其中,$eq$gte, $lt, $lte$gt,$ne 的語法是相同的。以 $eq 為例,其語法格式如下:

{ <field>: { $eq: <value> } }

假設(shè)要匹配集合 els 中名稱為 James 的文檔,對應(yīng)示例如下:

> db.els.find({name: {$eq: "James"}})
{ "_id" : ObjectId("5d1edd28eb81ddef9df74ffd"), "name" : "James", "attr" : [ 5, 6, 7 ] }

這等效于 SQL 中的 SELECT * FROM els WHERE name = "James"。

$in$nin 的語法相同。以 $in 為例,其格式如下:

{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }

假設(shè)要過濾出集合 els 中文檔字段 attr 中包含 6 或者 9 的文檔,對應(yīng)示例如下:

> db.els.find({attr: {$in: [6, 9]}})
{ "_id" : ObjectId("5d1edd28eb81ddef9df74ffd"), "name" : "James", "attr" : [ 5, 6, 7 ] }
{ "_id" : ObjectId("5d1edd28eb81ddef9df74fff"), "name" : "Kobe", "attr" : [ 1, 9, 9 ] }
{ "_id" : ObjectId("5d1edd28eb81ddef9df75000"), "name" : "Bosh", "attr" : [ 2, 9, 9 ] }

另外,$in$nin 均支持正則表達(dá)式。例如要過濾出集合 invenname 字段值以 或者 開頭的文檔,對應(yīng)示例如下:

> db.inven.find({name: {$in: [/^詹/, /^韋/]}})
{ "_id" : ObjectId("5d200b986c39176e3a421af2"), "name" : "詹姆斯", "number" : 6, "attribute" : { "h" : 203, "w" : 222, "p" : "前鋒" }, "status" : "A" }
{ "_id" : ObjectId("5d200b986c39176e3a421af3"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }

反過來,過濾出集合 invenname 字段值非 或者非 開頭的文檔。對應(yīng)示例如下:

> db.inven.find({name: {$nin: [/^詹/, /^韋/]}})
{ "_id" : ObjectId("5d200b986c39176e3a421af4"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d200b986c39176e3a421af5"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }
{ "_id" : ObjectId("5d200b986c39176e3a421af6"), "name" : "喬丹", "number" : 23, "attribute" : { "h" : 198, "w" : 216, "p" : "得分后衛(wèi)" }, "status" : "R" }

以上就是比較查詢操作符的相關(guān)知識(shí),更多關(guān)于比較查詢操作符的知識(shí)可查閱官方文檔 Comparison Query Operators。

邏輯查詢操作符

MongoDB 中的邏輯查詢操作符共有 4 種,它們是:

名稱 描述
$and 匹配符合多個(gè)條件的文檔
$not 匹配不符合條件的文檔
$nor 匹配不符合多個(gè)條件的文檔
$or 匹配符合任一條件的文檔

其中,$and, $nor$or 語法格式相同:

{ $keyword: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }

語法中的 keyword 代表 and/nor/or。而 $not 語法格式如下

{ field: { $not: { <operator-expression> } } }

$and 是隱式的,這意味著我們不必在查詢語句中表明 andAND 。 假設(shè)要過濾出集合 inven球衣號(hào)大于 10退役球員,此時(shí)有兩個(gè)條件:球衣號(hào)大于 10,退役球員。對應(yīng)示例如下:

> db.inven.find({status: "R", number: {$gt: 10}})
{ "_id" : ObjectId("5d159e794d3d891430a2512f"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25130"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25131"), "name" : "喬丹", "number" : 23, "attribute" : { "h" : 198, "w" : 216, "p" : "得分后衛(wèi)" }, "status" : "R" }

這等效于 SQL 中的 SELECT * FROM inven WHERE status = "R" AND number > 10。當(dāng)然,也可以采用顯式寫法,對應(yīng)示例如下:

> db.inven.find({$and: [{status: "R"}, {number: {$gt: 10}}]})
{ "_id" : ObjectId("5d159e794d3d891430a2512f"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25130"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25131"), "name" : "喬丹", "number" : 23, "attribute" : { "h" : 198, "w" : 216, "p" : "得分后衛(wèi)" }, "status" : "R" }

$or, $not$nor 均采用顯式寫法。假設(shè)要過濾出集合 inven球衣號(hào)大于 10 或者 退役球員 的文檔,對應(yīng)示例如下:

> db.inven.find({$or: [{status: "R"}, {number: {$gt: 10}}]})
{ "_id" : ObjectId("5d159e794d3d891430a2512e"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a2512f"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25130"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }
{ "_id" : ObjectId("5d159e794d3d891430a25131"), "name" : "喬丹", "number" : 23, "attribute" : { "h" : 198, "w" : 216, "p" : "得分后衛(wèi)" }, "status" : "R" }

這等效于 SQL 中的 SELECT * FROM inven WHERE status = "R" OR number > 10。

假設(shè)要過濾出集合 invennumber 不等于 11number 不等于 23 的文檔,對應(yīng)示例如下:

> db.inven.find({$nor: [{number: 23}, {number: 11}]})
{ "_id" : ObjectId("5d200b986c39176e3a421af2"), "name" : "詹姆斯", "number" : 6, "attribute" : { "h" : 203, "w" : 222, "p" : "前鋒" }, "status" : "A" }
{ "_id" : ObjectId("5d200b986c39176e3a421af3"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d200b986c39176e3a421af4"), "name" : "科比", "number" : 24, "attribute" : { "h" : 198, "w" : 212, "p" : "得分后衛(wèi)" }, "status" : "R" }

假設(shè)要過濾出集合 invennumber 不大于 20 的文檔,對應(yīng)示例如下:

> db.inven.find({number: {$not: {$gt: 20}}})
{ "_id" : ObjectId("5d200b986c39176e3a421af2"), "name" : "詹姆斯", "number" : 6, "attribute" : { "h" : 203, "w" : 222, "p" : "前鋒" }, "status" : "A" }
{ "_id" : ObjectId("5d200b986c39176e3a421af3"), "name" : "韋德", "number" : 3, "attribute" : { "h" : 193, "w" : 220, "p" : "得分后衛(wèi)" }, "status" : "R" }
{ "_id" : ObjectId("5d200b986c39176e3a421af5"), "name" : "姚明", "number" : 11, "attribute" : { "h" : 226, "w" : 308, "p" : "中鋒" }, "status" : "R" }

這個(gè)例子將比較查詢操作符和邏輯查詢操作符結(jié)合使用,實(shí)現(xiàn)了更細(xì)致的查詢。

更多關(guān)于邏輯查詢操作符的知識(shí)可查閱官方文檔 Logical Query Operators。

元素查詢操作符

MongoDB 中的元素查詢操作符只有 2 種,它們是:

名稱 描述
$exists 匹配具有指定字段的文檔
$type 匹配字段值符合類型的文檔

exists

在開始學(xué)習(xí) $exists 前,我們需要準(zhǔn)備以下數(shù)據(jù):

> db.elem.insertMany([
... {title: "湖人今夏交易頻繁", author: "Asyncins", date: "2019-07-01", article: "..."},
... {title: "詹姆斯現(xiàn)身 MIA-CHN 比賽現(xiàn)場", date: "2019-07-06", article: "..."},
... {title: "倫納德遲遲不肯表態(tài)", author: "Asyncins"}
... ])

$exists 語法格式如下:

{ field: { $exists: <boolean> } }

假設(shè)要過濾出集合 elem 中包含 author 字段的文檔,對應(yīng)示例如下:

> db.elem.find({author: {$exists: true}})
{ "_id" : ObjectId("5d203f1a6c39176e3a421af7"), "title" : "湖人今夏交易頻繁", "author" : "Asyncins", "date" : "2019-07-01", "article" : "..." }
{ "_id" : ObjectId("5d203f1a6c39176e3a421af9"), "title" : "倫納德遲遲不肯表態(tài)", "author" : "Asyncins" }

反過來,要過濾出集合 elem 中不包含 author 字段的文檔,對應(yīng)示例如下:

> db.elem.find({author: {$exists: false}})
{ "_id" : ObjectId("5d203f1a6c39176e3a421af8"), "title" : "詹姆斯現(xiàn)身 MIA-CHN 比賽現(xiàn)場", "date" : "2019-07-06", "article" : "..." }

type

在開始學(xué)習(xí) $type 前,我們需要準(zhǔn)備如下數(shù)據(jù):

> db.ops.insertMany([
...     {title: "北京高溫持續(xù),注意避暑", weight: 5, rec: false},
...     {title: "廣西持續(xù)降雨,最大降雨量 200 ml", weight: 5, rec: false},
...     {title: "高考分?jǐn)?shù)線已出,高分學(xué)子增多", weight: "hot", rec: true},
...     {title: "秋老虎是真是假?", weight: 3, rec: false}
... ])

$type 語法如下:

{ field: { $type: <BSON type> } }

它也支持陣列寫法:

{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }

假設(shè)要過濾出 weight 值類型為 String 的文檔,對應(yīng)示例如下:

> db.ops.find({weight: {$type: "string"}})
{ "_id" : ObjectId("5d1838eb51b88758035de5b7"), "title" : "高考分?jǐn)?shù)線已出,高分學(xué)子增多", "weight" : "hot", "rec" : true }

同理,過濾出 weight 值類型為 Number 的文檔的對應(yīng)示例如下:

> db.ops.find({weight: {$type: "number"}})
{ "_id" : ObjectId("5d1838eb51b88758035de5b5"), "title" : "北京高溫持續(xù),注意避暑", "weight" : 5, "rec" : false }
{ "_id" : ObjectId("5d1838eb51b88758035de5b6"), "title" : "廣西持續(xù)降雨,最大降雨量 200 ml", "weight" : 5, "rec" : false }
{ "_id" : ObjectId("5d1838eb51b88758035de5b8"), "title" : "秋老虎是真是假?", "weight" : 3, "rec" : false }

陣列寫法中的 BSON 類型為 or 關(guān)系,例如要過濾出 weight 值類型為 String 或者 Number 的文檔,對應(yīng)示例如下:

> db.ops.find({weight: {$type: ["string", "number"]}})
{ "_id" : ObjectId("5d1838eb51b88758035de5b5"), "title" : "北京高溫持續(xù),注意避暑", "weight" : 5, "rec" : false }
{ "_id" : ObjectId("5d1838eb51b88758035de5b6"), "title" : "廣西持續(xù)降雨,最大降雨量 200 ml", "weight" : 5, "rec" : false }
{ "_id" : ObjectId("5d1838eb51b88758035de5b7"), "title" : "高考分?jǐn)?shù)線已出,高分學(xué)子增多", "weight" : "hot", "rec" : true }
{ "_id" : ObjectId("5d1838eb51b88758035de5b8"), "title" : "秋老虎是真是假?", "weight" : 3, "rec" : false }

這等效于 SELECT * FROM ops WHERE weight.type = string OR weight.type = number 這樣的 SQL 偽代碼表示。要注意的是,$type 支持所有 BSON 類型的字符串標(biāo)識(shí)符和整數(shù)標(biāo)識(shí)符,例如 String 類型的字符串標(biāo)識(shí)符 sting 及其整數(shù)標(biāo)識(shí)符 2,即 {$type: "string"} 等效于 {$type: 2}。

更多關(guān)于元素查詢操作符的知識(shí)可查閱官方文檔 Element Query Operators

評估查詢操作符

MongoDB 中的評估查詢操作符共有 6 種,它們是:

名稱 描述
$expr 允許在查詢語句中使用聚合表達(dá)式
$jsonSchema 根據(jù)給定的 JSON 模式驗(yàn)證文檔
$mod 對字段的值執(zhí)行模運(yùn)算,并選擇具有指定結(jié)果的文檔
$regex 匹配與正則表達(dá)式規(guī)則相符的文檔
$text 執(zhí)行文本搜索
$where 匹配滿足 JavaScript 表達(dá)式的文檔

expr

在學(xué)習(xí) $expr 前,我們需要準(zhǔn)備以下數(shù)據(jù):

> db.acbook.insertMany([
... {_id: 1, category: "衣", 預(yù)算: 300, 開支: 600},
... {_id: 2, category: "食", 預(yù)算: 1000, 開支: 600},
... {_id: 3, category: "住", 預(yù)算: 800, 開支: 800},
... {_id: 4, category: "行", 預(yù)算: 220, 開支: 360},
... {_id: 5, category: "醫(yī)", 預(yù)算: 200, 開支: -50}
... ])

$expr 語法格式如下:

{ $expr: { <expression> } }

假設(shè)要過濾出超出預(yù)算的文檔,對應(yīng)示例如下:

> db.acbook.find({$expr: {$gt: ["$開支", "$預(yù)算"]}})
{ "_id" : 1, "category" : "衣", "預(yù)算" : 300, "開支" : 600 }
{ "_id" : 4, "category" : "行", "預(yù)算" : 220, "開支" : 360 }

示例中使用了 $gt 表達(dá)式,用于比較 開支預(yù)算。我們也可以使用 $lt 表達(dá)式,對應(yīng)命令如下:

> db.acbook.find({$expr: {$lt: ["$預(yù)算", "$開支"]}})
{ "_id" : 1, "category" : "衣", "預(yù)算" : 300, "開支" : 600 }
{ "_id" : 4, "category" : "行", "預(yù)算" : 220, "開支" : 360 }

$expr 支持的表達(dá)式非常多,詳見官方文檔 Expressions。

mod

$mod 的作用是對字段的值執(zhí)行模運(yùn)算,并選擇具有指定結(jié)果的文檔。其語法格式如下:

{ field: { $mod: [ divisor, remainder ] } }

假設(shè)要過濾出集合 acbook 中滿足 mod(開支, 6) = 0 的文檔。對應(yīng)示例如下:

> db.acbook.find({開支: {$mod: [6, 0]}})
{ "_id" : 1, "category" : "衣", "預(yù)算" : 300, "開支" : 600 }
{ "_id" : 2, "category" : "食", "預(yù)算" : 1000, "開支" : 600 }
{ "_id" : 4, "category" : "行", "預(yù)算" : 220, "開支" : 360 }

mod(600, 6) = 0, mod(360, 6) = 0。同理,要過濾出集合 acbook 中滿足 mod(開支, 6) = 2 的文檔,對應(yīng)示例如下:

> db.acbook.find({開支: {$mod: [6, 2]}})
{ "_id" : 3, "category" : "住", "預(yù)算" : 800, "開支" : 800 }

mod(800, 6) = 2。要注意的是,$mod 只接受 2 個(gè)參數(shù):divisorremainder。如果只傳入 1 個(gè)參數(shù),例如 db.acbook.find({開支: {$mod: [6]}}), 就會(huì)得到如下錯(cuò)誤提示:

Error: error: {
    "ok" : 0,
    "errmsg" : "malformed mod, not enough elements",
    "code" : 2,
    "codeName" : "BadValue"
}

不傳入值或傳入多個(gè)值也是不被允許的,在返回文檔中的 errmsg 處會(huì)給出對應(yīng)的提示。例如不傳入值對應(yīng)的提示為 malformed mod, not enough elements,而多個(gè)值對應(yīng)的提示為 malformed mod, too many elements

提示:在 2.6 版本中,傳入單個(gè)值時(shí)會(huì)默認(rèn)補(bǔ)上 0,傳入多個(gè)值時(shí)會(huì)忽略多余的值。例如 db.acbook.find({開支: {$mod: [6]}})db.acbook.find({開支: {$mod: [6, 2, 3, 5]}}) 等效于 db.acbook.find({開支: {$mod: [6, 0]}})。但 4.0 版本不允許這樣做。

regex

MongoDB 提供的 $regex 讓開發(fā)者可以在查詢語句中使用正則表達(dá)式,這實(shí)在是令人驚喜。MongoDB 中的正則表達(dá)式是 PCRE,即 Perl 語言兼容的正則表達(dá)式。$regex 語法格式如下:

{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }

三種格式任選其一,特定語法的使用限制可參考 $regex vs./pattern/Syntax 。也可以用下面這種語法:

{ <field>: /pattern/<options> }

正則表達(dá)式中有一些特殊選項(xiàng)(又稱模式修正符),例如不區(qū)分大小寫或允許使用點(diǎn)字符等,MongoDB 中支持的選項(xiàng)如下:

選項(xiàng) 描述 語法限制
i 不區(qū)分大小寫字母。
m 支持多行匹配。
x 忽略空格和注釋(#),注釋以 \n 結(jié)尾。 必須使用 $option
s 允許點(diǎn)(.)字符匹配括換行符在內(nèi)的所有字符,也可以理解為允許點(diǎn)(.)字符匹配換行符后面的字符。 必須使用 $option

在開始學(xué)習(xí)之前,準(zhǔn)備以下數(shù)據(jù):

> db.regexs.insertMany([
... {_id: 1, nickname: "abc123", desc: "Single Line Description."},
... {_id: 2, nickname: "abc299", desc: "First line \nSecond line"},
... {_id: 3, nickname: "xyz5566", desc: "Many spaces before    line"},
... {_id: 4, nickname: "xyz8205", desc: "Multiple\nline description"}
... ])

假設(shè)要過濾出 nickname 值結(jié)尾為 299 的文檔,對應(yīng)示例如下:

> db.regexs.find({nickname: {$regex: /299$/}})
{ "_id" : 2, "nickname" : "abc299", "desc" : "First line \nSecond line" }

這相當(dāng)于 SQL 中的模糊查詢,對應(yīng)的 SQL 語句為 SELECT * FROM regexs WHERE nickname like "%299"。接下來使用模式修正符 i 實(shí)現(xiàn)不區(qū)分大小寫的匹配,對應(yīng)示例如下:

> db.regexs.find({nickname: {$regex: /^aBc/i}})
{ "_id" : 1, "nickname" : "abc123", "desc" : "Single Line Description." }
{ "_id" : 2, "nickname" : "abc299", "desc" : "First line \nSecond line" }

這個(gè)語句的作用是過濾出集合 regexsnickname 字段值由 aBc 開頭的文檔,并在匹配時(shí)忽略大小寫字母。接下來我們再通過一個(gè)例子了解模式修正符 m 的用法和作用,對應(yīng)示例如下:

> db.regexs.find({desc: {$regex: /^s/, $options: "im"}})
{ "_id" : 1, "nickname" : "abc123", "desc" : "Single Line Description." }
{ "_id" : 2, "nickname" : "abc299", "desc" : "First line \nSecond line" }

這個(gè)語句的作用是過濾出集合 regexsdesc 字段值由 s 開頭的文檔,匹配時(shí)忽略大小寫字母,并進(jìn)行多行匹配。雖然 _id 為 2 的文檔中的 desc 并不是 sS 開頭,但由于使用了模式修正符 m,所以能夠匹配到 \n 符號(hào)后面的 Second。如果沒有使用模式修正符 m,那么匹配結(jié)果將會(huì)是 { "_id" : 1, "nickname" : "abc123", "desc" : "Single Line Description." }

點(diǎn)字符和星號(hào)在正則表達(dá)式中是最常用的組合,MongoDB 也支持這個(gè)組合。假設(shè)要過濾出集合 regexsdesc 字段值由 m 開頭且 line 結(jié)尾的文檔,對應(yīng)示例如下:

> db.regexs.find({desc: {$regex: /m.*line/, $options: "is"}})
{ "_id" : 3, "nickname" : "xyz5566", "desc" : "Many spaces before    line" }
{ "_id" : 4, "nickname" : "xyz8205", "desc" : "Multiple\nline description" }

如果不使用模式修正符 s.* 組合也是可用的,但無法匹配到換行符后面的內(nèi)容,那么匹配結(jié)果將會(huì)是 { "_id" : 3, "nickname" : "xyz5566", "desc" : "Many spaces before line" }。

模式修正符 x 的描述為:“忽略空格和注釋(#),注釋以 \n 結(jié)尾”。這理解起來有些困難,但你不用擔(dān)心,只要跟著本文指引和案例,就能夠掌握模式修正符 x 的正確用法。示例如下:

> var pattern = "abc #category code\n123 #item number"
> db.regexs.find({nickname: {$regex: pattern, $options: "x"}})
{ "_id" : 1, "nickname" : "abc123", "desc" : "Single Line Description." }

正則規(guī)則為 abc #category code\n123 #item number,根據(jù)模式修正符 x 的描述,我們可以將其轉(zhuǎn)換為 abc123。即過濾出集合 regexsnickname 值為 abc123 的文檔,所以執(zhí)行結(jié)果為:

{ "_id" : 1, "nickname" : "abc123", "desc" : "Single Line Description." }

再來看一個(gè)示例:

> var pattern = "abc #category code\n xyz#item number"
> db.regexs.find({nickname: {$regex: pattern, $options: "x"}})

還有 55% 的精彩內(nèi)容
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
支付 ¥15.99 繼續(xù)閱讀

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容