分享到

简介

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

例如,我们可以通过键入以下内容来表达对名为“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 运算符执行逻辑 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 年以来,Justin 一直在撰写关于数据库、Linux、基础设施和开发者工具的文章。他目前与妻子和两只兔子住在柏林。他通常不必以第三人称写作,这对所有相关方来说都是一种解脱。