分享到

簡介

當使用 MongoDB 時,您大部分的時間會花在以某種方式管理文件。無論您是建立新文件並將它們新增到集合、檢索文件、更新資料,還是修剪過時的項目,文件都是 MongoDB 模型的核心。

在本指南中,我們將介紹什麼是 MongoDB 文件,然後涵蓋您可能需要了解的常見操作,以管理以文件為中心的環境

什麼是 MongoDB 文件?

在 MongoDB 中,資料庫和集合中的所有資料都儲存在文件中。由於集合預設不指定必要的結構描述,因此集合中的文件可以包含任意複雜的結構,並且不需要與同級文件使用的格式相符。這提供了難以置信的靈活性,並允許結構描述隨著應用程式需求的變化而有機地發展。

MongoDB 文件本身使用 BSON 資料序列化格式,它是 JSON JavaScript 物件表示法的二進制表示形式。這提供了有組織的結構,其中包含定義的資料類型,可以透過程式設計方式進行查詢和操作。

BSON 文件由一對大括號 ({}) 表示,其中包含鍵值對。在 BSON 中,這些資料對稱為欄位及其。欄位在前,並以字串表示。值可以是任何有效的 BSON 資料類型。冒號 (:) 將欄位與其值分隔開來。逗號用於將每個欄位和值對彼此分開。

例如,這是一個 MongoDB 可以理解的有效 BSON 文件

{
_id: 80380,
vehicle_type: "car",
mileage: 7377.80,
color: "blue",
markets: [
"US",
"UK"
],
options: {
transmission: "automatic",
num_doors: 4,
power_windows: true
}
}

在這裡,我們可以看到相當多類型

  • _id 是一個整數
  • vehicle_typecolor 是字串
  • mileage 是一個浮點數
  • markets 是一個字串陣列
  • options 包含一個巢狀文件,其值由字串、整數和布林值組成

由於這種靈活性,文件是一種相當靈活的資料儲存媒介。可以輕鬆新增欄位,文件可以彼此嵌入,並且結構複雜性與儲存的資料完全匹配。

如何建立新文件

若要建立新文件,請切換到您要儲存已建立文件的資料庫。在本文中,我們將使用 school 資料庫進行示範

use school

您還需要選擇要插入文件的集合。與資料庫一樣,您不必明確建立要插入文件的集合。當寫入第一個資料時,MongoDB 會自動建立它。在此範例中,我們將使用名為 students 的集合。

現在您已經知道文件將儲存在哪裡,您可以使用以下方法之一插入新文件。

使用 insert() 方法

insert() 方法允許您將一個或多個文件插入到呼叫它的集合中。

若要插入單個文件,請透過在集合上呼叫該方法將文件傳遞給該方法。在這裡,我們為名為 Ashley 的學生插入一個新文件

db.students.insert(
{
first_name: "Ashley",
last_name: "Jenkins",
dob: new Date("January 08, 2003"),
grade_level: 8
}
)
WriteResult({ "nInserted" : 1 })

如果您想要同時插入多個文件,請傳遞文件陣列而不是將文件傳遞給 insert()。我們可以為名為 Brian 和 Leah 的學生新增兩個新文件

db.students.insert(
[
{
first_name: "Brian",
last_name: "McMantis",
dob: new Date("September 18, 2010"),
grade_level: 2
},
{
first_name: "Leah",
last_name: "Drake",
dob: new Date("October 03, 2009")
}
]
)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})

由於我們執行了大量寫入操作,因此我們的傳回值是 BulkWriteResult,而不是我們之前看到的 WriteResult 物件。

雖然 insert() 方法很靈活,但在許多 MongoDB 驅動程式中已被棄用,取而代之的是以下兩種方法。

使用 insertOne() 方法

insertOne() 方法可用於插入單個文件。與 insert() 方法不同,它一次只能插入一個文件,這使其行為更具可預測性。

語法與您使用 insert() 新增單個文件時相同。我們可以新增另一位名為 Naomi 的學生

db.students.insertOne(
{
first_name: "Naomi",
last_name: "Pyani"
}
)
{
"acknowledged" : true,
"insertedId" : ObjectId("60e877914655cbf49ff7cb86")
}

insert() 不同,insertOne() 方法傳回一個包含一些其他有用資訊的文件。它確認叢集已確認寫入,並且包含分配給文件的物件 ID,因為我們沒有提供一個。

使用 insertMany() 方法

為了涵蓋您想要一次插入多個文件的情況,現在建議使用 insertMany() 方法。就像使用 insert() 插入多個文件一樣,insertMany() 接受文件陣列。

