本章将更深入地探索MongoDB的能力。您将通过高等查询技能提高您的专业知识,理解事务和并发性,并更清晰地理解MongoDB的后端流程。
本章将涵盖以下主题:
先容聚合框架

MongoDB中的聚合框架是一个数据处理工具,可帮助您实行繁芜的数据转换和打算。您可以利用该框架过滤、转换数据,并从中得到洞察,而不是在数据库外部编写脚本来处理它。
该框架是环绕数据处理管道的观点设计的,数据进入,当它从一个又一个阶段通过时,会发生转换。
聚合框架的灵巧性和表达性语法有助于您以各种办法塑造数据,从大略的过滤和分组到重塑全体文档。以下是一个示例,以更好地理解聚合的真正代价。
设想您正在利用MongoDB管理一所大学的学生记录。在各种凑集中,如课程、学生和西席,您有一个student_grades凑集。该凑集中的每个文档代表一个学生的课程成绩。文档构造可能如下所示:
{ "_id": ObjectId("5f4b7de8e8189a46aaf6e3ad"), "student_id": ObjectId("5f4b7de8e7139a46aaf5e4a5"), "course_id": ObjectId("5f4b7de8e7179a56aaf6e1b2"), "grade": 76, "semester": "Fall 2023"}
假设大学希望在每个学年结束时得到一份报告,他们对打算每个学生的均匀成绩感兴趣。面对大量的课程和学生,手动打算乃至利用传统脚本打算将非常繁琐且耗时。然而,利用MongoDB聚合框架,您可以构建一个高效而强大的管道来:
过滤成绩:利用$match阶段,您可以过滤出目标学期所需的成绩。按学生分组成绩:利用$group阶段,您可以按student_id分组成绩。同时,您可以打算每个学生的总成绩和科目数量。打算均匀成绩:分组后,您可以利用$project阶段打算每个学生的均匀成绩,通过将总成绩除以科目数量。以下是一个过滤、分组和打算均匀值的示例:
db.student_grades.aggregate([ { $match: { semester: "Fall 2023" } }, { $group: { _id: "$student_id" } }, { $project: { // 打算均匀值 averageGrade: { $divide: ["$totalGrade", "$totalCourses"] } } }])
MongoDB聚合的上风
聚合是MongoDB中的强大工具,许可您处理数据记录并返回打算结果。通过利用聚合框架,您可以以各种办法转换和组合数据,使剖析和提取故意义的洞察变得随意马虎。
在MongoDB中进行聚合供应了一系列上风,例如:
性能:本地数据库操作常日比提取数据并在外部处理更快。灵巧性:聚合框架供应了大量的工具和操作符,以繁芜的办法转换数据,常日减少了对外部数据处理逻辑的需求。数据完全性:在数据库级别操作确保了同等性和完全性,与可能会受到同步或事务问题影响的外部流程不同。资源效率:减少将大型数据集移出数据库进行处理的需求,可以导致网络带宽利用减少,以及减少运用程序做事器的内存或CPU开销。MongoDB聚合框架为您供应了一种强大、高效和可靠的数据处理方法,强调了它在实现最佳系统性能和全面数据剖析中的中央角色。
在接下来的部分中,您将探索聚合阶段,以更好地理解它们的能力和它们在当代数据驱动运用程序中所扮演的角色。
聚合阶段聚合阶段代表了数据转换过程中的一个特定步骤。每个阶段处理数据并输出文档,这些文档可以由后续阶段进一步处理。
让我们探索一些作为管道构建块的关键阶段:
$match利用$match来过滤文档,以便只有知足指定条件的文档才会通报到下一个管道阶段。这个舞台该当在管道的早期利用,由于过滤掉不必要的文档可以导致在后续阶段只处理干系文档,从而显著提高性能。 示例:查找所熟年龄超过22岁的用户。
输入
[ { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }, { "_id": 4, "name": "David", "age": 20 }]
管道
db.users.aggregate([{ $match: { age: { $gt: 22 } } }]);
输出
[ { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }]
$limit
如果您知道只须要结果的一个子集,可以在处理过程的早期利用$limit来减少通报给后续阶段的文档数量。 示例:只获取三个用户。
输入
[ { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }, { "_id": 4, "name": "David", "age": 20 }, { "_id": 5, "name": "Eve", "age": 29 }, { "_id": 6, "name": "Frank", "age": 26 }, { "_id": 7, "name": "Grace", "age": 24 }]
管道
db.users.aggregate([{ $limit: 3 }]);
输出
[ { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }]
$sort
如果须要排序,利用这个阶段来优化性能,特殊是如果有一个索引支持您的排序顺序。 示例:按年事降序排序用户。
输入
[ { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }, { "_id": 4, "name": "David", "age": 20 }, { "_id": 5, "name": "Eve", "age": 29 }, { "_id": 6, "name": "Frank", "age": 26 }, { "_id": 7, "name": "Grace", "age": 24 }]
管道
db.users.aggregate([{ $sort: { age: -1 } }]);
输出
[ { "_id": 5, "name": "Eve", "age": 29 }, { "_id": 6, "name": "Frank", "age": 26 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 7, "name": "Grace", "age": 24 }, { "_id": 3, "name": "Charlie", "age": 23 }, { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 4, "name": "David", "age": 20 }]
$skip
利用这个舞台跳过前N个文档,个中N是一个正整数,并将剩余的文档未修正地通报到下一个管道阶段。 示例:跳过前10个用户。
输入
[ { "_id": 1, "name": "Alice", "age": 21 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 23 }, { "_id": 4, "name": "David", "age": 20 }, { "_id": 5, "name": "Eve", "age": 29 }, { "_id": 6, "name": "Frank", "age": 26 }, { "_id": 7, "name": "Grace", "age": 24 }, { "_id": 8, "name": "Hannah", "age": 27 }, { "_id": 9, "name": "Ian", "age": 22 }, { "_id": 10, "name": "Jack", "age": 28 }, { "_id": 11, "name": "Katie", "age": 30 }, { "_id": 12, "name": "Liam", "age": 24 }]
管道
db.users.aggregate([{ $skip: 10 }]);
输出
[ { "_id": 11, "name": "Katie", "age": 30 }, { "_id": 12, "name": "Liam", "age": 24 }]
/project/set / $addFields
利用这些阶段重塑文档。$project阶段常日该当是您管道中的末了一个阶段,以指定要返回给客户真个字段。 示例:返回每个用户的唯一用户名和电话号码。
请把稳,对付$project / $set / $addFields的示例,您没有供应详细的输入和输出,因此我在这里没有展示详细的示例代码。如果您须要,我可以为您创建一个示例。
把稳每个记录都包含一个_id字段。这是由于在MongoDB中,默认情形下查询结果会包含_id字段。它作为每个文档的唯一标识符。除非在查询中明确打消,否则_id字段将涌如今输出中。
输入
[ { "_id": 1, "username": "Alice01", "age": 21, "phoneNumber": "123-456-7890", "email": "alice@example.com" }, { "_id": 2, "username": "Bob02", "age": 25, "phoneNumber": "234-567-8901", "email": "bob@example.com" }, { "_id": 3, "username": "Charlie03", "age": 23, "phoneNumber": "345-678-9012", "email": "charlie@example.com" }, { "_id": 4, "username": "David04", "age": 20, "phoneNumber": "456-789-0123", "email": "david@example.com" }]
管道
db.users.aggregate([{ $project: { username: 1, phoneNumber: 1 } }]);
输出
[ { "_id": 1, "username": "Alice01", "phoneNumber": "123-456-7890" }, { "_id": 2, "username": "Bob02", "phoneNumber": "234-567-8901" }, { "_id": 3, "username": "Charlie03", "phoneNumber": "345-678-9012" }, { "_id": 4, "username": "David04", "phoneNumber": "456-789-0123" }]
$group
在过滤和减少数据集之后,利用这个阶段按需对数据进行分组。 示例:获取用户的均匀年事,并按他们的国家进行分组。
输入
[ { "_id": 1, "username": "Alice", "age": 21, "country": "USA" }, { "_id": 2, "username": "Bob", "age": 25, "country": "USA" }, { "_id": 3, "username": "Charlie", "age": 30, "country": "UK" }, { "_id": 4, "username": "David", "age": 28, "country": "UK" }, { "_id": 5, "username": "Eve", "age": 29, "country": "Canada" }]
管道
db.users.aggregate([ { $group: { _id: "$country", averageAge: { $avg: "$age" } } }]);
输出
[ { "_id": "Canada", "averageAge": 29.0 }, { "_id": "UK", "averageAge": 29.0 }, { "_id": "USA", "averageAge": 23.0 }]
$unwind
当您须要展平数组字段并为每个元素天生一个新文档时,利用这个阶段。 示例:对付每个用户,解组爱好数组,并为他们的每个爱好天生一个文档。
输入
[ { "_id": 1, "username": "Alice", "hobbies": ["reading", "hiking", "swimming"] }, { "_id": 2, "username": "Bob", "hobbies": ["cycling", "painting"] }, { "_id": 3, "username": "Charlie", "hobbies": ["dancing", "cooking"] }, { "_id": 4, "username": "David", "hobbies": ["fishing"] }, { "_id": 5, "username": "Eve", "hobbies": [] }]
管道
db.users.aggregate([{ $unwind: "$hobbies" }]);
输出
[ { "_id": 1, "username": "Alice", "hobbies": "reading" }, { "_id": 1, "username": "Alice", "hobbies": "hiking" }, { "_id": 1, "username": "Alice", "hobbies": "swimming" }, { "_id": 2, "username": "Bob", "hobbies": "cycling" }, { "_id": 2, "username": "Bob", "hobbies": "painting" }, { "_id": 3, "username": "Charlie", "hobbies": "dancing" }, { "_id": 3, "username": "Charlie", "hobbies": "cooking" }, { "_id": 4, "username": "David", "hobbies": "fishing" }]
利用这个阶段实行与另一个凑集的左外连接。它许可输入凑集(您正在聚合的凑集)中的文档与不同凑集中的文档结合起来。为了有效地利用$lookup阶段,您必须理解其关键组件。这些组件在决定一个凑集中的文档如何与另一个凑集中的文档结合方面各扮演不同的角色。这些组件包括:
from字段,它标识您想要连接的凑集。localField和foreignField分别指定输入文档和from凑集中的文档的字段。as字段,它决定了添加到输入文档中的新数组字段的名称。这个数组包含来自from凑集的匹配文档。示例 1
订单凑集
[ { "_id": 1, "product_id": 100, "quantity": 2 }, { "_id": 2, "product_id": 101, "quantity": 5 }]
产品凑集
[ { "_id": 100, "name": "Laptop", "price": 1000, "stock": 10 }, { "_id": 101, "name": "Mouse", "price": 50, "stock": 0 }]
管道
db.orders.aggregate([ { $lookup: { from: "products", localField: "product_id", foreignField: "_id", as: "productDetails" } }]);
输出
[ { "_id": 1, "product_id": 100, "quantity": 2, "productDetails": [ { "_id": 100, "name": "Laptop", "price": 1000 } ] }, { "_id": 2, "product_id": 101, "quantity": 5, "productDetails": [ { "_id": 101, "name": "Mouse", "price": 50 } ] }]
示例 2
在这个示例中,您将利用MongoDB的$lookup阶段将订单与有库存的产品连接起来,为每个订单添加一个productDetails字段,个中包含产品的名称和价格。这种方法根据库存可用性动态地将订单链接到干系的产品信息。
管道
db.orders.aggregate([ { $lookup: { from: "products", let: { order_product_id: "$product_id" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$_id", "$$order_product_id"] }, // 仅匹配有库存的产品 { $gt: ["$stock", 0] } ] } } }, { $project: { name: 1, price: 1 } }, ], as: "productDetails", } }]);
输出 结果是订单文档的数组,个中每个文档都包括原始的订单字段(_id, product_id, quantity)加上productDetails数组,个中包含干系产品信息(仅有库存产品的名称和价格)。
[ { "_id": 1, "product_id": 100, "quantity": 2, "productDetails": [ { "name": "Laptop", "price": 1000 } ] }, { "_id": 2, "product_id": 101, "quantity": 5, "productDetails": [] }]
$geoNear
利用这个阶段根据文档与指定点的间隔对它们进行排序,从最近的到最远的。为了有效地利用$geoNear,凑集必须包含一个带有地理空间索引的字段。主要的是要把稳,$geoNear只能作为聚合管道中的第一个阶段利用。
请把稳,由于您没有供应$geoNear的示例输入、管道和输出,以是我在这里没有展示详细的示例代码。如果您须要,我可以为您创建一个示例。
示例:探求特定地点最近的餐馆输入
[ { "_id": 1, "name": "Deli Delight", "location": { "type": "Point", "coordinates": [-73.993, 40.7185] } }, { "_id": 2, "name": "Pizza Palace", "location": { "type": "Point", "coordinates": [-73.995, 40.717] } }, { "_id": 3, "name": "Burger Bliss", "location": { "type": "Point", "coordinates": [-73.99, 40.721] } }, { "_id": 4, "name": "Taco Tower", "location": { "type": "Point", "coordinates": [-73.997, 40.714] } }]
管道
db.restaurants.aggregate([ { $geoNear: { near: { type: "Point", coordinates: [-73.99279, 40.719296], }, distanceField: "distance", maxDistance: 2000, spherical: true } }])
输出
[ { "_id": 1, "name": "Deli Delight", "location": { "type": "Point", "coordinates": [-73.993, 40.7185] }, "distance": 88.12345678 }, { "_id": 2, "name": "Pizza Palace", "location": { "type": "Point", "coordinates": [-73.995, 40.717] }, "distance": 255.12345678 }, { "_id": 3, "name": "Burger Bliss", "location": { "type": "Point", "coordinates": [-73.99, 40.721] }, "distance": 200.12345678 }]
$redact
利用这个字段根据文档自身存储的信息来限定文档的内容。这常日用于数据访问掌握。
示例:仅显示用户有阅读权限的文档
输入
[ { "_id": 1, "title": "Document A", "content": "This is document A.", "access": "read" }, { "_id": 2, "title": "Document B", "content": "This is document B.", "access": "write" }, { "_id": 3, "title": "Document C", "content": "This is document C.", "access": "read" }, { "_id": 4, "title": "Document D", "content": "This is document D.", "access": "none" }]
管道
db.docs.aggregate([ { $redact: { $cond: { if: { $eq: ["$access", "read"] }, then: "$$DESCEND", else: "$$PRUNE" } } }])
输出
[ { "_id": 1, "title": "Document A", "content": "This is document A.", "access": "read" }, { "_id": 3, "title": "Document C", "content": "This is document C.", "access": "read" }]
$replaceWith
利用这个字段用指定的文档更换输入文档。
请把稳,您没有供应$replaceWith的示例输入、管道和输出,以是我在这里没有展示详细的示例代码。如果您须要,我可以为您创建一个示例。
示例:用其profile字段更换文档输入
[ { "_id": 1, "name": "Alice", "age": 25, "profile": { "hobbies": ["reading", "hiking"], "city": "New York" } }, { "_id": 2, "name": "Bob", "age": 30, "profile": { "hobbies": ["cycling", "painting"], "city": "Los Angeles" } }, { "_id": 3, "name": "Charlie", "age": 28, "profile": { "hobbies": ["dancing", "cooking"], "city": "Chicago" } }]
管道
db.users.aggregate([ { $replaceWith: "$profile" }])
输出
[ { "hobbies": ["reading", "hiking"], "city": "New York" }, { "hobbies": ["cycling", "painting"], "city": "Los Angeles" }, { "hobbies": ["dancing", "cooking"], "city": "Chicago" }]
MongoDB聚合框架是数据操作和剖析的强大工具。聚合阶段供应了一种灵巧高效的处理方法,用于转换、过滤数据并从中提取洞见,远远超出了大略的CRUD操作。通过理解这些聚合阶段,您现在可以处理各种任务——从繁芜的数据转换到天生繁芜的剖析报告。此外,将多个阶段链接在一起的能力供应了无限的可能性,使您能够构建知足特定需求的繁芜查询。采取这些能力不仅可以优化数据库操作,还可以使您能够从数据中获取更大的代价。
通过这些示例,您可以看到MongoDB聚合管道的强大功能,它许可您实行繁芜的数据操作,从而在数据库内部实现数据的转换和剖析,而无需将数据移动到其他系统。这使得MongoDB不仅适用于存储数据,还适用于处理和理解数据。
查询技能在本节中,您将探索数据库管理的高等查询技能。精确的查询对付高效的数据检索和剖析至关主要。本节将供应详细的方法和最佳实践,以确保查询的精确性和效率。
逻辑和比较运算符在MongoDB查询措辞中,逻辑和比较运算符是基本的构建块,许可用户根据特定条件过滤和选择数据。它们供应了一种对数据进行测试并确定哪些文档符合给定条件的方法:
$or利用此运算符实行逻辑OR操作,并返回匹配其数组中指定的任何条件的文档。 示例:查找年事为18或25的文档。
输入
[ { "_id": 1, "name": "Alice", "age": 18 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 20 }, { "_id": 4, "name": "David", "age": 25 }, { "_id": 5, "name": "Eve", "age": 19 }]
查询
// 查找年事为18或25的文档。db.users.find({ $or: [{ age: 18 }, { age: 25 }] })
输出
[ { "_id": 1, "name": "Alice", "age": 18 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 4, "name": "David", "age": 25 }]
$and
利用此运算符实行逻辑AND操作。它常日用于明确表示清晰,由于如果不该用运算符指定条件,MongoDB假定为AND操作。 示例:查找年事为22且名为Elie的文档。
输入
[ { "_id": 1, "name": "Alice", "age": 22 }, { "_id": 2, "name": "Elie", "age": 22 }, { "_id": 3, "name": "Charlie", "age": 22 }, { "_id": 4, "name": "David", "age": 25 }, { "_id": 5, "name": "Elie", "age": 23 }]
隐式$and操作
// 查找年事为22且名为Elie的文档。db.users.find({ age: 22, name: "Elie" })
输出
[{ "_id": 2, "name": "Elie", "age": 22 }]
显式$and操作
db.users.find({ $and: [ { age: 22 }, { name: "Alice" } ] })
输出
[{ "_id": 2, "name": "Alice", "age": 22 }]
$not
利用此运算符来否定条件的效果,返回不匹配指定条件的文档。 示例:返回年事不是18的文档。
输入
[ { "_id": 1, "name": "Alice", "age": 18 }, { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 22 }, { "_id": 4, "name": "David", "age": 18 }, { "_id": 5, "name": "Eve", "age": 23 }]
查询
// 返回年事不是18的文档。db.users.find({ age: { $not: { $eq: 18 } } })
输出
[ { "_id": 2, "name": "Bob", "age": 25 }, { "_id": 3, "name": "Charlie", "age": 22 }, { "_id": 5, "name": "Eve", "age": 23 }]
$nor
利用此运算符匹配不知足所有供应条件的文档。 示例:返回年事不是22且名字不是Elie的文档。
输入
[ { "_id": 1, "name": "Alice", "age": 22 }, { "_id": 2, "name": "Elie", "age": 23 }, { "_id": 3, "name": "Charlie", "age": 22 }, { "_id": 4, "name": "David", "age": 24 }, { "_id": 5, "name": "Eve", "age": 25 }]
查询
// 返回年事不是22且名字不是Elie的文档。db.users.find({ $nor: [ { age: 22 }, { name: "Elie" } ] })
输出
[ { "_id": 4, "name": "David", "age": 24 }, { "_id": 5, "name": "Eve", "age": 25 }]
和in和nin
利用这些运算符来匹配数组中指定的任何值或没有一个值。
请把稳,您没有供应$in和$nin的示例输入、查询和输出,以是我在这里没有展示详细的示例代码。如果您须要,我可以为您创建一个示例。
通过这些逻辑和比较运算符,MongoDB许可您构建繁芜的查询,以风雅地过滤和选择您须要的数据。节制这些查询技能对付任何必要处理数据库的开拓者或数据剖析师来说都是非常主要的。
示例:利用$in仅匹配具有特定年事的人,利用$nin仅匹配没有的年事。输入
[ { "_id": 1, "name": "Alice", "age": 18 }, { "_id": 2, "name": "Bob", "age": 20 }, { "_id": 3, "name": "Charlie", "age": 22 }, { "_id": 4, "name": "David", "age": 23 }, { "_id": 5, "name": "Eve", "age": 25 }]
$in 运算符
db.users.find({ age: { $in: [18, 20, 22] } }) // 年事是18、20或22
输出
[ { "_id": 1, "name": "Alice", "age": 18 }, { "_id": 2, "name": "Bob", "age": 20 }, { "_id": 3, "name": "Charlie", "age": 22 }]
$nin 运算符
db.users.find({ age: { $nin: [18, 20, 22] } })
输出
[ { "_id": 4, "name": "David", "age": 23 }, { "_id": 5, "name": "Eve", "age": 25 }]
$eq:利用此运算符匹配字段值即是指定值的文档。
示例:利用 $eq 查找恰好有200个座位的飞机。
输入
[ { "_id": 1, "model": "Boeing 737", "seats": 200 }, { "_id": 2, "model": "Airbus A320", "seats": 180 }, { "_id": 3, "model": "Boeing 747", "seats": 400 }, { "_id": 4, "model": "Airbus A330", "seats": 200 }, { "_id": 5, "model": "Boeing 777", "seats": 250 }]
隐式 $eq
db.airplanes.find({ seats: 200 });
输出
[{ "_id": 1, "model": "Boeing 737", "seats": 200 }]
显式 $eq
db.airplanes.find({ seats: { $eq: 200 } });
$ne:利用此运算符匹配字段值不即是指定值的文档。
示例:利用 $ne 检索所有除了型号为 "Boeing 777" 的飞机。
输入
// 检索所有飞机,除了型号为 "Boeing 777" 的。db.airplanes.find({ model: { $ne: "Boeing 777" } })
输出
[ { "_id": 1, "model": "Boeing 737", "seats": 200 }, { "_id": 2, "model": "Airbus A320", "seats": 180 }, { "_id": 3, "model": "Boeing 747", "seats": 400 }, { "_id": 4, "model": "Airbus A330", "seats": 200 }]
$gt(大于) 和$lt(小于):利用$gt运算符匹配字段值大于指定值的文档。比较之下,$lt运算符用于匹配字段值小于给定值的文档。
$gte (大于或即是) 和 $lte (小于或即是):利用 $gte 运算符匹配字段值大于或即是指定值的文档。另一方面,$lte 运算符匹配字段值小于或即是给定值的文档。 示例:利用 $gte 和 $lte 一起检索最大速率大于500且低于700的飞机。
输入
[ { "_id": 1, "model": "Boeing 737", "maxSpeed": 485 }, { "_id": 2, "model": "Airbus A320", "maxSpeed": 530 }, { "_id": 3, "model": "Boeing 747", "maxSpeed": 570 }, { "_id": 4, "model": "Airbus A330", "maxSpeed": 520 }, { "_id": 5, "model": "Boeing 777", "maxSpeed": 710 }, { "_id": 6, "model": "Concorde", "maxSpeed": 1350 }, { "_id": 7, "model": "Lockheed SR-71 Blackbird", "maxSpeed": 2200 }]
查询
db.airplanes.find({ maxSpeed: { $gt: 500, $lte: 700 }})
输出
[ { "_id": 2, "model": "Airbus A320", "maxSpeed": 530 }, { "_id": 3, "model": "Boeing 747", "maxSpeed": 570 }, { "_id": 4, "model": "Airbus A330", "maxSpeed": 520 }]
MongoDB中的逻辑和比较运算符供应了一个强大的框架,用于根据特定条件过滤和检索文档。这些运算符许可您构建精确的查询,确保检索到的数据既干系又故意义。通过节制这些运算符的利用,您可以优化数据库交互,减少不必要的数据检索,并确保运用程序只访问它们须要的数据。与任何工具集一样,关键是要理解每个运算符的细微差别,并谨严地运用它们,平衡精确度和性能。
数组查询和操作在MongoDB查询措辞中,数组查询和操作许可基于数组内容精确选择和修正文档。利用如$elemMatch和$all等运算符,以及数组更新运算符如$push和$pull,您可以高效地与文档内的基于数组的数据构造进行交互和修正。让我们更详细地看看这些运算符:
$all利用此运算符匹配包含查询中指定的所有元素的数组。 示例:查找标签包含nosql和mongodb的文档。
输入
[ { "_id": 1, "title": "Intro to NoSQL", "tags": ["database", "nosql"] }, { "_id": 2, "title": "Mastering MongoDB", "tags": ["mongodb", "database", "nosql"] }, { "_id": 3, "title": "SQL for Beginners", "tags": ["database", "sql"] }, { "_id": 4, "title": "MongoDB & NoSQL Exploration", "tags": ["mongodb", "nosql", "advanced"] }]
查询
db.books.find({ tags: { $all: ["nosql", "mongodb"] } })
输出
[ { "_id": 2, "title": "Mastering MongoDB", "tags": ["mongodb", "database", "nosql"] }, { "_id": 4, "title": "MongoDB & NoSQL Exploration", "tags": ["mongodb", "nosql", "advanced"] }]
$elemMatch
利用此运算符匹配数组字段知足多个条件的文档。 示例:假设有一个联赛凑集,每个文档都有一个结果数组,每个结果是一个子文档,包含每个玩家和他们的得分字段。
输入
[ { "_id": 1, "results": [ { "player": "Alice", "score": 82 }, { "player": "Bob", "score": 90 } ] }, { "_id": 2, "results": [ { "player": "Charlie", "score": 78 }, { "player": "David", "score": 83 } ] }, { "_id": 3, "results": [ { "player": "Eve", "score": 85 }, { "player": "Frank", "score": 88 } ] }]
查询
db.league.find({ results: { $elemMatch: { score: { $gte: 80, $lt: 85 } } }})
输出
[ { "_id": 1, "results": [ { "player": "Alice", "score": 82 }, { "player": "Bob", "score": 90 } ] }, { "_id": 2, "results": [ { "player": "Charlie", "score": 78 }, { "player": "David", "score": 83 } ] }]
$size
利用此运算符匹配具有指天命量元素的数组。 示例:查找标签数组恰好有三个元素的文档。
输入
[ { "_id": 1, "title": "Intro to NoSQL", "tags": ["database", "nosql", "beginner"] }, { "_id": 2, "title": "Mastering MongoDB", "tags": ["mongodb", "database", "nosql", "advanced"] }, { "_id": 3, "title": "SQL for Beginners", "tags": ["database", "sql"] }, { "_id": 4, "title": "MongoDB & NoSQL Exploration", "tags": ["mongodb", "nosql", "intermediate"] }]
查询
db.books.find({ tags: { $size: 3 } })
输出
[ { "_id": 1, "title": "Intro to NoSQL", "tags": ["database", "nosql", "beginner"] }, { "_id": 4, "title": "MongoDB & NoSQL Exploration", "tags": ["mongodb", "nosql", "intermediate"] }]
$push
利用此运算符向数组中添加一个项目。 示例:利用$push向AirFly的航线列表中添加一条新的东京航线。
输入
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London", "Paris"] }, { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Cape Town"] }]
查询 $push
db.airlines.updateOne( { name: "AirFly" }, { $push: { destinations: "Tokyo" } })
查询 find:实行find查询以检索最新更新的数据
db.airlines.find({})
输出
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London", "Paris", "Tokyo"] }, { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Cape Town"] }]
$pull
利用此运算符从数组中移除一个指定的值。
请把稳,您没有供应$pull的示例输入、查询和输出,以是我在这里没有展示详细的示例代码。如果您须要,我可以为您创建一个示例。
通过这些数组查询和操作运算符,您可以对MongoDB中的数组数据实行繁芜的查询和更新,从而实现对文档中数组字段的风雅掌握。
示例:利用$pull从AirFly的目的地列表中移除“巴黎”更新查询
db.airlines.updateOne( { name: "AirFly" }, { $pull: { destinations: "Paris" } })
查询 find:实行find查询以检索 pull 操作后的最新更新数据
db.airlines.find({})
输出
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London", "Tokyo"] }, { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Cape Town"] }]
$addToSet
利用此运算符向数组中添加一个值,但如果该值已经存在,则不会添加。 示例:检讨“柏林”是否不存在于目的地数组中,然后添加它。
输入
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London", "Paris"] }, { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Cape Town"] }]
更新查询
db.airlines.updateOne( { name: "AirFly" }, { $addToSet: { destinations: "Berlin" } });
查询 find:实行find查询以检索 addToSet 操作后的最新更新数据
db.airlines.find({})
输出
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London", "Paris", "Berlin"] }, // “柏林”被添加,由于它之前不在数组中。 { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Berlin"] } // 保持不变,由于“柏林”已经在数组中。]
$pop
利用此运算符从数组中移除第一个或末了一个项目。值为-1时移除第一个项目,值为1时移除末了一个项目。
更新查询
db.airlines.updateOne({ name: "AirFly" }, { $pop: { destinations: 1 } })
查询 find:实行find查询以检索 $pop 操作后的最新更新数据
db.airlines.find({})
输出
[ { "_id": 1, "name": "AirFly", "destinations": ["New York", "London"] }, // 数组末端的“巴黎”被移除。 { "_id": 2, "name": "SkyHigh", "destinations": ["Sydney", "Delhi", "Berlin"] } // 保持不变。]
MongoDB中的数组查询和操作功能为管理繁芜的数据构造供应了强大的办理方案。直接与数组元素交互和修正增强了数据检索和更新过程。随着数据日益变得繁芜,节制这些数组操为难刁难于您在不断演化的运用程序环境中保持灵巧性和性能至关主要。
数组字段投影技能在MongoDB查询措辞中,投影技能决定了却果集中从数组字段返回哪些元素。利用如$、$elemMatch和$slice等运算符,您可以定制查询以检索特天命据,优化带宽和处理韶光,以实现更高效的数据库交互。让我们更详细地看看这些运算符:
$在投影中,$运算符用于从匹配查询条件的数组中投影仅有的第一个元素。 示例:利用$查找Bob的第一篇评论。
输入
{ "_id": 1, "title": "Mastering MongoDB", "reviews": [ { "reviewer": "Alice", "comment": "Great book!" }, { "reviewer": "Bob", "comment": "Informative." }, { "reviewer": "Charlie", "comment": "A must-read for database enthusiasts." } ]}
查询
db.books.find({ "reviews.reviewer": "Bob" }, { "reviews.$": 1 });
输出
{ "_id": 1, "reviews": [{ "reviewer": "Bob", "comment": "Informative." }]}
$elemMatch
利用此运算符在数组元素上指定多个条件,以便至少有一个数组元素知足所有指定条件。它特殊适用于处理数组的子文档。 示例:考虑一个学生凑集,每个学生都有一个考试成绩数组。
输入
[ { "_id": 1, "name": "John", "scores": [85, 88, 92] }, { "_id": 2, "name": "Jane", "scores": [78, 88, 89] }, { "_id": 3, "name": "Doe", "scores": [92, 90, 85, 88] }]
查询
db.students.find({ scores: { $elemMatch: { $gte: 85, $lte: 90 } } });
输出
[ { "_id": 1, "name": "John", "scores": [85] }, { "_id": 2, "name": "Jane", "scores": [88] }, { "_id": 3, "name": "Doe", "scores": [90] }]
$slice
利用此运算符限定从数组投影的元素数量。 示例:仅返回末了三个成绩。
输入
[ { "_id": 1, "team": "Tigers", "scores": [85, 88, 90, 92, 95] }, { "_id": 2, "team": "Lions", "scores": [78, 80, 82, 84, 86] }]
查询
// 返回末了3个成绩。db.league.find({}, { scores: { $slice: -3 } });
输出
[ { "_id": 1, "team": "Tigers", "scores": [90, 92, 95] }, { "_id": 2, "team": "Lions", "scores": [82, 84, 86] }]
索引和查询优化
MongoDB中的索引是分外的数据构造,在提高查询效率方面起着至关主要的浸染。没有这些索引,MongoDB将不得不在凑集中的每个文档中筛选查询结果。可用的索引可以大幅减少MongoDB检讨的文档数量。
利用索引的好处索引的好处包括:
性能提升:索引显著加快数据检索操作,减少了扫描凑集中每个文档的须要。减少负载:通过加速搜索查询,索引降落了系统的负载,导致更好的资源利用。排序效率:索引支持高效的排序操作,许可结果有序而无需额外的打算事情。支持繁芜查询:通过专门的索引,高等查询操作(如地理空间搜索或基于文本的搜索)变得可能和高效。选择性加载:索引可以限定加载到内存中的数据量,许可MongoDB更有效地处理大型数据集。在MongoDB中有效利用索引须要计策考虑。虽然它们优化了查询性能,但也占用了额外的存储空间。寻思熟虑地选择要索引的字段可以平衡性能增益与空间限定。
只管索引加快了读取操作,但由于须要更新索引,它们为写操作引入了一些延迟。通过理解索引的这些方面并将其与系统的须要对齐,您可以发挥索引的全部潜力,同时减轻潜在的寻衅。
索引类型MongoDB供应了多种索引类型,每种都旨在优化特定的查询模式。通过选择精确的索引,您可以显著提高查询性能。在这里,您可以探索关键的索引类型、它们的紧张用场以及它们的实现示例:
单字段索引描述:在文档的单个字段上创建索引。用例:适用于基于单个字段进行搜索的查询。示例:优化按用户名搜索的操作:db.users.createIndex({ "username": 1 })
复合索引描述:在文档内的多个字段上创建索引。用例:适用于在多个字段上进行排序或搜索的查询。此索引按升序排序firstname,按降序排序lastname。示例:
db.users.createIndex({ "firstname": 1, "lastname": -1 })
多键索引描述:在数组字段上创建索引,索引数组的每个元素。用例:适用于查询数组内容,如标签或种别。示例:
db.collection.createIndex({ "tags": 1 })
复合多键索引描述:在文档的多个字段上创建索引,至少一个是数组。MongoDB索引数组的每个元素,但有一个限定,即在复合多键索引中只能索引一个数组字段。用例:适用于在多个字段上进行排序或过滤查询,个中至少一个字段可能是数组。它们特殊适用于具有繁芜关系或多维属性的数据。示例:假设有一个文档凑集,个中tags是数组,rating是数值,并且您想创建一个复合多键索引:
db.collection.createIndex({ "tags": 1, "rating": -1 })
文本索引描述:许可对字符串内容和字符串内容数组进行全面文本搜索。文本索引可以包括任何值为字符串或字符串元素数组的字段。用例:适用于在多个字段中搜索文本内容,并为干系性分配字段权重。示例:在description字段上创建文本索引:
db.post.createIndex({ "description": "text" })
通配符索引描述:无论字段在文档中的位置如何,都进行索引。它特殊适用于处理无模式或不断演化的数据模型。用例:适用于在不知道键的情形下对嵌入文档中的字段进行索引,或者对文档中的每个字段进行索引。示例:对文档中的所有字段进行索引:
db.users.createIndex({ "$": 1 })
时效索引(TTL)描述:在指定的持续韶光后启用文档的自动删除。用例:非常适宜须要过期的数据,如日志或会话。示例:在创建每个日志项后的1小时内自动删除:
db.logs.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 })
唯一索引描述:逼迫实行索引字段值的唯一性。用例:当您须要确保字段(如用户名或电子邮件)是唯一的。示例:确保每当有人新注册时用户名是唯一的:
db.users.createIndex({ "username": 1 }, { unique: true })
部分索引描述:仅索引知足指定过滤条件的文档。用例:适用于只有文档子集须要被索引的情形,例如生动的账户。示例:在凑集中仅索引18岁或以上的人:
db.users.createIndex( { age: 1 }, { partialFilterExpression: { age: { $gte: 18 } } })
结论
索引对付提高MongoDB中的性能至关主要。选择和管理精确的索引类型有助于加快数据库查询速率并提高运用程序的效率。始终考虑数据和查询需求的最佳索引策略非常主要。
MongoDB中的地理空间功能MongoDB中的地理空间功能旨在支持创建位置感知运用程序并促进基于位置的查询,知足不同用户的需求。通过其专门的索引和运算符,MongoDB可以有效地管理地理数据,使实在用于各种运用程序,如舆图和位置搜索。
遗留坐标对是利用两个元素数组表示位置的传统办法,个中经度在前,纬度在后。例如,[-73.97, 40.77]表示纽约市的一个点。在对此类数据进行索引时,您可以利用2d索引类型。以下是为遗留坐标对创建索引的示例:
db.collection.createIndex({ loc: "2d" })
通过这些索引类型和地理空间功能,MongoDB供应了强大的工具集,用于构建和管理须要处理繁芜数据构造和实行高效查询的运用程序。
地理空间工具(GeoJSON objects)地理空间工具有三种类型:
Point表示具有经度和纬度坐标的单个空间点。
LineString由两个或多个点组成的一系列点,形成一条线。
Polygon由点构成的线性环,形成封闭环路,定义了一个区域。
地理空间索引(Geospatial indexes)地理空间索引是专门优化数据库中空间查询的专用数据构造,使基于位置的信息检索更快。
2d索引这些索引旨在支持在二维平面上表示为点的数据的查询。它们紧张用于遗留坐标对。创建2d索引时,您将2d指定为索引类型。示例如下:
db.collection.createIndex({ loc: "2d" })
2dsphere索引
这些是MongoDB中为球形表面(如地球)上的地理空间查询设计的专用索引。它们许可实行诸如识别某个区域内的点、打算到指定点的靠近度以及获取精确坐标匹配等操作。索引字段可以是GeoJSON工具或遗留坐标对。在遗留对的情形下,2dsphere索引会自动将它们转换为GeoJSON点。示例如下:
db.collection.createIndex({ loc: "2dsphere" })
地理空间运算符(Geospatial operators)
地理空间运算符是专门用于增强地理空间数据库中空间剖析的查询工具,实现基于位置的数据检索。
$near返回按与特定点的间隔排序的工具,从最近到最远。
$nearSphere返回球形上与点靠近的地理空间工具。
$geoWithin查找几何图形完备位于指定形状内的文档。
$geoIntersects返回几何图形与查询中定义的形状相交的文档。
$maxDistance限定利用或near或nearSphere的地理空间查询的结果。此运算符指定从点开始的最大间隔,超出此间隔的文档将不包含在查询结果中。主要的是要把稳,$maxDistance指定的值必须是非负数。
以下是利用$maxDistance查询附近地点的示例:
// 创建2dsphere索引:db.places.createIndex({ location: "2dsphere" });// 查询附近地点(在点的500米范围内):db.places.find({ location: { $near: { $geometry: { type: "Point", coordinates: [-73.9667, 40.78] }, $maxDistance: 500 } }})
以下是查询位于指定多边形形状内的文档的示例:
// 查询位于指定多边形内的文档:db.places.find({ location: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [0, 0], [3, 6], [6, 1], [0, 0] ] ] } } }})
MongoDB中的地理空间功能对付处理位置数据的当代运用程序至关主要。从查询靠近度到在球形表面上定义兴趣区域,这些专用索引和功能确保了空间操作的高效和精确。随着运用程序变得更加位置感知和数据驱动,利用这些地理空间工具对付供应有代价的洞察和用户体验变得越来越主要。
总结在本章中,您深入理解了MongoDB的许多高等功能。您探索了聚合框架,提高了查询技能,并理解了索引的主要性。有了这些工具,您将更好地准备应对繁芜的数据库任务,并充分利用MongoDB。随着您的深入,运用您所学并连续在这个根本上构建。
不才一章中,您将深入研究聚合框架,并探索适宜最多样化问题的浩瀚聚合阶段和表达式运算符。