[Mongo] MongoDB Shell 指令操作(Shell CLI)
# 啟動 MongoDB Shell
$ mongosh
# 查看指令
> help # 檢視和 shell 有關的指令
> db.help() # 檢視和 db 有關的指令
> db.[collection].help() # 檢視和 collections 有關的指令
基本觀念
-
NoSQL: not only SQL
-
階層 關係:
- database:在 MongoDB server 中可以有多個 database(DB)
- collection: 用來整理組織 document,類似 SQL 中的 table。一個 db 中可以有多個 collections。
- document: 個別的紀錄(record),類似 SQL 中的 row。一個 collections 中可以有多個 documents
- collection: 用來整理組織 document,類似 SQL 中的 table。一個 db 中可以有多個 collections。
- database:在 MongoDB server 中可以有多個 database(DB)
-
MongoDB 將資料儲存成 BSON 的文件,BSON 是一種以二進位(binary)方式來表徵 JSON 的方法
- 在操作 MongoDB 時,我們提供的是 JSON,MongoDB 的 drivers 會把這個 JSON 轉成對應的 BSON 在保存在 MongoDB 中
- BSON 可以包含比 JSON 更多的資料類型
- BSON 的儲存效率更高
-
MongoDB 的原子性(Atomicity)是建立在 Document Level,也就是說,一個 Document 要嘛有被完整保存成功,要嘛失敗完全沒存,不會有只存了「部分 Document」的情況。
當你使用 insertMany()
或 updateMany()
這類方法一次改動多個 documents 時,是可能會發生部分失敗的情況。但要留意的是,這裡的部分失敗指的是這個「操作」,而不是「document」。如果需要確保整個操作的原子性,需要使用 transaction。
基本指令
> help # 檢視常用指令
> show dbs # 檢視目前所有的 database
> show collections # 檢視當前 db 有的 collections
> db # 顯示當前所在資料庫
> quit # 離開
資料庫相關
db # 顯示當前所在資料庫
show dbs # 顯示現有資料庫(database)
show collections # 顯示當前資料庫內所有的 collections
use [DatabaseName] # 使用此資料庫,如果該資料庫不存在則自動「建立」並使用
##
# 針對 database
##
db.getCollectionNames() # 顯示當前資料庫中的 collection 名稱
db.dropDatabase() # 刪除資料庫
##
# 針對 collection
##
db.myCollection.drop() # 刪除 myCollection 這個 collection
- 在 MongoDB 中,如果該 database 不存在,MongoDB 會在第一次 儲存資料到該 database 時建立該 database。
CRUD
CREATE
##
# Create
# db.collection.insertOne({})
# db.collection.insertMany([{}, {}])
##
db.todos.insertOne({ title: 'Hello Mongo' }); # 建立 document
Ordered Behavior
預設的情況下,MongoDB 使用的是 ordered behavior,在新增資料時如果發生錯誤(例如,duplicate key error):
- 它不會有 rollback 的行為,成功寫進去的資料不會因為其中一個資料寫不進去而被還原
- 預設當
ordered
是true
時,一旦發生錯誤,在錯誤發生後的資料就不會繼續寫入。如果希望它能夠忽略掉這個錯誤,繼續寫入後面的資料,則可以把orderer
設成false
db.hobbies.insertMany(
[
{
_id: 'hiking',
name: 'hiking',
},
{
_id: 'yoga',
name: 'yoga',
},
{
_id: 'cooking',
name: 'cooking',
},
],
{ ordered: false },
);
Write Concern
Write Concern 的目的是確保 MongoDB 對寫操作的確認程度(acknowledgement level)。它允許用戶指定在寫操作被認為成功之前,MongoDB 必須接收到的確認數量。這可以確保數據的耐久性(durability)和一致性(availability),例如在單一伺服器、複寫集(replica sets)或分片集群(shared clusters)中,提供不同程度的數據安全保障。較高的 Write Concern 能增加數據安全性,但可能會降低效能。
如同這張圖所示:
MongoDB 在把資料寫進資料庫時,為了效能,實際上還有一個 Memory 存在;另外,需要的話,還可以寫一個實體的 Journal 檔,如此,一旦資料庫發生錯誤,只要有寫了 Journal 檔,還是可以把沒有寫入到資料庫中的資料復原。
透過 Write Concern 就可以設定這個行為:
{ w: <value>, j: <boolean>, wtimeout: <number> }
w
- Durability:預設的情況下是
w: majority
,表示大部分的 replica set 需要確認完成寫的東西後,才算成功。當資料是不可遺失的話,可以設定更高的 write concerns。 - Performance:如果你的使用情境非常重視寫入的效能,並且能夠容忍可能的資料流失,則可以把 write concern 設成
w: 1
或w: 0
。
- Durability:預設的情況下是
j
:用於指定寫操作在資料寫入日誌(journal)後才會得到確認(acknowledge)。日誌是一種預寫日誌(write-ahead log),用來確保在錯誤發生時數據的完整性- 當
j
是true
時,表示 MongoDB 確保寫操作只有在數據成功寫入日誌(on-disk journal)後才會被確認,但相對的會犧牲效能
- 當
wtimeout
:單位是 ms,目的是避免w
設定的節點無限期等待
當使用情境不同的,例如在 Standalone 或 Replica Sets 時,w
和 j
會有不同的行為,詳細可以參考官方文件的說明。
不等待資料是否寫進 memory 中:
db.hobbies.insertOne('writing', { writeConcern: { w: 1, j: true, wtimeout: 200 } });
// {
// acknowledged: false,
// insertedId: ObjectId('66782454b1ed5d2a0fbe0dce')
// }
READ
keywords: find()
, findOne()
, count()
, distinct()
##
# Read
# db.collection.find()
# db.collection.findOne()
# db.collection.countDocuments()
# db.collection.estimatedDocumentCount()
# db.collection.distinct()
##
db.todos.countDocuments(); # todos 中有多少 documents
db.todos.estimatedDocumentCount();
db.todos.find(); # 檢視某一 Collections 內所有的 documents
db.todos.find({ title: 'Create new Node course' }); # 檢視特定 document
db.todos.find({ _id: ObjectId('5c0beffdeb866557d13c0ef8') }); # 檢視特定 document
db.todos.find().pretty(); # 以格式化的方式列出 document
db.todos.find().limit(2); # 指列出前兩筆 document
db.todos.find().skip(2); # 忽略前兩筆資料
db.todos.find().sort({ title: 1 }); # 排序資料,1 是 ascending,-1 是 descending
db.todos.find().toArray()
db.todos.find().forEach()
Projection:SELECT fields
// Projection(類似 SQL 的 SELECT,只選出有需要用到的欄位,確保效能)
db.todos.find({}, { title: true }); // 會顯示 _id 和 title
db.todos.find({}, { _id: false, title: true }); // 只會顯示 title
db.todos.find({}, { title: false }); // 除了 title 其他都會顯示
db.todos.find({}, { title: true, tag: false }); // 錯誤的語法
Query Elements in an Array
// Query 尋找陣列中的資料
// 假設 hobbies 是 array,我們想要找 hobbies 中包含 'sports' 的 document
// hobbies: ['sports', 'video game']
db.todos.find({ hobbies: 'sports' });
上面的 query 會找到在陣列中「包含」有該元素的資料,如果你想要找到是在該陣列中「只有那個元素」,則要使用 hobbies: ["sports"]
。
Query Embedded Documents (Nested Object)
可以在 key 的地方使用 "x.y.z"
,就可以搜尋 embedded documents(nested objects)
db.todos.find({ 'status.description': 'on-time' });
db.movies.find({ 'rating.average': { $gte: 6.7 } });
Query Array of Object
如果是 Array of Object,例如:
# history: [
# { disease: 'headache', treatment: 'aspirin' },
# { disease: 'fever', treatment: 'ibuprofen' }
# ]
如果我們希望找到 history 中有 disease 是 'headache'
的元素,可以使用:
db.patientData.find({ 'history.disease': 'headache' });
當如果你希望找到的是 history
中有完全相同的物件,則是使用:
# 可以用類似的方式來 query
db.patientData.find({ history: { disease: 'headache', treatment: 'aspirin' } });
Operator
Comparison Operators
// $eq: equal(預設沒帶 operator 的話就是這個)
// $gt: greater than
// $gte: greater than or equal to
// $lt: less than
// $lte: less than or equal to
// $ne: not equal
db.todos.find({ priority: { $gt: 2 } });
db.movies.find({ runtime: { $ne: 60 } });
db.movies.find({ runtime: { $in: [] } });
// $in 和 $nin operator
db.todos.find({ status: { $in: ['A', 'D'] } }); // status 的值是「'A' 或 'D'」都符合
db.movies.find({ runtime: { $in: [30, 42] } }); // runtime 的值是「30 或 42」都符合
db.movies.find({ runtime: { $nin: [30, 42] } }); // runtime 的值不是「30 或 42」都符合
Logical Operators
// $or operator
db.todos.find({
$or: [{ title: 'Create new Node course' }, { title: 'Learn NodeJS' }],
});
db.movies.find({
$or: [{ 'rating.average': { $lt: 5 } }, { 'rating.average': { $gt: 9.3 } }],
});
// $nor operator:表示不是 xxx 也不是 xxx
db.movies.find({
$nor: [{ 'rating.average': { $lt: 5 } }, { 'rating.average': { $gt: 9.3 } }],
});
// $and operator,預設不指定就是 $and
db.movies.find({
$and: [{ 'rating.average': { $gt: 9 } }, { genres: 'Drama' }],
});
// 等同於
db.movies.find({ 'rating.average': { $gt: 9 }, genres: 'Drama' });
之所以會需要 $and
operator,是因為在 JavaScript 中,物件中不能有重複的 key。舉例來說,如果我們想要找出 genres
這個 array 中同時有 Horror
和 Drama
這兩個 elements 的:
// ❌ JavaScript 並不能這樣寫,因為當物件中有相同的 key,後面的會改掉前面的
db.movies.find({ genres: 'Horror', genres: 'Drama' });
// 所以上面的寫法實際上等同於
db.movies.find({ genres: 'Drama' });
// 如果要達到原本的目的,就要用 $and operator
db.movies.find({ $and: [{ genres: 'Horror' }, { genres: 'Drama' }] });
但如果是在 Golang 的 mongo driver 中,不用 $and
即可可以達到原本的目的:
filter := bson.D{{"genres", "Drama"}, {"genres", "Horror"}}
UPDATE
##
# Update
# db.collection.updateOne({}, {})
# db.collection.updateMany({}, {})
# db.collection.replaceOne()
# 第一個參數的物件稱作 update filter,類似 find
# 第二個參數的物件稱作 update action,透過 $set 可以更新內容
##
db.todos.updateOne(
{ _id: ObjectId('5c0beffdeb866557d13c0ef8') },
{ $set: { tag: 'course', title: 'Learn Node JS' } }
);
DELETE
##
# Destroy
# db.collection.deleteOne()
# db.collection.deleteMany()
##
db.todos.deleteOne({ tags: 'course' })
db.todos.deleteMany({ tags: 'course' }) # 刪除 tags 為 'course' 的 documents
db.todos.deleteMany({}) # 刪除 todos 中所有的 documents
db
指我們所在的資料庫post
是 collection ,會在insert
document 之後被建立{title: 'Hello Mongo'}
是所見建立的 document- 過去使用的
insert()
,update()
寫法目前處於 deprecated 狀態,建議不要使用。
- MongoDB CRUD Operations @ MongoDB Manual
- Insert Documents @ MongoDB CRUD Operations
- Query Documents @ MongoDB CRUD Operations
- Update Documents @ MongoDB CRUD Operations
- Delete Documents @ MongoDB CRUD Operations
- Views @ Introduction to MongoDB > Database and Collections
建立有資料型別驗證(Document Validation)的 Collection
可用的 bsonType 可以參考這裡的官方文件。
關於 schema validation 可以這裡的官方文件。
預設的情況下,MongoDB 不會對 Document 做任何資料型別的驗證,但如果有需要,可以在建立 Collection 的時候,利用 createCollection
就把 Collection 中 Document 中的欄位名稱和資料型別定義好:
db.createCollection('users', {
// 定義 users 這個 collection 的 validation rules
validator: {
$jsonSchema: {
bsonType: 'object',
// 這個 object 中有哪些欄位是必填的
required: ['email', 'age', 'address', 'habits'],
// 定義每個欄位的型別
properties: {
email: {
bsonType: 'string',
description: 'must be a string and is required',
},
age: {
bsonType: 'int',
description: 'must be a integer and is required',
},
// embedded / nested document
address: {
bsonType: 'object',
required: ['country', 'addressLine'],
properties: {
country: {
bsonType: 'objectId',
description: 'must be objectId and is required',
},
addressLine: {
bsonType: 'string',
description: 'must be string and is required',
},
},
},
// array
habits: {
bsonType: 'array',
description: 'must be an array and is required',
// 定義 array 裡的 items 的型別
items: {
bsonType: 'string',
description: 'must be a string and is required',
},
},
},
},
},
});
當有不符合 validation rules 的 document 要存進 mongoDB 時,就會噴錯。