关于Mongoose中exec方法arguments、getOptions及Redis缓存逻辑的疑问
1. 重写exec方法时的arguments来源
在JavaScript中,arguments是函数内部的内置类数组对象,它包含当前函数被调用时传入的所有参数。
在重写Mongoose Query的exec方法场景下,当业务代码调用Model.find(...).exec()或Model.find(...).exec(callback)时,这里传入的参数(比如可选的回调函数)就会成为重写后exec方法里的arguments:
- 若调用
await query.exec(),arguments为空; - 若调用
query.exec((err, docs) => { ... }),arguments里就包含这个回调函数。
2. this.getOptions()的具体含义
这里的this指向Mongoose的Query实例(即调用Model.find()、Model.findOne()等方法返回的查询对象)。getOptions()是Mongoose Query原型上的方法,作用是获取当前查询的所有配置选项。
这些选项包含两类:
- Mongoose内置查询配置:比如
lean(是否返回普通JS对象而非Mongoose文档实例)、sort、limit、skip等; - 自定义扩展选项:如果在查询时通过
query.setOptions({ cache: true, cacheTtl: 3600 })添加了缓存相关配置,getOptions()也能拿到这些字段,用来控制缓存开关和过期时间。
3. Redis缓存完整实现逻辑
这类实现核心是重写Mongoose Query的exec方法,通过拦截查询请求实现缓存逻辑,完整流程如下:
核心步骤
判断缓存开关
先调用this.getOptions()获取查询配置,检查是否存在cache: true这类开启缓存的标记,如果没有直接执行原exec方法查询MongoDB。生成唯一缓存Key
为保证缓存的准确性,需根据查询核心信息生成唯一Key,通常包含:- 当前查询对应的集合名称(
this.model.collection.name); - 查询条件(
JSON.stringify(this.getQuery())); - 查询选项(
JSON.stringify(this.getOptions()));
将这些信息拼接后做哈希处理(比如crypto.createHash('md5').update(...).digest('hex')),得到短且唯一的Key。
- 当前查询对应的集合名称(
读取Redis缓存
用生成的Key查询Redis,若存在缓存值,将JSON字符串解析为JS对象后返回:- 若原调用传入了回调函数,通过
arguments[0](null, cachedData)返回; - 若用
await调用,直接返回cachedData。
- 若原调用传入了回调函数,通过
查询MongoDB并写入缓存
如果Redis无缓存,调用原exec方法(比如const originalExec = Query.prototype.exec; originalExec.apply(this, arguments))查询MongoDB。拿到结果后,将其序列化为JSON字符串存入Redis,并设置过期时间(从getOptions()中取cacheTtl,比如3600秒),最后把MongoDB的结果返回给调用者,保持和原exec方法一致的返回逻辑。
关键细节
- 兼容回调和Promise两种调用方式:Mongoose的
exec既支持传入回调,也支持返回Promise; - 设置缓存过期:必须配置合理的过期时间,避免缓存数据与MongoDB长期不一致;
- 异常降级:若Redis查询出错,直接降级到查询MongoDB,不影响主业务流程。
内容的提问来源于stack exchange,提问作者user17885189

