MongoDB 新手入門 - CRUD

本文是 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"
    }
])

除了常用的 insertOneinsertMany 方法之外大脉,還可以用以下方式插入文檔

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

查詢所有 sizeuom 等于 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.uomcm葵腹,statusp
  • $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 從文檔中移除指定字段

除了常用的三個方法,還有以下方法也可以用于更新文檔

刪除文檔

在 MongoDB 中烤惊,通常使用以下方法刪除文檔

刪除所有為文檔

db.inventory.deleteMany({})

刪除所有匹配條件的文檔

db.inventory.deleteMany({ status : "A" })

刪除匹配條件的一個文檔

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

除了常用的兩個方法外乔煞,還可以用以下方法刪除文檔

批量寫操作

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" : {
   }
}

參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吝沫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子递礼,更是在濱河造成了極大的恐慌惨险,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脊髓,死亡現(xiàn)場離奇詭異辫愉,居然都是意外死亡,警方通過查閱死者的電腦和手機将硝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門恭朗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屏镊,“玉大人,你說我怎么就攤上這事痰腮《妫” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵膀值,是天一觀的道長棍丐。 經(jīng)常有香客問我,道長沧踏,這世上最難降的妖魔是什么歌逢? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮翘狱,結(jié)果婚禮上趋翻,老公的妹妹穿的比我還像新娘。我一直安慰自己盒蟆,他們只是感情好踏烙,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著历等,像睡著了一般讨惩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寒屯,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天荐捻,我揣著相機與錄音,去河邊找鬼寡夹。 笑死处面,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的菩掏。 我是一名探鬼主播魂角,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼智绸!你這毒婦竟也來了野揪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瞧栗,失蹤者是張志新(化名)和其女友劉穎斯稳,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迹恐,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡挣惰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憎茂。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡唆涝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唇辨,到底是詐尸還是另有隱情,我是刑警寧澤能耻,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布赏枚,位于F島的核電站,受9級特大地震影響晓猛,放射性物質(zhì)發(fā)生泄漏饿幅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一戒职、第九天 我趴在偏房一處隱蔽的房頂上張望栗恩。 院中可真熱鬧,春花似錦洪燥、人聲如沸磕秤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽市咆。三九已至,卻和暖如春再来,著一層夾襖步出監(jiān)牢的瞬間蒙兰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工芒篷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搜变,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓针炉,卻偏偏與公主長得像挠他,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子篡帕,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

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