查询语法详解
本章深入介绍 MongoDB 的查询语法,包括查询运算符、正则表达式、地理空间查询等高级功能。
查询运算符详解
比较运算符
// $eq - 等于
db.users.find({ age: { $eq: 25 } })
// 简写形式
db.users.find({ age: 25 })
// $ne - 不等于
db.users.find({ status: { $ne: "deleted" } })
// $gt / $gte - 大于 / 大于等于
db.products.find({ price: { $gt: 100 } })
db.products.find({ price: { $gte: 100 } })
// $lt / $lte - 小于 / 小于等于
db.products.find({ price: { $lt: 1000 } })
db.products.find({ price: { $lte: 1000 } })
// $in / $nin - 在数组中 / 不在数组中
db.users.find({ role: { $in: ["admin", "moderator"] } })
db.products.find({ category: { $nin: ["outlet", "clearance"] } })
元素查询
// $exists - 检查字段是否存在
// 查找有 email 字段的用户
db.users.find({ email: { $exists: true } })
// 查找没有 phone 字段的用户
db.users.find({ phone: { $exists: false } })
// $type - 根据 BSON 类型查询
// 查找 age 字段为数字类型的文档
db.users.find({ age: { $type: "number" } })
// 常见类型:double, string, object, array, bool, null, int, long
db.users.find({ score: { $type: "number" } })
评估运算符
// $regex - 正则表达式匹配
db.users.find({ username: { $regex: "^zhang" } })
db.users.find({ email: { $regex: /@example\.com$/ } })
// $text - 文本搜索(需要创建文本索引)
db.articles.find({ $text: { $search: "mongodb tutorial" } })
// $where - JavaScript 表达式(性能较差,慎用)
db.users.find({ $where: "this.age > 25 && this.name.startsWith('z')" })
// $mod - 取模运算
db.products.find({ price: { $mod: [100, 0] } }) // price % 100 == 0
数组查询
// 准备测试数据
db.students.insertMany([
{ name: "Alice", scores: [85, 90, 92] },
{ name: "Bob", scores: [70, 75, 80] },
{ name: "Charlie", scores: [95, 88, 91] }
])
// $all - 数组包含所有指定元素
db.students.find({ scores: { $all: [85, 90] } })
// $elemMatch - 数组中至少有一个元素匹配条件
db.students.find({ scores: { $elemMatch: { $gt: 90 } } })
// $size - 数组长度
db.students.find({ tags: { $size: 3 } })
// 索引访问 - 获取数组特定位置的元素
db.students.find({ "scores.0": { $gt: 80 } }) // 第一个分数 > 80
嵌套文档查询
// 准备数据
db.orders.insertMany([
{
orderId: "ORD001",
customer: { name: "张三", city: "Beijing", age: 30 },
items: [{ product: "iPhone", price: 999 }, { product: "Case", price: 30 }]
},
{
orderId: "ORD002",
customer: { name: "李四", city: "Shanghai", age: 25 },
items: [{ product: "MacBook", price: 1999 }]
}
])
// 点符号查询嵌套字段
db.orders.find({ "customer.city": "Beijing" })
// 精确匹配嵌套文档(字段顺序必须一致)
db.orders.find({ customer: { name: "张三", city: "Beijing" } })
// 嵌套数组中的文档查询
db.orders.find({ "items.product": "iPhone" })
// 嵌套数组中特定条件的文档
db.orders.find({ "items": { $elemMatch: { price: { $gt: 500 } } } })
正则表达式查询
MongoDB 支持正则表达式进行模式匹配:
// 基本用法
db.users.find({ username: { $regex: "zhang" } })
// 使用正则表达式字面量
db.users.find({ email: { $regex: /@gmail\.com$/i } }) // i 表示不区分大小写
// 正则表达式选项
// i - 不区分大小写
// m - 多行模式
// x - 忽略空白字符
db.users.find({ bio: { $regex: "developer|engineer", $options: "i" } })
// 常用正则模式
// 以某字符串开头
db.users.find({ username: { $regex: "^admin" } })
// 以某字符串结尾
db.users.find({ email: { $regex: "@example\\.com$" } })
// 包含某字符串
db.users.find({ name: { $regex: "zhang" } })
// 精确匹配(忽略正则)
db.users.find({ name: "张三" }) // 比正则更高效
地理空间查询
MongoDB 支持地理位置查询,需要使用 GeoJSON 格式:
// 创建带地理位置的文档
db.places.insertMany([
{
name: "Central Park",
location: { type: "Point", coordinates: [-73.965355, 40.782865] },
category: "park"
},
{
name: "Times Square",
location: { type: "Point", coordinates: [-73.9855, 40.7580] },
category: "attraction"
},
{
name: "Empire State Building",
location: { type: "Point", coordinates: [-73.9857, 40.7484] },
category: "building"
}
])
// 创建 2dsphere 索引(用于地球表面计算)
db.places.createIndex({ location: "2dsphere" })
// $near - 查询附近的点(返回距离)
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [-73.97, 40.77] },
$maxDistance: 5000 // 5 公里
}
}
})
// $geoWithin - 查询范围内的几何图形
db.places.find({
location: {
$geoWithin: {
$centerSphere: [[-73.97, 40.77], 5 / 6378.1] // 5 公里
}
}
})
投影操作符
投影用于控制返回的字段:
// 1 - 包含字段,0 - 排除字段
db.users.find({}, { username: 1, email: 1 }) // 只返回 username 和 email(默认包含 _id)
// 排除 _id 字段
db.users.find({}, { _id: 0, username: 1, email: 1 })
// 排除敏感字段
db.users.find({}, { password: 0, salt: 0 })
// 条件投影 - $slice(返回数组的一部分)
db.orders.find({}, { items: { $slice: 5 } }) // 前 5 个元素
db.orders.find({}, { items: { $slice: [5, 10] } }) // 跳过 5 个,返回 10 个
// 条件投影 - $elemMatch(返回数组中匹配的第一个元素)
db.students.find(
{ scores: { $elemMatch: { $gt: 90 } } },
{ name: 1, "scores.$": 1 }
)
// $ - 数组元素投影(返回匹配的第一个元素)
db.students.find(
{ "scores.score": { $gt: 90 } },
{ name: 1, "scores.$": 1 }
)
查询修饰符
// sort - 排序
db.users.find().sort({ age: 1, name: -1 }) // 年龄升序,名字降序
// limit - 限制返回数量
db.products.find().limit(10)
// skip - 跳过指定数量(分页)
db.products.find().skip(20).limit(10)
// countDocuments - 获取文档数量
db.users.countDocuments({ age: { $gt: 25 } })
// estimatedDocumentCount - 快速估算数量(不使用索引)
db.users.estimatedDocumentCount()
// hint - 强制使用特定索引
db.users.find({ age: { $gt: 25 } }).hint({ age: 1 })
// explain - 查看查询计划
db.users.find({ age: { $gt: 25 } }).explain()
聚合管道查询
聚合管道是 MongoDB 强大的数据处理框架:
// 计算每个类别的平均价格和商品数量
db.products.aggregate([
{ $group: {
_id: "$category",
avgPrice: { $avg: "$price" },
count: { $sum: 1 }
}},
{ $sort: { count: -1 } }
])
// 多阶段管道
db.orders.aggregate([
// 匹配阶段
{ $match: { status: "completed" } },
// 解构数组
{ $unwind: "$items" },
// 分组统计
{ $group: {
_id: "$items.product",
totalQuantity: { $sum: "$items.quantity" },
totalRevenue: { $sum: { $multiply: ["$items.price", "$items.quantity"] } }
}},
// 排序
{ $sort: { totalRevenue: -1 } },
// 限制输出
{ $limit: 10 }
])
查询优化技巧
1. 避免全表扫描
// 不好的做法:正则表达式以 ^ 开头会导致全表扫描
db.users.find({ email: { $regex: "^.*@gmail.com" } })
// 好的做法:使用精确匹配或前缀索引
db.users.find({ email: "[email protected]" })
db.users.find({ email: { $regex: "^user@" } }) // 前缀匹配可以使用索引
2. 使用投影减少返回数据
// 只返回需要的字段
db.orders.find(
{ customerId: 123 },
{ orderId: 1, total: 1, status: 1 }
)
3. 合理使用索引
// 创建复合索引
db.users.createIndex({ age: 1, status: 1 })
// 使用索引进行排序
db.users.find({ age: { $gt: 20 } }).sort({ age: 1 })
4. 分页优化
// 低效的分页(skip 大数据时性能差)
db.orders.find().skip(10000).limit(10)
// 优化:使用游标或基于 ID 的分页
// 记录上一页最后一条的 _id
db.orders.find({ _id: { $gt: lastId } }).limit(10)
常用查询模式
// 1. 登录验证
db.users.findOne({
email: userEmail,
password: hashedPassword
})
// 2. 分页查询
db.articles.find({ published: true })
.sort({ createdAt: -1 })
.skip((page - 1) * pageSize)
.limit(pageSize)
// 3. 模糊搜索
db.products.find({
name: { $regex: keyword, $options: "i" }
})
// 4. 复杂条件查询
db.orders.find({
createdAt: {
$gte: new Date("2024-01-01"),
$lt: new Date("2024-02-01")
},
status: { $in: ["completed", "shipped"] },
"customer.country": "China",
total: { $gte: 100 }
})
小结
本章我们学习了:
- 比较运算符:ne, gte, lte, nin
- 元素查询:type
- 数组查询:elemMatch, $size
- 嵌套文档查询:点符号访问
- 正则表达式:模式匹配
- 地理空间查询:geoWithin
- 投影操作符:字段选择和数组切片
- 查询优化:避免全表扫描,合理使用索引