MongoDB 4.X CRUD基本操作

H_Johnny發表於2020-06-05

本文總結了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()是我用得最多的文件插入方式,具體的語法格式如下:

db.collection.insert(
   <document or array of documents>,
   {
     writeConcern: <document>,
     ordered: <boolean>
   }
)

引數說明:

  • document:指定一個或多個文件;
  • writeConcern:文件寫入確認級別(可選),關於讀寫策略確認級別,以後再進行討論;
  • ordered:指定文件是否按順序插入(可選),預設為true;
    • 當指定為true時,插入多個文件時將文件排序儲存在一個陣列中進行插入,如果其中有一個文件插入失敗,則會導致陣列中餘下的文件不進行插入操作;
    • 當指定為false時,插入多個文件時將文件不進行排序儲存在一個陣列中進行插入,如果其中有一個文件插入失敗,則不影響陣列中餘下的文件進行插入操作。

如果插入的文件當中沒有指定_id欄位,則MongoDB會自動為文件生成具有唯一ObjectId值的欄位_id

使用示例:

// 沒有指定_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()

語法格式如下:

db.collection.insertOne(
   <document>,
   {
      writeConcern: <document>
   }
)

引數說明:

參考db.collection.insert()的引數說明。

使用示例:

// 單行插入文件,關於_id欄位指定與否也與db.collection.insert()一致
db.products.insertOne( { item: "card", qty: 15 } );

db.collection.insertMany()

語法格式如下:

db.collection.insertMany(
   [ <document 1> , <document 2>, ... ],
   {
      writeConcern: <document>,
      ordered: <boolean>
   }
)

引數說明:

參考db.collection.insert()的引數說明。

使用示例:

參考db.collection.insert()的引數說明。

關於返回確認資訊

db.collection.insert()在插入文件成功之後返回的資訊相對較為簡潔:

db.products.insert( { item: "card", qty: 15 } );
WriteResult({ "nInserted" : 1, "writeConcernError" : [ ] })

db.collection.insertOne()db.collection.insertMany()返回的資訊較為詳細:

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()方法。

來自官方文件的測試資料:

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()可以說是使用頻率最高的方法了,可以用來查詢資料庫集合當中的文件。

語法格式如下:

db.collection.find(<query>, <projection>)
  • query:查詢表示式;
  • projection:指定查詢結果集中需要顯示的欄位。
    • Col_name:1|true 代表顯示該欄位;
    • Col_name:0 | false 代表不顯示該欄位。

_id欄位是預設顯示的,如果不想顯示,則顯式指定{"_id" : 0}

查詢所有文件:

db.inventory.find()

db.inventory.find({})

db.collection.findOne()

db.collection.findOne()方法顯示符合條件查詢的第一條文件,接受的引數與db.collection.find()方法一致。

條件查詢操作符

通常對文件的查詢,是需要帶條件的,而很少使用到不帶條件的全文件檢索,以下總結了幾種常使用的查詢操作符:

比較操作符

比較操作符涉及的操作如下表所示:

名稱 說明
$eq 與指定值相等
$gt 大於指定的值
$gte 大於或等於指定的值
$in 指定的值在陣列中
$lt 小於指定的值
$lte 小於或等於指定的值
$ne 所有不等於指定的值
$nin 指定的值不在陣列中

使用示例:

// $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 指定查詢滿足其中某個條件查詢子句

使用示例:

// $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 指定查詢文件的某個欄位是否是對應型別

使用示例:

// $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 為欄位值取模並且符合條件的查詢

為了更好的使用這兩個主要的操作符,額外建立個文件:

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 }
]);

使用示例:

// $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指定的表示式可以修改文件中符合條件的欄位或代替整個文件。具體的語法格式如下:

db.collection.update(
   <query>,   //查詢表示式
   <update>,  //更新表示式
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>,
     collation: <document>,
     arrayFilters: [ <filterdocument1>, ... ],
     hint:  <document|string>                   // 版本4.2新增
   }
)

引數說明:

  • query:更新文件的查詢表示式;如果指定了引數upsert: true並且集合中沒有符合查詢條件的文件,查詢條件中有關於欄位_id指定了.分隔符的,並不會插入新的文件;

  • update:主要包含三種格式

    • 1.更新文件:只包含更新操作符表示式;
    • 2.替換文件:只包含<field1>: <value1>對;
    • 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
// 測試資料
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選項
/* 使用選項引數 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()不同的是每次只更新單個文件。

語法格式如下:

db.collection.updateOne(
   <filter>,
   <update>,
   {
     upsert: <boolean>,
     writeConcern: <document>,
     collation: <document>,
     arrayFilters: [ <filterdocument1>, ... ],
     hint:  <document|string>        
   }
)

引數說明:

參考db.collection.update()的引數說明。

使用示例:

// 參考db.collection.update()

db.collection.updateMany()

根據update指定的引數可以修改文件中符合條件的欄位或代替整個文件,與db.collection.updateOne()不同的是更新所有符合條件的文件。

語法格式如下:

db.collection.updateMany(
   <filter>,
   <update>,
   {
     upsert: <boolean>,
     writeConcern: <document>,
     collation: <document>,
     arrayFilters: [ <filterdocument1>, ... ],
     hint:  <document|string>        
   }
)

引數說明:

參考db.collection.update()的引數說明。

使用示例:

// 參考db.collection.update()

刪除(Delete Operations)

刪除是指對集合當中已存在的文件進行清除操作,MongoDB提供以下幾種主要刪除文件方法:

  • db.collection.deleteOne():只刪除集合中符合條件的一個文件;
  • db.collection.deleteMany():刪除集合中所有符合條件的文件;
  • db.collection.remove():刪除集合中符合條件的一個或多個文件。

db.collection.deleteOne()

根據filter選項條件刪除集合中的單個文件,具體語法格式如下:

db.collection.deleteOne(
   <filter>,
   {
      writeConcern: <document>,
      collation: <document>
   }
)

引數說明:

  • filter:指定基於查詢表示式的過濾條件,關於查詢表示式可以檢視db.collecion.find()中的<query>
  • writeConcern:參考db.collection.insert()相同引數說明;
  • collation:指定校對規則(可選,版本3.4新增);

使用示例:

// 刪除指定條件的單個文件
db.orders.deleteOne( { "_id" : 1 } );
{ "acknowledged" : true, "deletedCount" : 1 }

db.collection.deleteMany()

根據filter選項條件刪除集合中的單個文件,具體語法格式如下:

db.collection.deleteMany(
   <filter>,
   {
      writeConcern: <document>,
      collation: <document>
   }
)

引數說明:

參考db.collection.deleteOne()的引數說明。

使用示例:

// 刪除指定條件的多個文件
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/

https://docs.mongodb.com/manual/reference/operator/query-evaluation/

https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-array-update-operations

https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline

☆〖本人水平有限,文中如有錯誤還請留言批評指正!〗☆

相關文章