分享到

简介

查询文档是在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,而是可以检查该值是否为 [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 表达式中的每个表达式都必须评估为 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 运算符执行逻辑或计算。如果传入的任何表达式为 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 运算符返回文档,如果被测试的数组中至少包含一个满足所有提供条件的元素。

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

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)

贾斯汀自 2013 年以来一直撰写关于数据库、Linux、基础设施和开发者工具的文章。他目前与妻子和两只兔子住在柏林。他通常不需要以第三人称写作,这对所有相关方来说都是一种解脱。
© . All rights reserved.