我們可以新增三位名為 Jasmine、Michael 和 Toni 的新學生

db.students.insertMany(
[
{
first_name: "Jasmine",
last_name: "Took",
dob: new Date("April 11, 2011")
},
{
first_name: "Michael",
last_name: "Rodgers",
dob: new Date("February 25, 2008"),
grade_level: 6
},
{
first_name: "Toni",
last_name: "Fowler"
}
]
)
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("60e8792d4655cbf49ff7cb87"),
ObjectId("60e8792d4655cbf49ff7cb88"),
ObjectId("60e8792d4655cbf49ff7cb89")
]
}

insertOne() 一樣,insertMany() 傳回一個文件,該文件確認寫入並提供一個陣列,其中包含已分配給插入文件的 ID。

如何查詢現有文件

查詢文件是一個相當廣泛的主題,值得單獨撰寫一篇文章。您可以在我們關於在 MongoDB 中查詢資料的指南中找到有關如何制定查詢以檢索不同類型文件的詳細資訊。

雖然詳細資訊最好留在上面連結的文章中,但我們至少可以介紹 MongoDB 提供的查詢文件的方法。從 MongoDB 提取文件的主要方法是在相關集合上呼叫 find() 方法。

例如,若要從 students 集合中收集所有文件,您可以呼叫不帶參數的 find()

db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

為了使輸出更具可讀性,您也可以在 find() 之後鏈式呼叫 pretty() 方法

db.<collection>.find().pretty()
{
"_id" : ObjectId("60e8743b4655cbf49ff7cb83"),
"first_name" : "Ashley",
"last_name" : "Jenkins",
"dob" : ISODate("2003-01-08T00:00:00Z"),
"grade_level" : 8
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb84"),
"first_name" : "Brian",
"last_name" : "McMantis",
"dob" : ISODate("2010-09-18T00:00:00Z"),
"grade_level" : 2
}
{
"_id" : ObjectId("60e875d54655cbf49ff7cb85"),
"first_name" : "Leah",
"last_name" : "Drake",
"dob" : ISODate("2009-10-03T00:00:00Z")
}
{
"_id" : ObjectId("60e877914655cbf49ff7cb86"),
"first_name" : "Naomi",
"last_name" : "Pyani"
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb87"),
"first_name" : "Jasmine",
"last_name" : "Took",
"dob" : ISODate("2011-04-11T00:00:00Z")
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb88"),
"first_name" : "Michael",
"last_name" : "Rodgers",
"dob" : ISODate("2008-02-25T00:00:00Z"),
"grade_level" : 6
}
{
"_id" : ObjectId("60e8792d4655cbf49ff7cb89"),
"first_name" : "Toni",
"last_name" : "Fowler"
}

您可以看到 _id 欄位已新增到每個文件中。MongoDB 要求集合中的每個文件都有唯一的 _id。如果您在物件建立時未提供一個,它會為您新增一個。您可以使用此 ID 可靠地檢索單個物件

db.students.find(
{
_id : ObjectId("60e8792d4655cbf49ff7cb89")
}
)
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

您可以透過上面連結的文章,了解更多關於查詢資料的各種方法。

如何更新現有文件

資料庫的許多或大多數用例都要求您能夠修改資料庫中的現有資料。欄位可能需要更新以反映新值,或者您可能需要將其他資訊附加到現有文件中(當其可用時)。

MongoDB 使用一些相關方法來更新現有文件

  • updateOne():根據提供的篩選器,更新集合中的單個文件。
  • updateMany():更新集合中符合提供的篩選器的多個文件。
  • replaceOne():根據提供的篩選器,替換集合中的整個文件。

我們將介紹如何使用這些變體中的每一個來執行不同類型的更新。

更新運算符

