简介
查询 文档 是在 MongoDB 中执行许多不同操作所必需的基本技能。您需要能够有效查询以检索所需文档,更新数据库中的现有信息,并了解文档之间的异同。
在本指南中,我们将介绍如何编写 MongoDB 查询的基础知识,以帮助您根据要求检索文档。我们将向您展示查询的一般工作方式,然后我们将探讨 MongoDB 提供的各种运算符,以帮助您通过评估条件来缩小结果范围。
如果您正在使用 MongoDB,请查看 Prisma 的 MongoDB 连接器!您可以使用 Prisma Client 自信地管理生产 MongoDB 数据库。
要开始使用 MongoDB 和 Prisma,请查看我们的 从头开始指南 或如何添加到现有项目。
创建示例集合
在本文中,我们将使用一个名为 students 的集合和一个名为 teachers 的集合,它们都存储在一个名为 school 的数据库中。
您可以使用以下命令创建示例数据库并填充集合
use schooldb.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 运算符允许您查询字段值大于提供的参考数字的文档。
例如,要查找所有高于 6 年级学生的记录,我们可以输入
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 运算符表示查询值等于或大于提供值。
我们可以执行与上面相同的查询,但另外包括 6 年级学生,方法是输入
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 运算符检查小于或等于提供参考的值。
例如,查找 6 年级及以下的学生,输入
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 表达式中的每个表达式都必须评估为 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 运算符执行逻辑或计算。如果传递给它的任何表达式为 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 表达式之外而不是内部)。
例如,我们可以通过输入查找所有在 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 运算符接受一个对象数组,并返回不匹配这些对象中指定任何条件的文档。只有未能满足所有条件的文档才会被返回。
例如,如果您想检索不在 6 年级且姓氏不以“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 设置为 true 或 false 以确定要检索哪些文档。
例如,如果您想查找具有年级的学生文档,您可以输入
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 提供的许多运算符,以指定您感兴趣的文档的确切参数。
了解如何编写查询以缩小结果范围并筛选出符合您规范的文档在读取和更新数据时都非常重要。通过熟悉运算符的各种链式方式,您可以表达匹配不同类型文档的复杂要求。
如果您正在使用 MongoDB,请查看 Prisma 的 MongoDB 连接器!您可以使用 Prisma Client 自信地管理生产 MongoDB 数据库。
要开始使用 MongoDB 和 Prisma,请查看我们的 从头开始指南 或如何添加到现有项目。
常见问题解答
您可以使用 find 语句中的 $gt 运算符查找日期字段大于特定日期的文档。
基本语法如下所示
db.collection.find( { <Field Name>: { $gt:ISODate('Date here') } } )
MongoDB 数据库查询分析器 是一个工具,用于收集有关针对正在运行的 mongod 实例执行的数据库命令的详细信息。
这包括 CRUD 操作以及配置和管理命令。这在尝试筛选慢速操作时特别有用。
基本语法如下
{ $strLenCP: "Hello World!" }
这个特定的字符串将返回一个值为 12。
要查询更大集合中某个字段的唯一值,您可以使用 distinct() 方法。
基本语法如下
db.collection.distinct("<Field_Name>")
这会返回集合中特定字段的所有唯一值,没有重复。
您可以使用 mongoexport 命令行工具将数据库内容导出为 JSON。需要注意的是,这不应在 mongo shell 中运行,而应在命令行中运行。
基本语法如下所示,其中我们将集合导出的输出指定为 json
mongoexport --collection=events --db=reporting --out=events.json
