比较操作符:$in 与 $nin
语义合并说明:整合三篇博客的语法定义、应用场景与边界问题,保留最完整示例。
// 语法模板
{ <字段名>: { $in: [<值1>, <值2>] } } // 匹配字段值在数组内的文档
{ <字段名>: { $nin: [<值1>, <值2>] } } // 匹配字段值不在数组内的文档
案例:
$in基础查询:db.accounts.find({ name: { $in: ["Alice", "Charlie"] } }) // 返回 name 为 Alice 或 Charlie 的文档$nin的边界问题:db.accounts.find({ name: { $nin: ["Alice", "Charlie"] } }) // 返回非 Alice/Charlie 的文档,包括无 name 字段的文档- 字段缺失处理:
db.accounts.find({ "id.type": { $ne: "checking", $exists: true } // 排除字段缺失的文档 })
要点:
$in优化多值匹配,$nin会包含字段缺失文档- 结合
$exists避免意外匹配空字段
逻辑操作符:$not、$and、$or、$nor
数据整合:合并操作符语法表与简写优化策略,补充性能建议
| 操作符 | 语法示例 | 特性与优化 |
|---|---|---|
$not | { balance: { $not: { $lt: 500 } } } | 返回不满足条件的文档(含字段缺失) |
$and | { balance: { $gt: 100, $lt: 500 } } | 同字段多条件可简写,默认逗号分隔等效 $and |
$or | { $or: [{balance:{$lt:100}}, {balance:{$gt:500}}] } | 等值查询优先用 $in(如 {name:{$in:["Alice"]}}) |
$nor | { $nor: [{name:"Alice"}, {balance:{$lt:100}}] } | 所有条件均不成立时匹配(含字段缺失) |
案例:
// $nor 复杂条件:非 Alice/Charlie 且余额 ≥100
db.accounts.find({
$nor: [
{ name: "Alice" },
{ name: "Charlie" },
{ balance: { $lt: 100 } }
]
})
要点:
$and简写提升代码简洁性,$or用$in优化性能$not和$nor需谨慎处理字段缺失问题
字段操作符:$exists 与 $type
语法与功能:
| 操作符 | 语法 | 功能说明 |
|---|---|---|
$exists | { <字段名>: { $exists: <布尔值> } } | true:匹配包含该字段的文档;false:匹配不包含该字段的文档。 |
$type | { <字段名>: { $type: <数据类型名> } } | 匹配字段值为指定 BSON 类型的文档(支持类型名或序号)。 |
数据整合:补充 BSON 类型表(合并博客2数据),增强类型查询说明。
// 语法模板
{ <字段名>: { $exists: true } } // 匹配包含字段的文档
{ <字段名>: { $type: <数据类型> } } // 匹配指定BSON类型的文档
案例:
- 字段存在性检查:
db.accounts.find({ "id.type": { $exists: true } }) // 仅返回含 id.type 字段的文档 - 多类型匹配:
db.accounts.find({ _id: { $type: ["string", "object"] } }) // _id 为字符串或对象类型
BSON 类型对照表:
| 类型名称 | BSON 编号 | 用例说明 |
|---|---|---|
| String | 2 | { $type: 2 } |
| Object | 3 | 嵌套文档 |
| Array | 4 | 数组字段 |
| Null | 10 | 匹配 null 值 |
| Boolean | 8 | 布尔类型 |
要点:
$exists解决字段缺失导致的误匹配$type支持多类型筛选,兼容编号或名称
数组操作符:$all 与 $elemMatch
语法差异:
| 操作符 | 语法 | 匹配条件 |
|---|---|---|
$all | { <数组字段>: { $all: [<值1>, <值2>] } } | 数组字段必须包含查询值列表中的所有元素。 |
$elemMatch | { <数组字段>: { $elemMatch: {<条件>} } } | 数组字段中至少有一个元素满足指定条件。 |
去重与增强:合并嵌套数组示例,补充组合查询场景
// 语法模板
{ <数组字段>: { $all: [<值1>, <值2>] } } // 数组必须包含所有值
{ <数组字段>: { $elemMatch: { <条件> } } } // 至少一个元素满足条件
案例:
$all精确匹配:db.accounts.find({ contact: { $all: ["China", "Beijing"] } }) // 需同时包含 China 和 Beijing$elemMatch范围查询:db.accounts.find({ contact: { $elemMatch: { $gt: 1000, $lt: 2000 } } }) // 存在元素值在 1000-2000 之间- 组合查询:
db.accounts.find({ contact: { $all: [ { $elemMatch: { $gt: 1000, $lt: 2000 } }, // 要求两个独立元素满足不同条件 { $elemMatch: { $gt: 3000, $lt: 4000 } } ] } })
要点:
$all匹配完整元素,$elemMatch匹配元素属性- 组合使用可处理多条件数组查询
正则表达式操作符 $regex
语法整合:统一两种语法场景,强调 $in 联用限制
// 常规语法
{ name: { $regex: /lie/, $options: "i" } } // 不区分大小写匹配 "lie"
// $in 联用语法(必须显式声明)
{ name: { $in: [/^C/i, /^J/i] } } // 匹配以 C 或 J 开头的名称
案例:
db.accounts.find({ name: { $regex: "LIE", $options: "i" } }) // 忽略大小写查询
要点:
$in中必须使用{ $regex: /pattern/ }语法$options: "i"实现不区分大小写匹配
游标管理
核心机制:
find()返回游标,默认返回前20个文档- 闲置10分钟后自动关闭,
noCursorTimeout()可禁用
游标方法速查表:
| 方法 | 功能描述 | 示例 |
|---|---|---|
cursor.hasNext() | 检查是否有更多文档 | while (cursor.hasNext()) |
cursor.next() | 获取下一文档 | printjson(cursor.next()) |
cursor.limit(N) | 限制返回文档数(N=0 无限制) | cursor.limit(5) |
cursor.skip(N) | 跳过前 N 个文档 | cursor.skip(10) |
cursor.noTimeout() | 禁用自动关闭 | cursor.noTimeout() |
案例:
// 分页查询(跳过第1条,限制返回1条)
const cursor = db.accounts.find().skip(1).limit(1);
cursor.forEach(printjson); // 遍历结果
// 禁用超时(需手动关闭)
const longCursor = db.accounts.find().noCursorTimeout();
longCursor.close(); // 避免内存泄漏
hasNext() + next():遍历游标结果集:
const cursor = db.accounts.find({ "name": "George" });
while (cursor.hasNext()) {
printjson(cursor.next());
}
forEach():简化遍历:
db.accounts.find({ "name": "George" }).forEach(printjson);
limit() + skip():分页控制:
// 返回第二篇文档(跳过第一篇)
db.accounts.find().skip(1).limit(1);
要点:
limit(0)取消返回数量限制- 长期游标需手动关闭防止资源泄露
游标超时管理
- 禁用超时:
const cursor = db.accounts.find().noCursorTimeout(); - 手动关闭:
cursor.close(); // 避免内存泄漏
综合应用示例
1 ) NestJS + MongoDB
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { AccountDocument } from './account.schema';
@Injectable()
export class AccountService {
constructor(
@InjectModel('Account') private accountModel: Model<AccountDocument>
) {}
// $in 查询(多值匹配)
async findUsersByName(names: string[]): Promise<AccountDocument[]> {
return this.accountModel.find({ name: { $in: names } }).exec();
}
// $elemMatch 组合查询(数组范围匹配)
async findComplexContacts(): Promise<AccountDocument[]> {
return this.accountModel.find({
contact: {
$all: [
{ $elemMatch: { $gt: 1000, $lt: 2000 } },
{ $elemMatch: { $gt: 3000, $lt: 4000 } }
]
}
}).exec();
}
// 正则表达式查询
async regexSearch(pattern: string): Promise<AccountDocument[]> {
return this.accountModel.find({
name: { $regex: pattern, $options: 'i' }
}).exec();
}
// 游标分页控制
async paginate(skip: number, limit: number): Promise<AccountDocument[]> {
return this.accountModel.find().skip(skip).limit(limit).exec();
}
}
2 ) SQL 等效查询参考
-- $in 等效
SELECT * FROM accounts WHERE name IN ('Alice', 'Charlie');
-- $and 等效
SELECT * FROM accounts WHERE balance > 100 AND name > 'Fred';
关键总结与最佳实践
1 ) 操作符特性精要:
- 边界处理:
$nin、$not、$nor默认包含字段缺失文档,必须搭配$exists精确过滤 - 性能优化:同字段多条件用简写(如
{balance: {$gt:100, $lt:500}}),多值匹配优先$in而非$or- 同字段多条件时优先用简写语法(如
{ field: { $gt: X, $lt: Y } }) - 多值匹配时
$in比$or更高效
- 同字段多条件时优先用简写语法(如
- 正则限制:
$in中正则表达式需显式声明{ $regex: /pattern/ }- 与
$in联用时需改用数组语法({ field: { $in: [/regex1/, /regex2/] } })
- 与
2 ) 游标管理准则:
- 大数据集使用
skip().limit()分页,避免全量加载limit(0)表示返回所有匹配文档
noCursorTimeout()需手动关闭游标,防止内存泄漏
3 ) 工程化建议:
- 嵌套数组查询优先组合
$all和$elemMatch确保条件独立性 - BSON 类型查询时使用名称(如
"string")而非编号,提升代码可读性
4 )关键知识点补充
BSON 类型表
| 类型 | 编号 | 说明 |
|---|---|---|
| Double | 1 | 双精度浮点数 |
| String | 2 | UTF-8 字符串 |
| Object | 3 | 嵌套文档 |
| Array | 4 | 数组 |
| Binary | 5 | 二进制数据 |
| ObjectId | 7 | 文档主键 |
| Boolean | 8 | 布尔值 |
| Null | 10 | 空值 |
游标最佳实践
- 使用
await cursor.close()避免内存泄漏 - 分页时组合
skip().limit()替代全量查询 - 大数据集优先用
$match聚合阶段过滤文档
515

被折叠的 条评论
为什么被折叠?