在我們查看更新文件的每種方法之前,我們應該先了解一些可用的更新運算符。

  • $currentDate:將欄位的值設定為目前日期,可以是日期或時間戳記類型。
    • 語法:{ $currentDate: { <欄位>: <類型>, ... } }
  • $inc:將欄位的值遞增設定的量。
    • 語法:{ $inc: { <欄位>: <數量>, ... } }
  • $min:如果指定的值小於目前值,則更新欄位的值。
    • 語法:{ $min: { <欄位>: <值>, ... } }
  • $max:如果指定的值大於目前值,則更新欄位的值。
    • 語法:{ $max: { <欄位>: <值>, ... } }
  • $mul:透過將欄位的值乘以給定的數字來更新它。
    • 語法:{ $mul: { <欄位>: <值>, ... } }
  • $rename:將欄位名稱重新命名為新的識別符。
    • 語法:{ $rename: { <欄位>: <新名稱>, ... } }
  • $set:將欄位的值替換為給定的值。
    • 語法:{ $set: { <欄位>: 值, ... } }
  • $setOnInsert:在 upsert 操作期間,如果正在建立新文件,則設定欄位的值,否則不執行任何操作。
    • 語法:{ $setOnInsert: { <欄位>: <值>, ... } }
  • $unset:從文件中移除欄位。
    • 語法:{ $unset: { <欄位>: "", ... } }
  • $:滿足查詢的第一個陣列元素的佔位符。
    • 語法:{ <更新運算符>: {<陣列>.$: <值> } }
  • $[]:滿足查詢的所有陣列元素的佔位符。
    • 語法:{ <更新運算符>: { <陣列>.$[]: <值> } }
  • $addToSet:將值新增到陣列,除非它們已存在。
    • 語法:{ $addToSet: { <欄位>: <值>, ... } }
  • $pop:移除陣列的第一個或最後一個元素。
    • 語法:{ $pop: { <欄位>: (-1 或 1), ... } }
  • $pull:移除陣列中符合條件的所有元素。
    • 語法:{ $pull: { <欄位>: <條件>, ... } }
  • $push:將值附加到陣列。
    • 語法:{ $push: { <欄位>: <值>, ... } }
  • $pullAll:從陣列中移除所有指定的元素。
    • 語法:{ $pullAll: { <欄位>: [ <值>, ... ], ...} }
  • $each:修改 $addToSet$push 運算符,以便它們新增陣列的每個元素,而不是將陣列作為單個元素新增。
    • 語法:{ <更新運算符>: { <欄位>: { $each: [ <值>, ... ] }, ... } }
  • $position:與 $each 一起使用,並指定 $push 運算符應插入的位置。
    • 語法:{ $push: { <欄位>: { $each: [ <值>, ... ], $position: <數字> } } }
  • $slice:與 $each$push 一起使用,以限制陣列中元素的總數。
    • 語法:{ $push: { <欄位>: { $each: [ <值>, ... ], $slice: <數字> } } }
  • $sort:與 $each$push 一起使用,以排序陣列元素。
    • 語法:{ $push: { <欄位>: { $each: [ <值>, ... ], $sort: <排序順序> } } }

這些各種更新運算符允許您以不同的方式更新文件的各種欄位。

更新集合中的單個文件

MongoDB 的 updateOne() 方法用於更新集合中的單個文件。該方法接受兩個必需的參數以及一個指定可選參數的文件。

第一個參數是一個文件,用於指定將用於選擇文件的篩選條件。由於 updateOne() 方法最多修改集合中的一個文件,因此將使用第一個滿足篩選條件的文件。

第二個參數指定應執行的更新操作。可以在此處指定上面給出的更新操作,以更改匹配文件的內容。

第三個參數是一個包含各種選項的文件,用於修改方法的行為。最重要的潛在值是

  • upsert:如果篩選器與任何現有文件不匹配,則透過插入新文件將操作變成 upsert 程序。
  • collation:一個文件,用於定義應適用於操作的特定語言規則。

作為範例,我們可以更新單個學生記錄,我們透過 _id 欄位對其進行篩選,以確保我們針對正確的文件。我們可以將 grade_level 設定為新值

db.students.updateOne(
{ _id: ObjectId("60e8792d4655cbf49ff7cb89") },
{ $set: { grade_level: 3 } }
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

更新集合中的多個文件

MongoDB 的 updateMany() 方法的工作方式與 updateOne() 方法類似,但它會更新任何符合給定篩選器的文件,而不是在第一次匹配後停止。

updateMany() 語法完全遵循 updateOne() 語法,因此唯一的區別是操作的範圍。

作為範例,如果我們想要將 teachers 集合文件中 subjects 陣列中的所有 "composition" 實例更改為 "writing",我們可以使用類似這樣的方式

db.teachers.updateMany(
{ subject: "composition" },
{ $set: { "subjects.$": "writing" } }
)
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 }

如果您檢查文件,則每個 "composition" 實例都應已替換為 "writing"

