分享到

简介

查询 文档 是在 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 运算符检查提供的 value 与文档中的字段 value 之间是否相等。在大多数情况下,这与我们在上面使用的等式比较具有相同的功能。

例如,我们可以通过键入以下内容来表达对名为“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,而是检查值是否为[8, 9, 10, 11]中的任何一个。

$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表达式中的每个表达式都必须计算为真。

例如,您可以使用$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 计算。如果传递给它的任何表达式为真,则整个子句都被视为满足。

例如,您可以使用它来查询缺少我们上面查询的任何一个字段的学生

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 年之前生日的学生。这与检查小于 2010 年的dob条目不同,因为它还会返回所有未设置该字段的文档

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 运算符如果被测试的数组包含至少一个满足所有提供条件的元素,则返回文档。

作为一个相当无用的例子,要返回教授字母顺序在“文学”和“词汇”之间的科目的教师的文档,您可以输入

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" ] }

教授“发音”的两名教师都列在这里,因为那是唯一满足这两个条件的元素。

按数组大小查询

最后,您可以使用 $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、基础设施和开发人员工具的文章。他现在住在柏林,与他的妻子和两只兔子一起生活。他通常不必以第三人称写作,这对所有相关方来说都是一种解脱。