'MongooseError: Query was already executed: in pre hook ['updateOne', 'findOneAndUpdate', 'update', 'updateMany'] using this.set in it

i'm using mongoose on v6.1.5.

i'm trying to create a mongoose hook on update function in order to track element that changed during the update, and make it has a public plugin.

schema.pre(['updateOne', 'findOneAndUpdate', 'update', 'updateMany'], async function () {
    const updatedFields = this.getUpdate()

    if (isNull(updatedFields)) {
      return
    }

    const trackedFields = reduce(toPairs(updatedFields), (acc, [key, value]: [string, any]): any => {
      if (includes(fieldsToTrack, key)) {
        return [...acc, {
          field: `${key}`,
          changedTo: value,
          at: Date.now()
        }]
      }

      return acc
    }, [])

    const docUpdated = await this.model.findOne(this.getQuery())
    const oldTrackedFields = docUpdated.get(`${name}`)

    this.set({
      [name]: [...oldTrackedFields, ...trackedFields]
    }).catch((err) => console.log(err))
  })

when running the code in my test :, i'm getting a strange error in the console which doesn't block the logic, but throw this:

it.only('should add modified field in __tokens if the field is tracked', async () => {
        const schema = new Schema({
          name: String,
          price: Number,
          toto: String
        })

        schema.plugin(mongooseTracker, {
          fieldsToTrack: ['name', 'toto'],
          name: '__tokens'
        })

        const Model = mongoose.model('test7', schema)

        await Model.create({ price: 10, name: 'nom', toto: 'c est moi' })
        await Model.findOneAndUpdate({ price: 10 }, { name: 'nouveauNom' })

        const doc = await Model.findOne({ price: 10 })

        expect(doc).toEqual(
          expect.objectContaining({
            '__tokens': expect.arrayContaining([
              expect.objectContaining({
                changedTo: 'nouveauNom',
                field: 'name'
              })
            ])
          })
        )
      })
console.log
    MongooseError: Query was already executed: test7.findOneAndUpdate({ price: 10 }, {
      '$set': {
        __to...
        at model.Query._wrappedThunk [as _findOneAndUpdate] (/home/barder/xxx/mongoose-tracker/node_modules/mongoose/lib/helpers/query/wrapThunk.js:21:19)
        at /home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:281:16
        at _next (/home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:103:16)
        at /home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:513:38
        at processTicksAndRejections (node:internal/process/task_queues:78:11) {
      originalStack: 'Error: \n' +
        '    at model.Query._wrappedThunk [as _findOneAndUpdate] (/home/barder/xxxx/mongoose-tracker/node_modules/mongoose/lib/helpers/query/wrapThunk.js:25:28)\n' +
        '    at /home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:281:16\n' +
        '    at _next (/home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:103:16)\n' +
        '    at /home/barder/xxx/mongoose-tracker/node_modules/kareem/index.js:513:38\n' +
        '    at processTicksAndRejections (node:internal/process/task_queues:78:11)'
    }

i'm bugging it and found out, it seems that this.set is a duplicate query but i don't understand why ? is this because update function use $set so it make a duplicate call ? And Is using .clone is a good solution ?

Please feel free to give me/us feedback, I/We would greatly appreciate it.



Solution 1:[1]

You cannot run query twice in mangoose, but can clone and run it. Try to change

const doc = await Model.findOne({ price: 10 })

to

const doc = await Model.findOne({ price: 10 }).clone()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1