分享到

簡介

查詢文件是在 MongoDB 中執行許多不同操作的必要技能。您需要能夠查詢才能有效地檢索您需要的文件、更新資料庫中的現有資訊,並了解文件之間的共通點和差異。

在本指南中,我們將介紹如何撰寫 MongoDB 查詢的基本知識,以協助您根據需求檢索文件。我們將向您展示查詢在一般層級上的運作方式,然後我們將探索 MongoDB 提供的各種運算子,以協助您透過評估條件來縮小結果範圍。

建立範例集合

在本文中,我們將使用一個名為 students集合和一個名為 teachers 的集合,它們都儲存在名為 school 的資料庫中。

您可以使用以下命令建立範例資料庫並填入集合

use school
db.students.insertMany([
{
first_name: "Ashley",
last_name: "Jenkins",
dob: new Date("January 08, 2003"),
grade_level: 8
},
{
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")
},
{
first_name: "Naomi",
last_name: "Pyani"
},
{
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"
}
])
db.teachers.insertMany([
{
first_name: "Nancy",
last_name: "Smith",
subjects: [
"vocabulary",
"pronunciation"
]
},
{
first_name: "Ronald",
last_name: "Taft",
subjects: [
"literature",
"grammar",
"composition"
]
},
{
first_name: "Casey",
last_name: "Meyers",
subjects: [
"literature",
"composition",
"grammar"
]
},
{
first_name: "Rebecca",
last_name: "Carrie",
subjects: [
"grammar",
"literature"
]
},
{
first_name: "Sophie",
last_name: "Daggs",
subjects: [
"literature",
"composition",
"grammar",
"vocabulary",
"pronunciation"
]
}
])

基本查詢語法

現在您有兩個包含文件的集合,您可以實驗如何檢索個別文件或文件群組。從 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.student.find(
{
_id : ObjectId("60e8792d4655cbf49ff7cb89")
}
)
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

依相等性篩選結果

您可以透過檢查相等性來篩選結果,方法是提供一個物件,指定您要尋找的欄位和值配對。

例如,您可以使用以下查詢取得名為「Brian」的學生清單

db.students.find({first_name: "Brian"})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }

您使用欄位值表示法指定的任何屬性都將被解釋為相等性查詢。如果您提供多個欄位,則所有值都必須相等,文件才會符合。

例如,如果我們執行與之前相同的相等性比對,但包含 grade_level 為 3,則不會傳回任何文件

db.students.find({first_name: "Brian", grade_level: 3})

使用比較運算子篩選

雖然簡單的相等性篩選很有用,但其可表達的內容相當有限。對於其他類型的比較,MongoDB 提供了各種比較運算子,以便您可以使用其他方式查詢。

如果您使用過其他程式設計語言,則可用比較運算子的基本功能可能相當熟悉。大多數運算子的運作方式是將一個物件傳遞給欄位名稱,其中包含運算子和您要比較的數值,如下所示

<field_name>: { <operator>: <value_to_compare_against> }

等於

$eq 運算子檢查提供的值與文件中欄位值之間的相等性。在大多數情況下,這與我們在上面使用的相等性比較具有相同的功能。

例如,我們可以輸入以下內容來表達對名為「Brian」的學生的相同查詢

