Search code examples
node.jsmongodbloopback

I need to compare old object with updated object with or without loopback hooks irrespective to the calling api


I need to compare the old object with an updated object with or without loopback hooks or maybe mixins irrespective to the calling API.

==> context.currentInstance is available only via prototype.updateAttributes() which is a little different from others(save/create etc) as when a 'before save' hook triggered by prototype.updateAttributes(), ctx.data will be populated with the data to be changed as you’d expect for an update operation. This is old data ready to be changed and I can compare it with updated data context.data or context.instance. But I am not getting currentInstance in all cases...

I am using "before save" hook of loopback.

Let's say oldObject = data before update. and updatedObject = data after the update.

Based on who is calling the hook function I am receiving either ctx.currentInstance(This is oldObject mean data without changes) + ctx.data(this is updatedObject) or ctx.instance (This is new data)

case-1 If the object is new than no comparison can be done. if(ctx.instance){//true}

case-2 If the object is updated then we can compare data and find the changes that which property is changed. if(ctx.currentInstance && ctx.data){//true}

Now my query is related to case-2. If I want to find the diff between oldObject and updatedObject then I can use lodash or I can use deep-diff (deep-diff is a javascript/node.js module providing utility functions for determining the structural differences between objects and includes some utilities for applying differences across objects.) But This solution fails when any hook say before save hook triggered by anything other than prototype.updateAttributes()

how to get affected attributes/properties of the persisted model? The condition is that the entire object is received in ctx.currentInstance and ctx.data.

Model.observe("before save", async function (ctx) {
  ctx.hookState.olddata = ctx.data; // or ctx.currentInstance or anyhting that gives the old object (just before updation)
});

I can pass ctx.hookState between hooks. So setting the value in any loopback hook will work. Condition is that I am receiving the whole object and not just the updated part. Any loopback hook triggered by any call say save/create/update/delete etc.


Solution

  • In many cases, LoopBack does not have the old object data. If you want to access that data, you have to fetch it from the database in your "before save" hook.

    A mock-up to illustrate my point:

    Model.observe("before save", async function (ctx) {
      if (ctx.currentInstance) {
        ctx.hookState.oldData = [ctx.currentInstance];
      } else if (ctx.isNewInstance) {
        // new instance being created, no old data
        ctx.hookState.oldData = [];
      } else if (ctx.where) {
        // updating multiple records via `updateAll`
        ctx.hookState.oldData = await ctx.Model.find(ctx.where);
      } else {
        ctx.hookState.oldData = [await ctx.Model.findById(ctx.instance.id)];
      }
    });
    

    Please note that "before save" hook is fired by operations updating multiple instances too (e.g. updateAll), you need to handle that case too.