Justin808
Justin808

Reputation: 21512

Mongoose pre findOneAndUpdate hook issues

I'm trying to update counts on a pre hook. The issue is that for some unknown reason the findOneAndUpdate hook doesn't have access to the document, as far as I can tell.

I would like to do this:

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();
});

But for some reason the this in the hook isn't the document, its a Query object which seems about useless.

What am I missing? How do I use a pre hook to update counts on a findOneAndUpdate?

Upvotes: 17

Views: 21452

Answers (7)

Mohamed_Iyed
Mohamed_Iyed

Reputation: 99

mongoose Documentation:

You cannot access the document being updated in pre('updateOne') or pre('findOneAndUpdate') query middleware. If you need to access the document that will be updated, you need to execute an explicit query for the document.

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

Upvotes: 0

t33n
t33n

Reputation: 343

    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))
        }
    })

Upvotes: 0

Harvester Haidar
Harvester Haidar

Reputation: 553

This worked for me

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

Upvotes: 0

CyberMessiah
CyberMessiah

Reputation: 1268

Another solution is to use the official MongoDB documentation on middleware. They explain why "this" does not refer to the document itself. You may try something in that sense:

   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();   
     });

Upvotes: 0

Arausi Daniel
Arausi Daniel

Reputation: 191

I had a similar issue when I used the updateOne method and was also going to use the updateOne pre hook to make intermittent update before saving to the database. couldn't find a way for it to work. I ended up using the findOneAndUpdate pre hook and doing the updateOne in it.

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()
})

Upvotes: 2

user3419036
user3419036

Reputation: 251

You can do smthng like that ->

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

pay attention to _update.$set because in the context "this" will be a query. So you can easily add anything you want!

Upvotes: 17

robertklep
robertklep

Reputation: 203286

The documentation states:

Query middleware differs from document middleware in a subtle but important way: in document middleware, this refers to the document being updated. In query middleware, mongoose doesn't necessarily have a reference to the document being updated, so this refers to the query object rather than the document being updated.

An update action generally updates a document that only exists in the database (it tells the MongoDB server: "find document X and set property X to value Z"), so the full document isn't available to Mongoose and, hence, you can't update the counts (which requires access to at least the arrays whose length you want to determine).

As an aside: why do you need separate *Count properties in your schema anyway? If you want to query for arrays matching a certain size, you can use the $size operator on the arrays directly.

If you really do need the count properties, then for each update, you need to track the number of changes you made to each of the arrays (in terms of the number of items added/deleted) and use the $inc operator to adjust the counts.

Upvotes: 10

Related Questions