本文總結了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新增);
-
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()。
總結
- 本文簡單梳理了在Mongo Shell下基本的CRUD操作,主要適用於DBA的運維管理,如果是研發同學,根據不同的程式語言使用不同客戶端驅動進行操作,詳細同樣可以參考官方文件;
- 針對CRUD各個方面還有其他一些額外的方法,比如查詢修改文件方法db.collection.findAndModify(),這裡只是總結每個文件操作中一些最基礎的方法,對於額外高階的方法這裡不再贅述;
- 掌握了這些基本的CRUD操作,就可以對MongoDB文件進行操作了,但還是需要控制好許可權,畢竟資料安全不是小事,做變更之前做好資料的備份,以防萬一。
參考
https://docs.mongodb.com/manual/crud/
https://docs.mongodb.com/manual/reference/operator/query-evaluation/
☆〖本人水平有限,文中如有錯誤還請留言批評指正!〗☆