Mongoose的findOneAndUpdate预钩子问题

17

我试图在预先钩子上更新计数。问题在于由于某种未知的原因,findOneAndUpdate 钩子没有访问文档的权限,据我所知。

我想要做到这一点:

source.pre('findOneAndUpdate', function (next) {
  console.log('------------->>>>>> findOneAndUpdate: ');
  this.objects = this.objects || [];
  this.people = this.people || [];
  this.events = this.events || [];

  this.objectCount = this.objects.length;
  this.peopleCount = this.people.length;
  this.eventCount = this.events.length;

  next();
});

但出于某种原因,钩子中的this不是文档,而是一个看起来毫无用处的查询对象。

我错过了什么?如何使用预处理钩子来更新findOneAndUpdate上的计数?

7个回答

17

你可以做类似这样的事情 ->

source.pre('findOneAndUpdate', function (next) {
    console.log('------------->>>>>> findOneAndUpdate: ');
    this._update.$set.objects = [];
    this._update.$set.people = [];
    this._update.$set.events =  [];
    next();
});

注意_update.$set,因为在这个上下文中,“this”将是一个查询。所以你可以轻松地添加任何你想要的东西!


1
我在其他问题中看到了类似的答案,但这实际上是受支持的还是只是一个hack? - T.Okahara
1
@T.Okahara 看起来直接访问 _update 可能有点 hacky,但在当前的 API 中,有 Query.prototype.getUpdate 和 Query.prototype.setUpdate 这两个方法,看起来是一种支持的实现方式。 - Mr5o1

10

文档中指出:

查询中间件与文档中间件有微妙但重要的区别:在文档中间件中,this指的是正在更新的文档。在查询中间件中,Mongoose并不一定拥有对正在更新的文档的引用,因此this指的是查询对象而不是正在更新的文档。

更新操作通常只更新数据库中存在的文档(它告诉MongoDB服务器:"查找文档X并将属性X设置为值Z"),因此完整的文档对于Mongoose来说是不可用的,因此您不能更新计数(这需要访问至少要确定其长度的数组)。

顺便说一下:为什么您需要模式中单独的*Count属性? 如果您想查询匹配特定大小的数组,可以直接在数组上使用$size运算符。

如果您确实需要计数属性,则对于每个更新,您需要跟踪您对每个数组所做的更改数量(以添加/删除的项数表示),并使用$inc运算符来调整计数。


计数器的存在是为了让我可以使用find返回完整的项目列表,并排除数组,因为当我返回完整列表时不需要这些细节。那么如何自动更新计数器呢?对我来说,如果没有访问文档的预钩子是毫无意义的。 - Justin808
@Justin808 如果你想返回数组大小,请考虑使用 汇总。如果你想要更新计数,你需要先从数据库中检索完整的文档,然后更新值,更新计数(或者像pre('save')这样使用 文档 中间件),然后将其保存回数据库。你不能在更新操作中完成它,原因已经解释过了。 - robertklep
我想我不太理解$size的用法。目前我正在执行SourceModel.find({treeId: treeId}, '-objects -people -events', callback)操作。我该在哪里添加$size来返回objectspeopleevents数组的大小呢? - Justin808
@Justin808 这将需要一个单独的(聚合)查询。请参见我之前评论中的链接。如果您的数组不是很大,我会认真考虑将它们拉进来以确定它们的大小。根据具体情况,您可能可以通过更新操作使其正常工作;请参见我的编辑。 - robertklep

2

当我使用updateOne方法并想在保存到数据库之前进行间歇性更新时,遇到了类似的问题。我无法找到使其正常工作的方法。最终,我使用了findOneAndUpdate预钩子,并在其中执行了updateOne。

schema.pre('findOneAndUpdate', async function(next){ 
     const schema = this;
     const { newUpdate } = schema.getUpdate();
     const queryConditions = schema._condition

     if(newUpdate){
       //some mutation magic
       await schema.updateOne(queryConditions, {newUpdate:"modified data"}); 
       next()
     }
     next()
})

0

这对我有用

SCHEMA.pre('findOneAndUpdate', function(next){
   this._update.yourNestedElement
   next();
});

0

mongoose 文档:

在 pre('updateOne') 或 pre('findOneAndUpdate') 查询中间件中不能访问正在更新的文档。如果需要访问将要更新的文档,您需要执行显式查询。

schema.pre('findOneAndUpdate', async function() {
  const docToUpdate = await this.model.findOne(this.getQuery());
  console.log(docToUpdate); // The document that `findOneAndUpdate()` will modify
});

0
    schema.pre(['findOneAndUpdate'], async function(next) {
        try {
            const type = this.get('type')
            const query = this.getQuery()

            const doc =  await this.findOne(query)

            if (type) {
                this.set('type', doc.type)
            }

            next()
        } catch (e) {
            next(new BaseError(e))
        }
    })

0

另一种解决方案是使用官方 MongoDB 中间件文档。他们解释了为什么 "this" 不是指代文件本身。你可以尝试类似这样的方法:

   source.pre('findOneAndUpdate', async function(next) {
     const docToUpdate = await this.model.findOne(this.getFilter());
        //modify the appropriate objects
     docToUpdate.save(function(err) {
       if(!err) {
         console.log("Document Updated");
        }
     });
    console.log(docToUpdate); 
     // The document that `findOneAndUpdate()` will modify
       next();   
     });

我只想提一下,this.getQuery() 已经过时了,应该使用 this.getFilter()。 - Marco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接