db.students.find({
first_name: { $eq: "Brian" }
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }

不等於

您也可以查詢等於提供值的的文件。此運算子為 $ne

例如,尋找所有已設定 grade_level 的學生的一種方法是搜尋欄位未設定為 null 的項目

db.students.find({
grade_level: { $ne: null }
})
{ "_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 }

大於

$gt 運算子可讓您查詢欄位值大於提供的參考編號的文件。

例如,若要尋找六年級以上年級學生的所有記錄,我們可以輸入

db.students.find({
grade_level: { $gt: 6 }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }

大於或等於

$gte 運算子表示查詢值等於或大於提供的值。

我們可以執行與上述相同的查詢,但另外輸入以下內容以包含六年級的學生

db.students.find({
grade_level: { $gte: 6 }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

小於

使用 $lt 運算子尋找小於提供值的值。

例如,我們可以輸入以下內容來檢視 2010 年 1 月 1 日之前的出生日期

db.students.find({
dob: { $lt: new Date("January 1, 2010") }
})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

小於或等於

$lte 運算子檢查小於或等於所提供參考的值。

例如,尋找六年級及以下年級的學生,輸入

db.students.find({
grade_level: { $lte: 6 }
})
{ "_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 }

比對一組值中的任何一個

$in 運算子的運作方式與 $eq 相等性運算子類似,但可讓您在陣列中提供多個可能的值。例如,它可以檢查值是否為 [8, 9, 10, 11] 中的任何一個,而不是檢查欄位值是否等於 8。

$in 運算子也適用於正規表示式。例如,我們可以輸入以下內容來尋找所有名字以「i」或「e」結尾的學生

db.students.find({
first_name: {
$in: [
/i$/,
/e$/
]
}
})
{ "_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("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

比對一組值中的任何一個都不符合

上述程序的反向操作是尋找所有值不在給定陣列中的文件。此運算子為 $nin

例如,我們可以輸入以下內容來尋找所有名字以「i」或「e」結尾的學生

db.students.find({
first_name: {
$nin: [
/i$/,
/e$/
]
}
})
{ "_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("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }

使用邏輯運算子篩選

若要形成更複雜的查詢,您可以使用邏輯運算子組合多個條件。邏輯運算子的運作方式是將運算式的物件或包含多個運算式物件的陣列傳遞給它們。

邏輯 AND 運算子

$and 運算子將傳回滿足傳遞給它的所有運算式的結果。$and 運算式中的每個運算式都必須評估為 true 才會傳回。

例如,您可以使用 $and 查詢同時設定了出生日期和年級的學生

db.students.find({
$and: [
{ dob: { $ne: null } },
{ grade_level: { $ne: null } }
]
})
{ "_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 }

邏輯 OR 運算子

$or 運算子執行邏輯 OR 運算。如果傳遞給它的任何運算式為 true,則整個子句都視為滿足。

例如,您可以使用它來查詢缺少我們在上面查詢的欄位中任一欄位的學生

db.students.find({
$or: [
{ dob: { $eq: null } },
{ grade_level: { $eq: null } }
]
})
{ "_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("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

邏輯 NOT 運算子

$not 運算子會否定傳遞給它的運算式的值。由於 $not 是一元運算子,因此它會直接對定義運算子運算式的單一物件進行運算,而不是對運算式陣列進行運算。

這會導致與先前的運算子略有不同的語法。您可以使用 $not 作為欄位比對的值的一部分,而不是包裝完整的欄位和值運算式,它只採用運算子運算式作為其引數,而不是完整的運算式(欄位名稱在 $not 運算式之外,而不是在其中)。

例如,我們可以輸入以下內容來尋找所有生日不在 2010 年之前的學生。這與檢查 dob 項目是否小於 2010 不同,因為它也會傳回任何未設定該欄位的文件

db.students.find({
dob: {
$not: {
$lt: new Date("January 1, 2010")
}
}
})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
{ "_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("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

邏輯 NOR 運算子

$nor 運算子會採用物件陣列,並傳回與這些物件中指定的任何條件都不符的文件。只會傳回未通過所有條件的文件。

例如,如果您想要檢索年級不是六年級且姓氏不以「s」結尾的學生的文件,您可以輸入

db.students.find({
$nor: [
{ grade_level: 6 },
{ last_name: /s$/ }
]
})
{ "_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("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }

依存在性篩選

其他一些測試方式是基於欄位或值的狀態。

例如,$exists 篩選器檢查文件內欄位的存在性。您可以將 $exists 設定為 truefalse,以判斷要檢索哪些文件。

例如,如果您想要尋找具有年級的學生文件,您可以輸入

db.students.find({
grade_level: { $exists: true }
})
{ "_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 }

依陣列特性篩選

您也可以透過文件持有的陣列查詢文件。有許多運算子可用於根據陣列元素或其他屬性進行比對。

指定必要元素

$all 運算子會傳回具有包含給定所有元素的陣列的文件。

例如,如果您只想檢索同時教授作文和文法的教師,您可以輸入

db.teachers.find({
subjects: {
$all: [ "composition", "grammar" ]
}
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }

單一元素的多個需求

如果受測陣列包含至少一個滿足所有提供條件的元素,$elemMatch 運算子會傳回文件。

作為一個相當無用的範例,若要傳回教授科目在字母順序上介於「literature」和「vocabulary」之間的教師文件,您可以輸入

db.teachers.find({
subjects: {
$elemMatch: {
$gt: "literature",
$lt: "vocabulary"
}
}
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Nancy", "last_name" : "Smith", "subjects" : [ "vocabulary", "pronunciation" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }

教授「pronunciation」的兩位教師都在此處列出,因為這是唯一滿足這兩個條件的元素。

依陣列大小查詢

最後,您可以使用 $size 運算子查詢特定大小的文件。例如,若要尋找所有教授三個科目的教師,請輸入

db.teachers.find({
subjects: { $size: 3 }
})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }
{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }

結論

在本指南中,我們介紹了如何使用 MongoDB 資料庫查詢文件。我們介紹了 find() 方法的基本運作方式,以及如何讓其輸出更易於閱讀。之後,我們檢視了 MongoDB 提供的許多運算子,以指定您感興趣文件的確切參數。

了解如何撰寫查詢以縮小結果範圍並挑選出符合您規格的文件,對於讀取和更新資料都很重要。透過熟悉各種可以將運算子鏈結在一起的方式,您可以表達符合不同類型文件的複雜需求。

常見問題解答

您可以在 find 陳述式中使用 $gt 運算子,以尋找日期欄位大於特定日期的文件。

基本語法如下所示

db.collection.find( { <Field Name>: { $gt:ISODate('Date here') } } )

MongoDB 資料庫查詢分析器是一種工具,可收集針對執行中的 mongod 執行個體執行的資料庫命令的詳細資訊。

這包括 CRUD 操作以及組態和管理命令。當嘗試排序以找出緩慢操作時,這可能特別有用。

若要查詢字串長度,您可以使用 `$strLenCP` 運算子。此運算子會傳回指定字串的 UTF-8 程式碼點數量。

基本語法如下所示

{ $strLenCP: "Hello World!" }

此特定字串將傳回值 12

若要僅查詢較大集合中欄位的唯一值,您可以使用 distinct() 方法。

基本語法如下所示

db.collection.distinct("<Field_Name>")

這會傳回集合中特定欄位的所有唯一值,且沒有重複。

您可以使用 mongoexport 命令列工具將資料庫內容匯出為 JSON。務必注意,這不應在 mongo shell 內執行,而應在命令列中執行。

基本語法如下所示,我們在其中指定集合匯出的輸出為 json

mongoexport --collection=events --db=reporting --out=events.json
關於作者
Justin Ellingwood

Justin Ellingwood

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