對於開發人員而言,資料庫的增刪改查操作才是最常使用的功能,學習 MongoDB 時還需對這些功能熟記於心才行啊。我就在這裡做一個簡單的備忘,多看官方文檔才是正道。 ...
連接
標準 URI 連接語法
通常,可以設定標準的 URI 連接語法,作為連接配置:
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
mongodb://
: 固定的格式,即指定當前的 URI 是標準的 MongoDB 連接語法username:password@
: 可選項,如果設置,在連接資料庫伺服器之後,驅動會嘗試登錄這個資料庫host1
: 必須的,至少指定一個 host,它指定了要連接伺服器的地址。如果要連接副本及集,需要指定多個主機地址portX
: 可選的指定埠,如果不填,預設為27017
/database
: 如果指定賬號和密碼,連接並驗證登錄指定資料庫。若不指定,預設打開 test 資料庫?options
: 連接選項,如果沒有指定資料庫,則前面需要加上/
。所有連接選項都是鍵值對name=value
,鍵值對之間通過&
或;
隔開
連接選項
標準的連接格式包含了多個選項,如下所示:
replicaSet=name
: 驗證副本集的名稱slaveOk=true|false
: 選擇連接主伺服器的方式true
: 表示有從伺服器,當connect=direct
時會連接第一臺機器,即使這台不是主伺服器;當connect=replicaSet
時會發送所有的寫請求到主並且把讀請求分佈在其他從伺服器false
: 表示無從伺服器,當connect=direct
時會自動找尋主伺服器;當connect=replicaSet
時僅連接主伺服器,並且所有的讀寫命令都連接到主伺服器
safe=true|false
: 設置為true
時,在執行更新操作之後,驅動都會發送getLastError
命令來確保更新成功w=n
: 驅動添加{w: n}
到getLastError
命令,應用於safe=true
wtimeoutMS=ms
: 驅動添加{wtimeout: ms}
到getLastError
命令,應用於safe=true
fsync=true|false
: 驅動添加{fsync: true}
到getLastError
命令,應用於safe=true
journal=true|false
: 如果設置為true
,同步到日誌(在提交到資料庫前寫入到實體中),應用於safe=true
connectTimeoutMS=ms
: 可以打開連接的時間socketTimeoutMS=ms
: 發送和接受 socket 的時間
連接命令格式
使用 mongosh 連接 MongoDB 時,也支持命令選項的方式添加配置:
--host arg
: 指定資料庫地址--port arg
: 指定資料庫埠-u [ --username ] arg
: 鑒權的賬號-p [ --password ] arg
: 鑒權的密碼
插入文檔
插入校驗
MongoDB 會對要插入的數據進行最基本的檢查:檢查文檔的基本結構,如缺少 _id
鍵會自動添加一個、是否包含非 UTF-8 字元、是否使用了無法識別的類型、檢查文檔大小等。
其中,檢查文檔大小主要因為 MongoDB 限制了所有文檔必須小於 16MB,主要是為了防止不良的模式設計並確保性能上的一致。
要查看文檔的 BSON 大小,可以在 shell 中執行 Object.bsonsize(doc)
查看位元組大小。
單個插入
官方文檔:db.collection.insertOne() — MongoDB Manual
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)
批量插入
官方文檔:db.collection.insertMany() — MongoDB Manual
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
通過傳輸 ordered=true
可以確保文檔按提供的順序插入,指定為 false
則允許 MongoDB 重新排列插入的順序以提高性能,預設為 true
值。
以有序插入的方式使用 insertMany
出現錯誤將會阻塞後續的插入動作,無需插入的方式則不管其他插入是否出現了錯誤。
刪除文檔
單個刪除
官方文檔:db.collection.deleteOne() — MongoDB Manual
db.collection.deleteOne(
<filter>,
{
writeConcern: <document>,
collation: <document>,
hint: <document|string> // Available starting in MongoDB 4.4
}
)
使用 hint
參數可以指定 filter
查詢時命中的索引,這對於複雜的索引結構時可以提升部分效率。
批量刪除
官方文檔:db.collection.deleteMany() — MongoDB Manual
db.collection.deleteMany(
<filter>,
{
writeConcern: <document>,
collation: <document>,
hint: <document|string> // Available starting in MongoDB 4.4
}
)
單個刪除並返回
官方文檔:db.collection.findOneAndDelete() — MongoDB Manual
db.collection.findOneAndDelete(
<filter>,
{
writeConcern: <document>,
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
collation: <document>
}
)
更新文檔
更新文檔是原子操作:如果兩個更新同時發生,那麼首先到達伺服器的更新會先被執行,然後再執行下一個更新。
因此,相互衝突的更新可以安全地迅速接連完成,而不會破壞任何文檔:最後一次更新將“成功”。如果不想使用預設行為,則可以考慮使用文檔版本控制模式。
單個更新
官方文檔:db.collection.updateOne() — MongoDB Manual
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
在更新文檔的時候,比較需要註意的事 upsert
的使用,其是一種特殊類型的更新:如果找不到與篩選條件相匹配的文檔,則會以這個條件和更新文檔為基礎來創建一個新文檔;如果找到了匹配的文檔,則進行正常的更新。
因此,使用 upsert
的好處就是,可以使用同一套代碼創建和更新文檔。
使用 upsert
時就涉及到一個 $setOnInsert
運算符,它的作用是,只會在插入文檔時設置欄位的值,在後續的更新時不對其進行更改。
批量更新
官方文檔:db.collection.updateMany() — MongoDB Manual
db.collection.updateMany(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
單個替換
官方文檔:db.collection.replaceOne() — MongoDB Manual
db.collection.replaceOne(
<filter>,
<replacement>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
hint: <document|string> // Available starting in 4.2.1
}
)
replaceOne
和 updateOne
操作的區別在於,updateOne
可以更新文檔中部分鍵的值,而 replaceOne
的作用是直接將整個文檔都替換掉,通常是建議使用 updateOne
而不是 replaceOne
。
單個更新並返回
官方文檔:db.collection.findOneAndUpdate() — MongoDB Manual
db.collection.findOneAndUpdate(
<filter>,
<update document or aggregation pipeline>, // Changed in MongoDB 4.2
{
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
upsert: <boolean>,
returnDocument: <string>,
returnNewDocument: <boolean>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
使用 findOneAndUpdate
而不是 updateOne
的最大目的莫過於使用 findOneAndUpdate
可以返回更新前後文檔內容。
通過設置 returnNewDocument
的值為 true
可以返回更新後的文檔,為 false
時可以返回更新前的文檔。
單個替換並返回
官方文檔:db.collection.findOneAndReplace() — MongoDB Manual
db.collection.findOneAndReplace(
<filter>,
<replacement>,
{
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
upsert: <boolean>,
returnDocument: <string>,
returnNewDocument: <boolean>,
collation: <document>
}
)
更新運算符
官方文檔:Update Operators — MongoDB Manual
對於如 updateOne
、updateMany
、findOneAndUpdate
等更新操作,MongoDB 提供了原子的更新運算符支持只更新部分數據。
運算符支持以 { <operator>: { <field>: <value>,... } }
的方式使用,並且可以同時使用多個運算符。
為避免出現歧義,不建議對同一個屬性同時使用不同的運算符。
欄位
$currentDate
: 將指定鍵設置為當前時間$inc
: 對數字類型的鍵做遞增操作$min
: 會比較原始值和更新值,並設置成較小值$max
: 會比較原始值和更新值,並設置成較大值$mul
: 對數字類型的鍵做乘法操作$rename
: 修改指定鍵的名稱$set
: 將指定鍵設置為指定值$setOnInsert
: 與upsert
配合使用,如果更新導致文檔插入,則設置欄位的值,對修改現有文檔的更新操作無效$unset
: 刪除指定鍵
數組
$
: 充當占位符,更新與查詢條件匹配的第一個元素$[]
: 充當占位符,為匹配查詢條件的文檔更新數組中的所有元素$[<identifier>]
: 充當占位符,為匹配查詢條件的文檔更新與arrayFilters
條件匹配的所有元素$addToSet
: 僅當集合中不存在該元素時,才將元素添加到數組中$pop
: 傳{ $pop: { field: 1 } }
表示刪除數組中的最後一項,傳-1
時表示刪除第一項$pull
: 刪除與指定查詢匹配的所有數組元素$push
: 新增一項到數組中$pullAll
: 刪除列出的所有數組元素
數組內層
$each
: 使用$push
或$addToSet
運算符時,增加此運算符可以批量添加元素$position
: 與$each
配合使用,可以指定添加元素的位置$slice
: 與$each
配合使用,防止數組的增長超過某個大小,傳正數表示從左到右截斷$sort
: 與$each
配合使用,對添加完元素的數組進行排序
位運算
$bit
: 支持對整數值進行按位 AND
、OR
或 XOR
更新,通過使用 { $bit: { <field>: { <and|or|xor>: <int> } } }
完成更新。
批量寫操作
官方文檔:db.collection.bulkWrite() — MongoDB Manual
MongoDB 提供了 bulkwrite
命令支持批量執行命令,批處理時在一定程度上減少了網路交互的損耗。
db.collection.bulkWrite(
[ <operation 1>, <operation 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
支持的以下操作:
insertOne
updateOne
updateMany
replaceOne
deleteOne
deleteMany
查詢文檔
單個查詢
官方文檔:db.collection.findOne() — MongoDB Manual
db.collection.findOne(query, projection, options)
第一個參數是查詢條件,可以使用鍵值對的方式指定需要匹配的條件,多個鍵之間是 AND
的關係。
第二個參數用於僅返回指定的鍵,既可以節省網路傳輸的數據量,也可以減少客戶端解碼文檔的時間和記憶體消耗。
第三個參數可以指定查詢到可選項,這些選項會修改查詢行為和返回結果的方式。更多查看 官方文檔。
批量查詢
官方文檔:db.collection.find() — MongoDB Manual
db.collection.find(query, projection, options)
資料庫會使用游標返回 find
的執行結果。
對於游標,客戶端可以限制結果的數量,跳過一些結果,按任意方向的任意鍵組合對結果進行排序等操作。更多查詢 官方文檔。
下述是一些常用的游標方法:
count()
: 統計游標引用的文檔數量hasNext()
: 查詢是否有下一個結果hint(index)
: 指定游標命中的索引limit(number)
: 限制返回結果的數量next()
: 返回下一個搜索結果size()
: 返回應用skip()
和limit()
之後的查詢數量skip(number)
: 控制跳過一定的數量,跳過大量數據會比較慢sort(sort)
: 使用鍵值對的方式對搜索結果進行排序
在伺服器端,游標會占用記憶體的資源。通常在以下情況會終止游標及進行隨後的清理:
- 當游標遍歷完成匹配的結果時,它會清除自身
- 當游標超出客戶端的作用域時,驅動程式會向資料庫發送終止游標的信號
- 當游標達到 10 分鐘沒有使用時,資料庫游標會被自動銷毀
查詢運算符
官方文檔:Query and Projection Operators — MongoDB Manual
同樣的,設置查詢條件時,MongoDB 提供了查詢運算符支持更多的查詢方式。
比較運算符
$eq
: 返回等於指定值的結果,等同{ <field>: <value> }
的顯式表示$gt
: 返回大於指定值的結果$gte
: 返回大於等於指定值的結果$in
: 返回等於指定數組中任意一個值的結果,對於一個鍵匹配多個值,$in
比$or
更方便$lt
: 返回小於指定值的結果$lte
: 返回小於等於指定值的結果$ne
: 返回不等於指定值的結果$nin
: 返回不等於指定數組中任意一個值的結果
邏輯運算符
$and
: 返回同時符合查詢子句要求的結果$not
: 元運算符,匹配不符合查詢語句要求的結果$nor
: 返回不能同時符合查詢子句要求的結果$or
: 返回與任意一個查詢子句的條件匹配的結果
對於普通的 $and
類型查詢,我們總是希望儘可能用最少的參數來限定結果的範圍。而 $or
類型查詢則相反,如果第一個參數能夠匹配儘可能多的文檔,則其效率最高。
元素運算符
$exists
: 判斷指定鍵是否存在,返回符合要求的結果$type
: 判斷指定鍵的數據類型,返回符合要求的結果(好的模式設計不應該出現不同類型)
表達式運算符
$expr
: 允許使用聚合表達式,適合用在一些比較複雜的查詢語句$jsonSchema
: 根據給定的 JSON 模式驗證文檔,返回符合要求的結果$mod
: 指定鍵的值執行模運算,返回符合要求的結果$regex
: 使用正則表達式對指定鍵做匹配,MongoDB 可以利用索引來查詢首碼正則表達式,但不能用於不區分大小寫的搜索$text
: 對使用文本索引的欄位內容執行文本搜索,可以作為一個簡易的搜索引擎使用$where
: 允許在查詢時執行任意 JavaScript 代碼(性能較差,儘可能使用$expr
代替)
數組運算符
$all
: 匹配包含查詢中指定的所有元素的數組$elemMatch
: 用於強制將查詢子句與單個數組元素進行匹配$size
: 匹配指定數組長度大小的結果
投影運算符
投影運算符針對的是查詢語句的第二個參數,即返回的結果。
$
: 僅返回數組中與查詢條件匹配的第一個元素$elemMatch
: 對返回結果中的數組元素做$elemMatch
匹配,僅返回數組中滿足要求的元素$meta
: 與$text
運算符配合使用,返回與文檔關聯的元數據,可以返回匹配分數,或者索引鍵$slice
: 對返回結果中的數組做截取返回
特殊的 null 值
在 MongoDB 中,null
是一個比較特殊的值,它可以與自身匹配。也就是說了,通過 { field: null }
可以查詢出 field
鍵為 null
的文檔。
不過,更特殊的是,null
同樣會匹配缺少這個鍵值的文檔。也就是說,{ field: null }
可以查詢出不包含 field
鍵的文檔。
如果僅想匹配鍵值為 null
的文檔,則需要檢查該鍵的值是否為 null
,並且通過 $exists
條件確認該鍵已存在,如 { field: { $eq: null, $exists: true } }
就是這樣的查詢條件組。