本文是 MongoDB 新手入門 系列的第一篇贵少,在本文中拒迅,我們將會講解 MongoDB 基本的增刪改查操作施符,在學習完本文后甲馋,讀者將會掌握對 MongoDB 的集合文檔進行基本的新增养泡,修改嗜湃,刪除以及基于多種條件的查詢操作。
本文將會持續(xù)修正和更新澜掩,最新內(nèi)容請參考我的 GITHUB 上的 程序猿成長計劃 項目购披,歡迎 Star,更多精彩內(nèi)容請 follow me肩榕。
插入文檔
在 MongoDB shell 中刚陡,插入文檔有以下兩個方法:
- 使用
db.collection.insertOne()
插入單個文檔 - 使用
db.collection.insertMany()
插入多個文檔
在插入文檔時,如果 collection 不存在株汉,插入操作將會自動創(chuàng)建 collection筐乳。如果文檔沒有 _id
字段, MongoDB 將會自動添加一個 _id
字段乔妈,類型為 ObjectId
蝙云。
在 MongoDB 中,每一個文檔都需要一個唯一的
_id
字段作為主鍵路召,如果插入時沒有指定_id
字段勃刨,MongoDB 驅(qū)動將會自動生成一個類型為ObjectId
的_id
字段波材。
ObjectId
是一種能夠快速生成的,有序的身隐,12 字節(jié)大小的唯一值廷区,包含:
- 一個 4 字節(jié)的時間戳,代表了 ObjectId 的創(chuàng)建時間抡医,取值為 Unix 時間戳(秒)
- 一個 5 字節(jié)的隨機值躲因,該值每個進程都只會生成一次,對每臺服務(wù)器和進程來說該值是唯一的
- 一個 3 字節(jié)的自增值忌傻,初始為一個隨機數(shù)
use sample_mflix
// 插入單個文檔
db.movies.insertOne(
{
title: "The Favourite",
genres: [ "Drama", "History" ],
runtime: 121,
rated: "R",
year: 2018,
directors: [ "Yorgos Lanthimos" ],
cast: [ "Olivia Colman", "Emma Stone", "Rachel Weisz" ],
type: "movie"
}
)
// 插入多個文檔
db.movies.insertMany([
{
title: "Jurassic World: Fallen Kingdom",
genres: [ "Action", "Sci-Fi" ],
runtime: 130,
rated: "PG-13",
year: 2018,
directors: [ "J. A. Bayona" ],
cast: [ "Chris Pratt", "Bryce Dallas Howard", "Rafe Spall" ],
type: "movie"
},
{
title: "Tag",
genres: [ "Comedy", "Action" ],
runtime: 105,
rated: "R",
year: 2018,
directors: [ "Jeff Tomsic" ],
cast: [ "Annabelle Wallis", "Jeremy Renner", "Jon Hamm" ],
type: "movie"
}
])
除了常用的 insertOne
和 insertMany
方法之外大脉,還可以用以下方式插入文檔
db.collection.bulkWrite()
-
配合
upsert: true
選項db.collection.updateOne()
db.collection.updateMany()
db.collection.findAndModify()
db.collection.findOneAndUpdate()
查詢文檔
最基本的查詢方法是 db.collection.find()
和 db.collection.findOne()
,在 MongoDB 中插入以下文檔
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" }
]);
查詢 Collection 中所有文檔
// 等價 SQL:SELECT * FROM inventory
db.inventory.find({})
// 等價 SQL:SELECT * FROM inventory LIMIT 1
db.inventory.findOne({})
指定查詢條件
等值查詢
// 等價 SQL:SELECT * FROM inventory WHERE status = "D"
db.inventory.find({status: "D"})
// 等價 SQL:SELECT * FROM inventory WHERE status != "D"
db.inventory.find({ status: { $ne: "D" } })
IN 查詢
// 等價 SQL:SELECT * FROM inventory WHERE status in ("A", "D")
db.inventory.find({status: { $in: ["A", "D"]}})
// 等價 SQL: SELECT * FROM inventory WHERE status NOT IN ("A", "D")
db.inventory.find({ status: { $nin: ["A", "D"] } })
范圍查詢
// SQL: SELECT * FROM inventory WHERE qty >= 50 AND qty < 100
db.inventory.find({ qty: { $gte: 50, $lt: 100 } })
比較操作符支持這些: $lt
水孩,$gt
镰矿,$gte
,$lte
俘种。
AND 查詢
// SQL:SELECT * FROM inventory WHERE status = "A" AND qty < 30
db.inventory.find({ status: "A", qty: { $lt: 30 } })
OR 查詢
// SQL:SELECT * FROM inventory WHERE status = "A" OR qty < 30
db.inventory.find({ $or: [ { status: "A"}, { qty: { $lt: 30 } } ] })
同時使用 AND 和 OR
// SQL: SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%" )
db.inventory.find({
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
})
NOT
// 查詢 qty 模 5 值為 1 的所有文檔秤标,這里匹配的 qty 可能值為 1, 6宙刘, 11苍姜, 16 等
db.inventory.find({ qty: { $mod: [5, 1] } })
// 查詢 qty 模 5 值部位 1 的所有文檔,可能值為2, 3, 4, 5, 7, 8, 9, 10, 12 等
db.inventory.find({ qty: { $not: { $mod: [5, 1] } } })
查詢嵌套的文檔
查詢所有 size
等于 { h: 14, w: 21, uom: "cm" }
的文檔
db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
查詢所有 size
中 uom
等于 in
的文檔
db.inventory.find( { "size.uom": "in" } )
查詢數(shù)組
在 MongoDB 中插入以下文檔
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
{ item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
{ item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
下面的示例查詢所有字段 tags
值只包含元素 "red" 和 "blank"(順序一致) 的文檔
db.inventory.find({ tags: ["red", "blank"]})
如果只是查詢同時包含 "red" 和 "blank" 兩個值悬包,并且不關(guān)心排序或者數(shù)組中是否包含其它元素衙猪,則可以使用 $all
操作符
db.inventory.find({ tags: { $all: ["red", "blank"] } })
查詢 tags
包含 "red" 的文檔
db.inventory.find({ tags: "red" })
查詢所有 dim_cm
包含至少一個大于值 25 的所有文檔
db.inventory.find({ dim_cm: { $gt: 25} })
查詢所有 dim_cm
包含至少一個值大于 15 或者 小于 20 的所有文檔
db.inventory.find({ dim_cm: { $gt: 15, $lt: 20 } })
查詢所有 dim_cm
包含至少一個值大于 22 且小于 30 的所有文檔
db.inventory.find({ dim_cm: { $elemMatch: { $gt: 22, $lt: 30} } })
查詢數(shù)組 dim_cm
的第二個值大于 25
db.inventory.find({ "dim_cm.1": { $gt: 25 }})
查詢數(shù)組 tags
擁有 3 個元素的所有文檔
db.inventory.find({ "tags": { $size: 3 } })
查詢返回指定字段
默認情況下,MongoDB 的查詢會返回匹配文檔中所有的字段布近,通過 projection
可以返回指定的字段垫释。
返回指定字段
// SQL: SELECT _id, item, status FROM inventory WHERE status = "A"
db.inventory.find({ status: "A" }, { item: 1, status: 1 })
查詢結(jié)果中會自動返回 _id
字段,可以通過設(shè)置 _id: 0
來主動消除該字段撑瞧。
// SQL: SELECT item, status FROM inventory WHRE status = "A"
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
排除指定字段
db.inventory.find({ status: "A" }, { status: 0, instock: 0 })
返回數(shù)組中指定元素
使用 $slice
操作符返回 instock
數(shù)組中最后一個元素
db.inventory.find({ status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } })
查詢值為 NULL 或者缺失字段
在 MongoDB 中棵譬,不同的查詢操作符對 null
的處理方式是不同的。在 MongoDB 中插入以下文檔
db.inventory.insertMany([
{ _id: 1, item: null },
{ _id: 2 }
])
等值查詢
查詢 item
值為 null
或者不包含 item
字段的所有文檔
db.inventory.find({ item: null })
類型檢查
查詢所有 item
值為 null
的文檔
db.inventory.find({ item: { $type: 10} })
這里的
$type = 10
對應(yīng)了 BSON 類型Null
存在性檢查
查詢所有不包含字段 item
的文檔
db.inventory.find({ item: { $exists: false } })
查詢所有包含 item 字段预伺,但是值為 null
的文檔
db.inventory.find({ item: { $eq: null, $exists: true } })
限制查詢結(jié)果數(shù)量
// 只查詢 3 條數(shù)據(jù)
db.inventory.find({}).limit(3)
// 從第 2 條開始订咸,查詢 3 條數(shù)據(jù)
db.inventory.find({}).limit(3).skip(2)
排序
排序方向 1
為正序, -1
為倒序酬诀。
db.inventory.find({}).sort({item: 1, qty: -1})
查詢集合中的文檔數(shù)量
該方法用于查詢匹配條件的文檔數(shù)量脏嚷,語法為
db.collection.count(query, options)
示例
db.orders.count( { ord_dt: { $gt: new Date('01/01/2012') } } )
查詢字段的唯一值 distinct
查詢集合中字段的唯一值,語法為
db.collection.distinct(field, query, options)
[圖片上傳失敗...(image-5a985-1653901669910)]
附錄:支持的查詢操作符
類別 | 操作符 | 用途 |
---|---|---|
Comparison | $eq |
等值判斷 |
Comparison | $gt |
大于某個值 |
Comparison | $gte |
大于等于某個值 |
Comparison | $in |
當前值在數(shù)組中 |
Comparison | $lt |
小于某個值 |
Comparison | $lte |
小于等于某個值 |
Comparison | $ne |
不等于某個值 |
Comparison | $nin |
當前值不再數(shù)組中 |
Logical | $and |
AND |
Logical | $not |
反轉(zhuǎn)查詢條件 |
Logical | $nor |
所有查詢條件都不匹配 |
Logical | $or |
OR |
Element | $exists |
字段存在性檢查 |
Element | $type |
字段類型檢查 |
Evaluation | $expr |
在查詢表達式中使用聚合語法 |
Evaluation | $jsonSchema |
驗證文檔符合指定的 JSON 模型 |
Evaluation | $mod |
對字段值進行取模運算 |
Evaluation | $regex |
選擇匹配正則表達式的文檔 |
Evaluation | $text |
執(zhí)行文本搜索 |
Evaluation | $where |
JavaScript 表達式匹配 |
Geospatial | $geoIntersects |
地理坐標匹配 |
Geospatial | $geoWithin |
地理坐標匹配 |
Geospatial | $near |
地理坐標匹配 |
Geospatial | $nearSphere |
地理坐標匹配 |
Array | $all |
匹配包含查詢中指定的所有元素的數(shù)組 |
Array | $elemMatch |
數(shù)組中的元素匹配表達式則返回文檔 |
Array | $size |
選擇數(shù)組大小為 size 的文檔 |
Bitwise | $bitsAllClear |
二進制匹配 |
Bitwise | $bitsAllSet |
二進制匹配 |
Bitwise | $bitsAnyClear |
二進制匹配 |
Bitwise | $bitsAnySet |
二進制匹配 |
Miscellaneous | $comment |
在查詢中添加注釋 |
Miscellaneous | $rand |
隨機生成一個 0-1 之間的浮點值 |
更新文檔
常用的文檔更新方法有以下三種
-
db.collection.updateOne(<filter>, <update>, <options>)
更新單個文檔 -
db.collection.updateMany(<filter>, <update>, <options>)
更新多個文檔 -
db.collection.replaceOne(<filter>, <update>, <options>)
替換單個文檔
我們這里以 updateOne()
方法為例進行講解料滥,updateOne()
方法的語法如下
db.collection.updateOne(
<filter>, // 要更新的文檔篩選條件
<update>, // 文檔更新命令
{
upsert: <boolean>, // 設(shè)置為 true 時然眼,如果 filter 沒有匹配到文檔艾船,則自動新增文檔
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
更新 item=paper
的文檔
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
更新操作符如下
-
$set
操作符指定了要更新匹配文檔的size.uom
為cm
葵腹,status
為p
-
$currentDate
操作符用于更新lastModified
字段為當前的日期高每,如果lastModified
字段不存在,則自動創(chuàng)建
更新文檔践宴,如果不存在則新增
db.restaurant.updateOne(
{ "name" : "Pizza Rat's Pizzaria" },
{ $set: {"_id" : 4, "violations" : 7, "borough" : "Manhattan" } },
{ upsert: true }
);
更多字段操作符如下
操作符 | 用途 |
---|---|
$currentDate |
設(shè)置字段值為當日期鲸匿,可以是日期或者是時間戳 |
$inc |
將字段的值加上某個數(shù)值 |
$min |
只有指定的值小于已經(jīng)存在的值時才更新 |
$max |
只有指定的額值大于已經(jīng)存在的值才更新 |
$mul |
將字段的值乘以某個數(shù)值 |
$rename |
重命名指定字段 |
$set |
設(shè)置文檔中要更新的字段值 |
$setOnInsert |
如果當前操作新增了文檔,則設(shè)置字段的值阻肩。如果更新操作只是修改一個已經(jīng)存在的文檔带欢,則該操作符無效 |
$unset |
從文檔中移除指定字段 |
除了常用的三個方法,還有以下方法也可以用于更新文檔
-
db.collection.findOneAndReplace()
. -
db.collection.findOneAndUpdate()
. -
db.collection.findAndModify()
. -
db.collection.bulkWrite()
.
刪除文檔
在 MongoDB 中烤惊,通常使用以下方法刪除文檔
刪除所有為文檔
db.inventory.deleteMany({})
刪除所有匹配條件的文檔
db.inventory.deleteMany({ status : "A" })
刪除匹配條件的一個文檔
db.inventory.deleteOne( { status: "D" } )
除了常用的兩個方法外乔煞,還可以用以下方法刪除文檔
-
db.collection.findOneAndDelete()
.findOneAndDelete() 提供了一個
sort
選項,該選項允許刪除按照指定排序規(guī)則排序后匹配的第一個文檔 -
db.collection.findAndModify()
.db.collection.findAndModify()
提供了一個sort
選項柒室,該選項允許刪除按照指定排序規(guī)則排序后匹配的第一個文檔
批量寫操作
MongoDB 提供了一種對單個 Collection 執(zhí)行批量寫入的操作能力渡贾,使用 db.collection.bulkWrite()
方法實現(xiàn)批量的插入、更新和刪除操作雄右。
有序和無序操作
批量寫操作可以試有序的(ordered
)或者無序(unordered
)的空骚,對于有序操作,MongoDB 會串行的執(zhí)行操作擂仍,如果寫操作過程中發(fā)生錯誤囤屹,MongoDB 將會直接返回,后面的操作將不會被執(zhí)行逢渔。無序操作則無法保證這種行為肋坚,當發(fā)生錯誤的時候,MongoDB 將會繼續(xù)處理剩余的文檔复局。
對于分片的集合來說冲簿,執(zhí)行有序的批量操作通常會比較慢,因為每一個操作都必須等待上一個操作的完成亿昏。默認情況下峦剔,bulkWrite()
執(zhí)行的是有序的操作,可以通過設(shè)置 ordered: false
選項來啟用無序操作模式角钩。
bulkWrite() 方法
bulkWrite()
支持以下寫操作
假設(shè)一個名為 characters
的集合中包含下面的文檔
{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },
{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },
{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }
下面的 bulkWrite()
方法對該集合執(zhí)行多個操作
db.characters.bulkWrite(
[
{ insertOne :
{
"document" :
{
"_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
}
}
},
{ insertOne :
{
"document" :
{
"_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
}
}
},
{ updateOne :
{
"filter" : { "char" : "Eldon" },
"update" : { $set : { "status" : "Critical Injury" } }
}
},
{ deleteOne :
{ "filter" : { "char" : "Brisbane" } }
},
{ replaceOne :
{
"filter" : { "char" : "Meldane" },
"replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
}
}
]
);
操作返回以下內(nèi)容
{
"acknowledged" : true,
"deletedCount" : 1,
"insertedCount" : 2,
"matchedCount" : 2,
"upsertedCount" : 0,
"insertedIds" : {
"0" : 4,
"1" : 5
},
"upsertedIds" : {
}
}