db.teachers.find()
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Nancy", "last_name" : "Smith", "subjects" : [ "vocabulary", "pronunciation" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "writing" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "writing", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bad"), "first_name" : "Rebecca", "last_name" : "Carrie", "subjects" : [ "grammar", "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "writing", "grammar", "vocabulary", "pronunciation" ] }

替換文件

replaceOne() 方法的工作方式與 updateOne() 方法類似,但它會替換整個文件,而不是更新個別欄位。語法與前兩個命令相同。

例如,如果 Nancy Smith 離開您的學校,並且您用一位名叫 Clara Newman 的文學老師替換她,您可以輸入以下內容

db.teachers.replaceOne(
{
$and: [
{ first_name: "Nancy" },
{ last_name: "Smith" }
]
},
{
first_name: "Clara",
last_name: "Newman",
subjects: [ "literature" ]
}
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

您可以看到匹配的文件已被移除,並且指定的文件已替換它

db.teachers.find()
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Clara", "last_name" : "Newman", "subjects" : [ "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "writing" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "writing", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bad"), "first_name" : "Rebecca", "last_name" : "Carrie", "subjects" : [ "grammar", "literature" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "writing", "grammar", "vocabulary", "pronunciation" ] }

如何刪除文件

從集合中移除文件也是文件生命週期的一部分。若要移除文件,您可以使用 deleteOne()deleteMany() 方法。它們具有相同的語法,並且僅在它們操作的文件數量上有所不同。

在大多數情況下,您使用這些方法中的任何一種刪除文件所要做的就是向其提供一個篩選器文件,該文件指定您希望如何選擇要刪除的文件。deleteOne() 方法最多刪除一個文件(無論篩選器產生多少匹配項),而 deleteMany() 方法刪除每個符合篩選條件的文件。

例如,若要刪除單個學生,您可以提供 _id 以明確匹配他們

db.students.deleteOne({
_id: ObjectId("60e8792d4655cbf49ff7cb87")
})
{ "acknowledged" : true, "deletedCount" : 1 }

如果我們想要刪除任何未分配年級的學生,我們可以改用 deleteMany() 方法

db.students.deleteMany({
grade_level: { $eq: null }
})
{ "acknowledged" : true, "deletedCount" : 2 }

如果我們檢查,我們應該看到所有剩餘的學生都已分配年級

db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler", "grade_level" : 3 }

結論

學習如何建立、查詢、更新和移除文件,讓您具備每天有效管理 MongoDB 中文件所需的技能。熟悉各種文件和集合方法,以及允許您匹配和修改資訊的運算符,使您可以表達資料庫系統可以理解的複雜想法。

常見問題

在 MongoDB 中,嵌入式或巢狀文件是指在其內部包含另一個文件的文件。

以下是一個嵌入式文件的範例,其中 address(以額外的花括號表示為子文件)可以使用 user 記錄來存取。

db.user.findOne({_id: 111111})
{
_id: 111111,
email: “email@example.com”,
name: {given: “Jane”, family: “Han”},
address: {
street: “111 Elm Street”,
city: “Springfield”,
state: “Ohio”,
country: “US”,
zip: “00000”,
}
}

MongoDB 中文件大小上限為 16 MB。

此限制有助於確保單一文件不會使用過多的 RAM,或在傳輸期間使用過多的頻寬。

若要儲存大於 16MB 的文件,MongoDB 提供了 GridFS API

若要移除文件,您可以使用 deleteOne()deleteMany() 方法。它們具有相同的語法,唯一的區別在於它們操作的文件數量。

若要刪除單一文件,移除具有特定 _id 的文件的基本語法如下:

db.students.deleteOne({
_id: ObjectId("60e8792d4655cbf49ff7cb87")
})

而若要刪除多個符合特定條件的文件,語法也類似:

db.students.deleteMany({
grade_level: { $eq: null }
})

若要在 MongoDB 中修改文件,有一些相關的更新方法

具體來說,若要將新資料附加到文件,您可以使用 $addToSet 更新運算子。此運算子會將值新增至文件的陣列,除非這些值已存在。

在 MongoDB 中,沒有明確比較兩個文件的特定方法。 可以透過設定查詢來比較文件中任何欄位之間的等式與運算子來完成。

比較也可以透過設定聚合管道來完成。此方法允許您建立階段,以:

  • 將多個文件中的值分組在一起
  • 對分組的資料執行運算以返回單一結果
  • 分析資料隨時間的變化
關於作者
Justin Ellingwood

Justin Ellingwood

Justin 自 2013 年以來一直撰寫關於資料庫、Linux、基礎架構和開發者工具的文章。他目前與妻子和兩隻兔子住在柏林。 他通常不必以第三人稱寫作,這對所有相關方來說都是一種解脫。