Appearance
查询进阶
本章将介绍MongoDB的高级查询技巧,包括条件操作符、数组查询、正则表达式、嵌套文档查询等内容。
条件操作符
比较操作符
javascript
// 比较操作符
// 准备测试数据
db.products.insertMany([
{ name: "iPhone", price: 6999, stock: 100 },
{ name: "iPad", price: 4999, stock: 50 },
{ name: "MacBook", price: 12999, stock: 30 },
{ name: "AirPods", price: 1299, stock: 200 },
]);
// $eq - 等于
db.products.find({ price: { $eq: 6999 } });
// 等同于
db.products.find({ price: 6999 });
// $ne - 不等于
db.products.find({ price: { $ne: 6999 } });
// $gt - 大于
db.products.find({ price: { $gt: 5000 } });
// $gte - 大于等于
db.products.find({ price: { $gte: 5000 } });
// $lt - 小于
db.products.find({ price: { $lt: 5000 } });
// $lte - 小于等于
db.products.find({ price: { $lte: 5000 } });
// 组合使用
db.products.find({
price: { $gte: 1000, $lte: 7000 }, // 价格在1000到7000之间
});
// $in - 在列表中
db.products.find({ price: { $in: [1299, 4999] } });
// $nin - 不在列表中
db.products.find({ price: { $nin: [1299, 4999] } });逻辑操作符
javascript
// 逻辑操作符
// $and - 与操作(所有条件都满足)
db.products.find({
$and: [{ price: { $gte: 1000 } }, { stock: { $gte: 50 } }],
});
// 简写形式(推荐)
db.products.find({
price: { $gte: 1000 },
stock: { $gte: 50 },
});
// $or - 或操作(满足任一条件)
db.products.find({
$or: [{ price: { $lt: 2000 } }, { stock: { $gt: 100 } }],
});
// $not - 非操作
db.products.find({
price: { $not: { $gt: 5000 } }, // 价格不大于5000
});
// $nor - 既不...也不...
db.products.find({
$nor: [{ price: { $lt: 2000 } }, { stock: { $lt: 50 } }],
});
// 价格不小于2000 且 库存不小于50
// 复杂逻辑组合
db.products.find({
$and: [
{
$or: [{ category: "电子" }, { category: "数码" }],
},
{ price: { $gte: 1000 } },
],
});元素操作符
javascript
// 元素操作符
// $exists - 检查字段是否存在
db.users.find({ email: { $exists: true } }); // 有email字段
db.users.find({ email: { $exists: false } }); // 没有email字段
// $type - 检查字段类型
db.users.find({ age: { $type: "int" } }); // 类型为int
db.users.find({ age: { $type: 16 } }); // 类型码16为int
// 类型名称和编号对应表:
// "double": 1, "string": 2, "object": 3, "array": 4
// "binData": 5, "objectId": 7, "bool": 8, "date": 9
// "null": 10, "regex": 11, "int": 16, "timestamp": 17
// "long": 18, "decimal": 19数组查询
基本数组查询
javascript
// 数组查询基础
// 准备测试数据
db.products.insertMany([
{ name: "产品A", tags: ["电子", "热销", "新品"], price: 999 },
{ name: "产品B", tags: ["服装", "热销"], price: 199 },
{ name: "产品C", tags: ["电子", "新品"], price: 599 },
{ name: "产品D", tags: ["食品"], price: 29 },
]);
// 精确匹配整个数组
db.products.find({ tags: ["电子", "热销", "新品"] });
// 只匹配tags数组完全等于["电子", "热销", "新品"]的文档
// 匹配数组中包含某个元素
db.products.find({ tags: "电子" });
// 匹配tags数组中包含"电子"的文档
// $in - 匹配数组中包含多个元素之一
db.products.find({ tags: { $in: ["电子", "服装"] } });
// $all - 匹配数组中包含所有指定元素
db.products.find({ tags: { $all: ["电子", "新品"] } });数组元素操作符
javascript
// $elemMatch - 匹配数组中满足所有条件的元素
// 准备测试数据
db.students.insertMany([
{
name: "张三",
scores: [
{ subject: "数学", score: 90 },
{ subject: "语文", score: 85 },
],
},
{
name: "李四",
scores: [
{ subject: "数学", score: 70 },
{ subject: "语文", score: 95 },
],
},
]);
// 错误写法:分别匹配字段
db.students.find({
"scores.subject": "数学",
"scores.score": { $gt: 80 },
});
// 这会匹配到张三和李四,因为条件可以匹配不同的数组元素
// 正确写法:使用$elemMatch
db.students.find({
scores: {
$elemMatch: {
subject: "数学",
score: { $gt: 80 },
},
},
});
// 只匹配数学成绩大于80的学生(张三)
// $size - 匹配数组长度
db.products.find({ tags: { $size: 3 } });
// 匹配tags数组长度为3的文档数组索引查询
javascript
// 使用索引查询数组元素
// 查询数组第一个元素
db.products.find({ "tags.0": "电子" });
// 查询数组第二个元素
db.products.find({ "tags.1": "热销" });
// $slice - 返回数组的部分元素
db.products.find(
{ name: "产品A" },
{ tags: { $slice: 2 } }, // 只返回前2个元素
);
db.products.find(
{ name: "产品A" },
{ tags: { $slice: -2 } }, // 只返回最后2个元素
);
db.products.find(
{ name: "产品A" },
{ tags: { $slice: [1, 2] } }, // 跳过第1个,返回2个元素
);
// $elemMatch在投影中使用
db.students.find(
{ name: "张三" },
{
scores: {
$elemMatch: { score: { $gte: 90 } },
},
},
);
// 只返回分数大于等于90的成绩记录正则表达式查询
javascript
// 正则表达式查询
// 准备测试数据
db.users.insertMany([
{ name: "张三", email: "zhangsan@example.com" },
{ name: "张四", email: "zhangsi@test.com" },
{ name: "李四", email: "lisi@example.com" },
{ name: "王五", email: "wangwu@example.cn" },
]);
// 使用正则表达式对象
db.users.find({ name: /^张/ }); // 姓名以"张"开头
db.users.find({ name: /三$/ }); // 姓名以"三"结尾
db.users.find({ name: /张|李/ }); // 姓名包含"张"或"李"
// 使用$regex操作符
db.users.find({ name: { $regex: /^张/ } });
// 忽略大小写
db.users.find({ email: { $regex: /EXAMPLE/i } });
// 使用$regex和$options
db.users.find({
email: {
$regex: "example",
$options: "i", // i: 忽略大小写
},
});
// 正则与其他条件组合
db.users.find({
name: { $regex: /^张/ },
age: { $gte: 20 },
});嵌套文档查询
javascript
// 嵌套文档查询
// 准备测试数据
db.users.insertMany([
{
name: "张三",
address: {
city: "北京",
district: "朝阳区",
street: "朝阳路",
},
},
{
name: "李四",
address: {
city: "上海",
district: "浦东新区",
street: "浦东大道",
},
},
]);
// 使用点表示法查询嵌套字段
db.users.find({ "address.city": "北京" });
db.users.find({ "address.district": "朝阳区" });
// 嵌套字段条件组合
db.users.find({
"address.city": "北京",
"address.district": "朝阳区",
});
// 精确匹配整个嵌套文档(字段顺序必须一致)
db.users.find({
address: {
city: "北京",
district: "朝阳区",
street: "朝阳路",
},
});查询结果处理
排序与分页
javascript
// 排序
db.users.find().sort({ age: 1 }); // 按age升序
db.users.find().sort({ age: -1 }); // 按age降序
db.users.find().sort({ city: 1, age: -1 }); // 多字段排序
// 分页查询
var page = 1;
var pageSize = 10;
db.users
.find()
.skip((page - 1) * pageSize)
.limit(pageSize)
.sort({ createdAt: -1 });
// 深分页优化(使用范围查询代替skip)
var lastId = ObjectId("...");
db.users.find({ _id: { $gt: lastId } }).limit(10);投影
javascript
// 投影 - 指定返回字段
// 只返回指定字段
db.users.find({}, { name: 1, email: 1 });
// 排除指定字段
db.users.find({}, { password: 0, salt: 0 });
// 混合使用(不能同时使用包含和排除,除了_id)
db.users.find({}, { name: 1, email: 1, _id: 0 });
// 嵌套字段投影
db.users.find({}, { "address.city": 1 });
// 数组元素投影
db.users.find({}, { tags: { $slice: 3 } });游标操作
javascript
// 游标操作
var cursor = db.users.find({ age: { $gte: 20 } });
// 检查是否有下一条
cursor.hasNext();
// 获取下一条
cursor.next();
// 遍历游标
cursor.forEach(function (doc) {
print(doc.name);
});
// 转换为数组
var docs = cursor.toArray();
// 获取查询计划
cursor.explain("executionStats");本章小结
本章介绍了MongoDB的高级查询技巧:
- 条件操作符:掌握比较、逻辑、元素操作符的使用
- 数组查询:学会数组元素匹配、$elemMatch、$size等操作
- 正则表达式:掌握正则表达式查询方法
- 嵌套文档查询:学会使用点表示法查询嵌套字段
- 查询结果处理:掌握排序、分页、投影等操作
- 游标操作:学会使用游标遍历查询结果
下一章,我们将学习索引,了解如何提高查询性能。
