1. 程式人生 > >MongoDB 4.X CRUD基本操作

MongoDB 4.X CRUD基本操作

本文總結了MongoDB 4.X在mongo shell客戶端涉及的對文件一些基本的增刪改查操作,即CRUD操作。主要結合了自己平時使用MongoDB的操作命令,更詳細的命令可以參考官方文件: **https://docs.mongodb.com/manual/crud/** 。 ## 建立(Create Operations) 建立(Create Operations)也叫插入操作,當集合不存在時,插入操作同時也會建立集合。MongoDB提供以下幾種插入文件方法: - **db.collection.insert()**:在指定集合中插入單個或多個文件。 - **db.collection.insertOne()**:在指定集合中插入單個文件(版本3.2新增)。 - **db.collection.insertMany()**:在指定集合中插入多個文件(版本3.2新增)。 ### db.collection.insert() 在平時的使用當中,**db.collection.insert()**是我用得最多的文件插入方式,具體的語法格式如下: ```javascript db.collection.insert( , { writeConcern: , ordered: } ) ``` 引數說明: - **document**:指定一個或多個文件; - **writeConcern**:文件寫入確認級別(可選),關於讀寫策略確認級別,以後再進行討論; - **ordered**:指定文件是否按順序插入(可選),預設為true; - 當指定為true時,插入多個文件時將文件排序儲存在一個數組中進行插入,如果其中有一個文件插入失敗,則會導致陣列中餘下的文件不進行插入操作; - 當指定為false時,插入多個文件時將文件不進行排序儲存在一個數組中進行插入,如果其中有一個文件插入失敗,則不影響陣列中餘下的文件進行插入操作。 如果插入的文件當中沒有指定`_id`欄位,則MongoDB會自動為文件生成具有唯一`ObjectId`值的欄位`_id`。 使用示例: ```javascript // 沒有指定_id欄位的插入單個文件 db.products.insert( { item: "card", qty: 15 } ); // 指定_id欄位的插入單個文件 db.products.insert( { _id: 10, item: "box", qty: 20 } ); // 插入多個文件,不進行排序,多個文件包含在陣列[]中 db.products.insert( [ { _id: 11, item: "pencil", qty: 50, type: "no.2" }, { item: "pen", qty: 20 }, { item: "eraser", qty: 25 } ] ); // 插入多個文件,並進行排序 db.products.insert( [ { _id: 20, item: "lamp", qty: 50, type: "desk" }, { _id: 21, item: "lamp", qty: 20, type: "floor" }, { _id: 22, item: "bulk", qty: 100 } ], { ordered: false } ); ``` ### db.collection.insertOne() 語法格式如下: ```javascript db.collection.insertOne( , { writeConcern: } ) ``` 引數說明: 參考**db.collection.insert()**的引數說明。 使用示例: ```javascript // 單行插入文件,關於_id欄位指定與否也與db.collection.insert()一致 db.products.insertOne( { item: "card", qty: 15 } ); ``` ### db.collection.insertMany() 語法格式如下: ```js db.collection.insertMany( [ , , ... ], { writeConcern: , ordered: } ) ``` 引數說明: 參考**db.collection.insert()**的引數說明。 使用示例: 參考**db.collection.insert()**的引數說明。 ### 關於返回確認資訊 **db.collection.insert()**在插入文件成功之後返回的資訊相對較為簡潔: ```javascript db.products.insert( { item: "card", qty: 15 } ); WriteResult({ "nInserted" : 1, "writeConcernError" : [ ] }) ``` **db.collection.insertOne()**和**db.collection.insertMany()**返回的資訊較為詳細: ```javascript db.products.insertOne( { item: "card", qty: 15 } ); { "acknowledged": true, "insertedId": ObjectId("5eccbd214139000074003be8") } db.products.insertMany( [ { _id: 10, item: "large box", qty: 20 }, { _id: 11, item: "small box", qty: 55 }, { _id: 12, item: "medium box", qty: 30 } ] ); { "acknowledged": true, "insertedIds": [ 10, 11, 12 ] } ``` ## 查詢(Read Operations) 查詢(Read Operations)讀操作,是對集合中已存在的文件進行查詢,即對應關係型資料庫當中的`select`操作,比如MySQL,MongoDB提供以下幾種主要查詢文件方法: - **db.collection.find()**:查詢指定集合中滿足條件的一個或多個文件和檢視; - **db.collection.findOne()**:查詢指定集合中滿足條件的第一個文件,並以格式化方式展現,通過`pretty()`方法。 來自官方文件的測試資料: ```javascript 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.collection.find() **db.collection.find()**可以說是使用頻率最高的方法了,可以用來查詢資料庫集合當中的文件。 語法格式如下: ```javascript db.collection.find(, ) ``` - **query**:查詢表示式; - **projection**:指定查詢結果集中需要顯示的欄位。 - Col_name:1|true 代表顯示該欄位; - Col_name:0 | false 代表不顯示該欄位。 `_id`欄位是預設顯示的,如果不想顯示,則顯式指定`{"_id" : 0}`。 查詢所有文件: ```javascript db.inventory.find() ``` 或 ```javascript db.inventory.find({}) ``` ### db.collection.findOne() **db.collection.findOne()**方法顯示符合條件查詢的第一條文件,接受的引數與**db.collection.find()**方法一致。 ### 條件查詢操作符 通常對文件的查詢,是需要帶條件的,而很少使用到不帶條件的全文件檢索,以下總結了幾種常使用的查詢操作符: #### 比較操作符 比較操作符涉及的操作如下表所示: | 名稱 | 說明 | | ---- | ------------------ | | $eq | 與指定值相等 | | $gt | 大於指定的值 | | $gte | 大於或等於指定的值 | | $in | 指定的值在陣列中 | | $lt | 小於指定的值 | | $lte | 小於或等於指定的值 | | $ne | 所有不等於指定的值 | | $nin | 指定的值不在陣列中 | 使用示例: ```javascript // $eq:等值查詢 SQL: SELECT * FROM inventory WHERE status = "D"; db.inventory.find( { status: "D" } ) // $ne 同$eq // $gt:範圍查詢(以大於為例) SQL: SELECT * FROM inventory WHERE qty > 30; db.inventory.find( { qty: { $gt: 30 } } ) // $gte、$lt、$lte 同$gt // $in:或查詢,可使用or代替 SQL: SELECT * FROM inventory WHERE status in ("A", "D") db.inventory.find( { status: { $in: [ "A", "D" ] } } ) // $nin 同$in ``` #### 邏輯操作符 邏輯操作符涉及的操作如下表所示: | 名稱 | 說明 | | ---- | -------------------------------- | | $and | 指定查詢同時滿足多個條件查詢子句 | | $not | 指定查詢不滿足條件查詢子句 | | $nor | 指定查詢無法滿足多個條件查詢子句 | | $or | 指定查詢滿足其中某個條件查詢子句 | 使用示例: ```js // $and: 邏輯與查詢 SQL: SELECT * FROM inventory WHERE status = "A" AND qty < 30; db.inventory.find( { $and: [ { status: { $eq: "A" }, qty: { $lt: 30 } } ] } ) // $not: 不符合查詢 SQL: SELECT * FROM inventory WHERE status <> "A"; db.inventory.find( { status: { $not: { $eq: "A" } } } ) /* $nor: 無法同時滿足多個條件查詢,欄位不存在時也符合 SQL: SELECT * FROM inventory WHERE status <> "A" AND qty > 30; 符合以下條件之一都會出現在結果集中: 1.文件包含status和qty欄位並且符合條件; 2.文件包含status欄位並且符合條件,不包含qty欄位; 3.文件不包含status欄位,包含qty欄位並且符合條件; 4.文件不包含status欄位和qty欄位。 */ db.inventory.find( { $nor: [ { status: { $eq: "A" } }, { qty: { $lt: 30 } } ] } ) // $or: 邏輯或查詢 SQL: SELECT * FROM inventory WHERE status = "A" OR qty < 30; db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } ) ``` #### 元素操作符 元素操作符主要涉及的操作如下表所示: | 名稱 | 說明 | | ------- | ------------------------------------ | | $exists | 指定查詢文件是否有對應的欄位 | | $type | 指定查詢文件的某個欄位是否是對應型別 | 使用示例: ```javascript // $exists: 是否存在指定欄位查詢 db.inventory.find( { price: { $exists: true } } ) // $type: 欄位是否是指定型別查詢 db.inventory.find( { "qty": { $type: "double" } } ) ``` #### 評估操作符 評估操作符主要涉及的操作如下表所示,更多操作符可以參考官方文件:**https://docs.mongodb.com/manual/reference/operator/query-evaluation/**。 | 名稱 | 說明 | | ----- | ------------------------------------------------------------ | | $expr | 為同一個文件中的欄位指定表示式並且符合條件的查詢,比如比較同一文件當中兩個欄位的值 | | $mod | 為欄位值取模並且符合條件的查詢 | 為了更好的使用這兩個主要的操作符,額外建立個文件: ```javascript db.monthlyBudget.insertMany([ { "_id" : 1, "category" : "food", "budget": 400, "spent": 450 }, { "_id" : 2, "category" : "drinks", "budget": 100, "spent": 150 }, { "_id" : 3, "category" : "clothes", "budget": 100, "spent": 50 }, { "_id" : 4, "category" : "misc", "budget": 500, "spent": 300 }, { "_id" : 5, "category" : "travel", "budget": 200, "spent": 650 } ]); ``` 使用示例: ```javascript // $expr: 允許使用聚合表示式,這裡以$gt為例,更多表達式參考 https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#aggregation-expressions db.monthlyBudget.find( { $expr: { $gt: [ "$spent" , "$budget" ] } } ) // $mod: 對欄位所在值進行取模運算,顯示符合條件的查詢,如qty欄位值對4取模,並且餘數為0 db.inventory.find( { qty: { $mod: [ 4, 0 ] } } ) ``` ## 更新(Update Operations) 更新(Update Operations)是對已存在的文件進行修改操作,MongoDB提供以下幾種主要更新文件方法: - **db.collection.update()**:更新或替換集合中符合條件的一個或多個文件; - **db.collection.updateOne()**:只更新集合中符合條件的第一個文件,即使有多個文件(版本3.2新增); - **db.collection.updateMany()**:更新集合中所有符合條件的文件(版本3.2新增)。 ### db.collection.update() 根據`update`指定的表示式可以修改文件中符合條件的欄位或代替整個文件。具體的語法格式如下: ```javascript db.collection.update( , //查詢表示式 , //更新表示式 { upsert: , multi: , writeConcern: , collation: , arrayFilters: [ , ... ], hint: // 版本4.2新增 } ) ``` 引數說明: - **query**:更新文件的查詢表示式;如果指定了引數`upsert: true`並且集合中沒有符合查詢條件的文件,查詢條件中有關於欄位`_id`指定了`.`分隔符的,並不會插入新的文件; - **update**:主要包含三種格式 - 1.更新文件:只包含更新操作符表示式; - 2.替換文件:只包含` : `對; - 3.聚合管道:版本4.2新增,詳細參考官方文件。 - **upsert**:當query查詢條件沒符合更新的文件,就新建立文件(可選),預設值為**false**; - **multi**:是否更新多個符合條件的文件(可選),預設值為**false**,只更新符合條件的第一個文件; - **writeConcern**:參考**db.collection.insert()**相同引數說明; - **collation**:指定校對規則(可選,版本3.4新增); - **arrayFilters**:文件陣列更新過濾操作符(可選,版本3.6新增); 詳細參考:**https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-array-update-operations** - **hint**:採用文件或字串的形式指定適用於查詢表示式的索引,如果索引不存在則報錯(可選,版本4.2新增)。 使用示例: 使用示例將通過使用兩種場景進行,一是沒有使用引數選項`upsert`,二是使用引數選項`upsert`。 - **不使用選項upsert** ```javascript // 測試資料 db.books.remove({}); db.books.insertMany([ { "_id" : 1, "item" : "TBD", "stock" : 0, "info" : { "publisher" : "1111", "pages" : 430 }, "tags" : [ "technology", "computer" ], "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "lmn", "rating" : 5 } ], "reorder" : false }, { "_id" : 2, "item" : "XYZ123", "stock" : 15, "info" : { "publisher" : "5555", "pages" : 150 }, "tags" : [ ], "ratings" : [ { "by" : "xyz", "rating" : 5 } ], "reorder" : false } ]); /* 使用選項引數 upsert: true 1、如果查詢表示式找到匹配的文件,則執行更新操作; 2、如果查詢表示式沒有找到匹配的文件,則執行插入操作; */ db.books.update( { item: "ZZZ135" }, // 查詢表示式 { // 更新或替換文件 item: "ZZZ135", stock: 5, tags: [ "database" ] }, { upsert: true } ); // 1.使用更新操作表示式 /* $set操作符 1、查詢表示式指定需要更新的文件 _id; 2、$inc操作符: stock的欄位值+5; 3、$set操作符: 替換item欄位值,替換嵌入文件info的publisher欄位值,替換tags欄位值,替換陣列ratings的第二個元素值 */ db.books.update( { _id: 1 }, { $inc: { stock: 5 }, $set: { item: "ABC123", "info.publisher": "2222", tags: [ "software" ], "ratings.1": { by: "xyz", rating: 3 } } } ); 更新之後的文件: { "_id" : 1, "item" : "ABC123", "stock" : 5, "info" : { "publisher" : "2222", "pages" : 430 }, "tags" : [ "software" ], "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "xyz", "rating" : 3 } ], "reorder" : false } // 2.為已存在的陣列新增元素 // $push操作符: 為指定文件陣列ratings新增一個元素 db.books.update( { _id: 2 }, { $push: { ratings: { "by" : "jkl", "rating" : 2 } } } ); 更新之後的文件: { "_id" : 2, "item" : "XYZ123", "stock" : 15, "info" : { "publisher" : "5555", "pages" : 150 }, "tags" : [ ], "ratings" : [ { "by" : "xyz", "rating" : 5 }, { "by" : "jkl", "rating" : 2 } ], "reorder" : false } // 3.文件移除欄位 // $unset操作符: 移除文件的指定欄位,為_id:1文件移除tags欄位 db.books.update( { _id: 1 }, { $unset: { tags: 1 } } ); 更新後的文件: { "_id" : 1, "item" : "TBD", "stock" : 0, "info" : { "publisher" : "1111", "pages" : 430 }, "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "lmn", "rating" : 5 } ], "reorder" : false } // 4.替換整個文件 // 替換_id:2的文件 db.books.update( { _id: 2 }, { item: "XYZ123", stock: 10, info: { publisher: "2255", pages: 150 }, tags: [ "baking", "cooking" ] } ); 更新後的文件: { "_id" : 2, "item" : "XYZ123", "stock" : 10, "info" : { "publisher" : "2255", "pages" : 150 }, "tags" : [ "baking", "cooking" ] } // 5.更新多個文件 db.books.update( { stock: { $lte: 10 } }, { $set: { reorder: true } }, { multi: true } ); 更新後的全部文件: [ { "_id" : 1, "item" : "ABC123", "stock" : 5, "info" : { "publisher" : "2222", "pages" : 430 }, "ratings" : [ { "by" : "ijk", "rating" : 4 }, { "by" : "xyz", "rating" : 3 } ], "reorder" : true } { "_id" : 2, "item" : "XYZ123", "stock" : 10, "info" : { "publisher" : "2255", "pages" : 150 }, "tags" : [ "baking", "cooking" ], "reorder" : true } ] ``` - **使用upserts選項** ```javascript /* 使用選項引數 upsert: true 1、如果查詢表示式找到匹配的文件,則執行更新操作; 2、如果查詢表示式沒有找到匹配的文件,則執行插入操作; */ // 1.插入未符合更新條件的文件 db.books.update( { item: "ZZZ135" }, { item: "ZZZ135", stock: 5, tags: [ "database" ] }, { upsert: true } ); 因為集合並未滿足條件的文件,則插入的文件為: { "_id" : ObjectId("5da78973835b2f1c75347a83"), "item" : "ZZZ135", "stock" : 5, "tags" : [ "database" ] } // 2.插入未符合更新條件並且基於更新操作符的文件 // 如果沒有符合更新查詢條件,並且使用的是更新操作符,則會基於當前的查詢條件和更新操作符欄位插入新的文件 db.books.update( { item: "BLP921" }, { $set: { reorder: false }, $setOnInsert: { stock: 10 } }, { upsert: true } ); 新插入的文件為: { "_id" : ObjectId("5da79019835b2f1c75348a0a"), "item" : "BLP921", "reorder" : false, "stock" : 10 } // 3.插入未符合更新條件並且基於聚合管道的文件 // 關於聚合管道請參考官方文件:https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline // 4.插入未符合更新條件並且同時聯合多文件操作符的文件 如果不符合查詢條件,則只會插入單個文件 db.books.update( { "info.publisher": "Self-Published" }, { $set: { reorder: false, tags: [ "literature", "hardcover" ], stock: 25 } }, { upsert: true, multi: true } ); 新插入的文件: { "_id" : ObjectId("5db337934f670d584b6ca8e0"), "info" : { "publisher" : "Self-Published" }, "reorder" : false, "stock" : 25, "tags" : [ "literature", "hardcover" ] } ``` ### db.collection.updateOne() 根據`update`指定的引數可以修改文件中符合條件的欄位或代替整個文件,與**db.collection.update()**不同的是每次只更新單個文件。 語法格式如下: ```javascript db.collection.updateOne( , , { upsert: , writeConcern: , collation: , arrayFilters: [ , ... ], hint: } ) ``` 引數說明: 參考**db.collection.update()**的引數說明。 使用示例: ```javascript // 參考db.collection.update() ``` ### db.collection.updateMany() 根據`update`指定的引數可以修改文件中符合條件的欄位或代替整個文件,與**db.collection.updateOne()**不同的是更新所有符合條件的文件。 語法格式如下: ```javascript db.collection.updateMany( , , { upsert: , writeConcern: , collation: , arrayFilters: [ , ... ], hint: } ) ``` 引數說明: 參考db.collection.update()的引數說明。 使用示例: ```javascript // 參考db.collection.update() ``` ## 刪除(Delete Operations) 刪除是指對集合當中已存在的文件進行清除操作,MongoDB提供以下幾種主要刪除文件方法: - **db.collection.deleteOne()**:只刪除集合中符合條件的一個文件; - **db.collection.deleteMany()**:刪除集合中所有符合條件的文件; - **db.collection.remove()**:刪除集合中符合條件的一個或多個文件。 ### db.collection.deleteOne() 根據`filter`選項條件刪除集合中的單個文件,具體語法格式如下: ```javascript db.collection.deleteOne( , { writeConcern: , collation: } ) ``` 引數說明: - **filter**:指定基於查詢表示式的過濾條件,關於查詢表示式可以檢視`db.collecion.find()`中的``; - **writeConcern**:參考**db.collection.insert()**相同引數說明; - **collation**:指定校對規則(可選,版本3.4新增); 使用示例: ```javascript // 刪除指定條件的單個文件 db.orders.deleteOne( { "_id" : 1 } ); { "acknowledged" : true, "deletedCount" : 1 } ``` ### db.collection.deleteMany() 根據`filter`選項條件刪除集合中的單個文件,具體語法格式如下: ```javascript db.collection.deleteMany( , { writeConcern: , collation: } ) ``` 引數說明: 參考**db.collection.deleteOne()**的引數說明。 使用示例: ```javascript // 刪除指定條件的多個文件 db.orders.deleteMany( {"cust_id" : "Cam Elot"} ); { "acknowledged" : true, "deletedCount" : 2 } ``` > 注意: 如果是對固定集合進行刪除文件操作則會報錯,固定集合的清除操作使用方法**db.collection.drop()**。 ## 總結 1. 本文簡單梳理了在Mongo Shell下基本的CRUD操作,主要適用於DBA的運維管理,如果是研發同學,根據不同的程式語言使用不同客戶端驅動進行操作,詳細同樣可以參考官方文件; 2. 針對CRUD各個方面還有其他一些額外的方法,比如查詢修改文件方法**db.collection.findAndModify()**,這裡只是總結每個文件操作中一些最基礎的方法,對於額外高階的方法這裡不再贅述; 3. 掌握了這些基本的CRUD操作,就可以對MongoDB文件進行操作了,但還是需要控制好許可權,畢竟資料安全不是小事,做變更之前做好資料的備份,以防萬一。 ## 參考 [**https://docs.mongodb.com/manual/crud/**][1] [**https://docs.mongodb.com/manual/reference/operator/query-evaluation/** ][2] [**https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-array-update-operations**][3] [**https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline**][4] **☆〖本人水平有限,文中如有錯誤還請留言批評指正!〗☆** [1]: https://docs.mongodb.com/manual/crud/ [2]:https://docs.mongodb.com/manual/reference/operator/query-evaluation/ [3]:https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-array-update-operations [4]:https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-p