怎么理解 Mongoose 的 pre 中间件,还有 path

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎么理解 Mongoose 的 pre 中间件,还有 path相关的知识,希望对你有一定的参考价值。

就类似connect是一样的.
创建一个执行连
model.pre -> schema.pre -> schema.save
model.pre 可以是具体业务逻辑的一些数据验证,或者原始数据转换.
schema.pre 跟业务无关的数据操作
参考技术A 我曾经翻译过mongoose的文档,并对里面的很多例子进行了实践,虽然不多,一开始也有很多疑惑,但是使用多了,就慢慢知道什么意思了。
8.Middleware中间件
8.1 什么是中间件
中间件是一种控制函数,类似插件,能控制流程中的init、validate、save、remove方法
8.2 中间件的分类
8.2.1 Serial串行
串行使用pre方法,执行下一个方法使用next调用
var schema = new Schema(…);
schema.pre(‘save’,function(next)
//做点什么
next();
);
8.2.2 Parallel并行
并行提供更细粒度的操作
var schema = new Schema(…);
schema.pre(‘save’,function(next,done)
//下一个要执行的中间件并行执行
next();
doAsync(done);
);
8.3 中间件特点
一旦定义了中间件,就会在全部中间件执行完后执行其他操作
使用中间件可以雾化模型,避免异步操作的层层迭代嵌套
8.4 使用范畴
1.复杂的验证
2.删除有主外关联的doc
3.异步默认
4.某个特定动作触发异步任务,例如触发自定义事件和通知
例如,可以用来做自定义错误处理
schema.pre(‘save’,function(next)
var err = new Eerror(‘some err’);
next(err);
);
entity.save(function(err)
console.log(err.message); //some err
);
不知道这看的明不明白,简单的说,中间件就相当于java中的过滤器、拦截器,在执行某个方法前,将其拦截住,也有点像AOP中的前置注入。举个简单的例子,当我们要执行save方法时,我们往往需要对存入的数据进行验证,虽然mongoose提供了safe、strict、schematype、default、validaition验证,但是这些验证都没有提供完善的错误处理或者拦截机制,而利用中间件,可以对错误的数据进行拦截、错误处理、修订等等。比如存入的用户名可能带有代码注入,这时候,通过中间件拦截用户名,给与转义,或进行错误提示、日志记录等。经过中间件的拦截,进入到save方法的数据从理想状态下应该是符合规范且完善的。由此看来,safe、strict、schematype、default、validaition本身就是内部提供的中间件。
关于path,其实也是一种中间件,如同xml的path解析,mongoose是针对mongodb数据库的一种orm模型,mongodb是javascript的json数据存储,有的时候,我们并不希望中间件只针对一个操作,而是针对操作对象的某个属性,那么就能使用path快速定位。这个类似于2.x的get和set方法,只是3.x貌似取消了。
3.x相对于2.x来说,有比较大的改进,学习的时候还得有2.x的一些基础方可。

Mongoose 是不是提供对 pre('save') 中属性先前值的访问?

【中文标题】Mongoose 是不是提供对 pre(\'save\') 中属性先前值的访问?【英文标题】:Does Mongoose provide access to previous value of property in pre('save')?Mongoose 是否提供对 pre('save') 中属性先前值的访问? 【发布时间】:2012-07-11 06:32:50 【问题描述】:

我想在pre('save') 中间件中将属性的新值/传入值与该属性的先前值(当前保存在数据库中的值)进行比较。

Mongoose 是否提供了执行此操作的工具?

【问题讨论】:

【参考方案1】:

我一直在寻找一种解决方案来检测多个字段中任何一个字段的变化。由于看起来您无法为完整架构创建设置器,因此我使用了虚拟属性。我只更新了几个地方的记录,所以对于这种情况,这是一个相当有效的解决方案:

Person.virtual('previousDoc').get(function() 
  return this._previousDoc;
).set(function(value) 
    this._previousDoc = value;
);

假设您的 Person 移动了,您需要更新他的地址:

const person = await Person.findOne(firstName: "John", lastName: "Doe");
person.previousDoc = person.toObject();  // create a deep copy of the previous doc
person.address = "123 Stack Road";
person.city = "Overflow";
person.state = "CA";
person.save();

然后在您的 pre 钩子中,您只需要引用 _previousDoc 的属性,例如:

// fallback to empty object in case you don't always want to check the previous state
const previous = this._previousDoc || ;

if (this.address !== previous.address) 
    // do something


// you could also assign custom properties to _previousDoc that are not in your schema to allow further customization
if (previous.userAddressChange) 

 else if (previous.adminAddressChange) 


【讨论】:

【参考方案2】:

老实说,我尝试了此处发布的解决方案,但我必须创建一个函数,将旧值存储在数组中,保存值,然后查看差异。

// Stores all of the old values of the instance into oldValues
const oldValues = ;
for (let key of Object.keys(input)) 
    if (self[key] != undefined) 
        oldValues[key] = self[key].toString();
    

    // Saves the input values to the instance
    self[key] = input[key];


yield self.save();


for (let key of Object.keys(newValues)) 
    if (oldValues[key] != newValues[key]) 
       // Do what you need to do
    

【讨论】:

这不使用中间件,所以它不是对所提问题的回答【参考方案3】:

接受的答案效果很好。也可以使用另一种语法,将 setter 与 Schema 定义内联:

var Person = new mongoose.Schema(
  name: 
    type: String,
    set: function(name) 
      this._previousName = this.name;
      return name;
    
);

Person.pre('save', function (next) 
  var previousName = this._previousName;
  if(someCondition) 
    ...
  
  next();
);

【讨论】:

【参考方案4】:

Mongoose 允许您配置用于进行比较的自定义设置器。 pre('save') 本身不会给你你需要的东西,但在一起:

schema.path('name').set(function (newVal) 
  var originalVal = this.name;
  if (someThing) 
    this._customState = true;
  
);
schema.pre('save', function (next) 
  if (this._customState) 
    ...
  
  next();
)

【讨论】:

我是否需要这样做才能访问 validators 中的先前值?或者在验证器的情况下有更直接的方法? @aaronheckmann 很抱歉恢复了一个旧线程,我想这不会起作用,如果我们在负载均衡器后面有多个节点服务器。 @aaronheckmann 如果您使用的是 nginx,则有一个值可以指定主机,并将其发送到之前发送的同一服务器..或者您是如何解决的? 我不知道这是否曾经有效,但它不再有效。 this.name 未定义 我知道你写这篇 @JoshWoodcock 已经 4 年了,但我遇到了同样的问题。

以上是关于怎么理解 Mongoose 的 pre 中间件,还有 path的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose pre.save() 异步中间件未按预期工作

Mongoose pre.save() 异步中间件未按预期工作

Mongoose 是不是提供对 pre('save') 中属性先前值的访问?

Mongoose 将 req 对象传递给中间件

Mongoose 子文档预删除中间件未调用

为任何更新查询增加 Mongoose 文档版本的简单方法?