Trigger Match Expression for updatedAt

I am setting up a database trigger for insert and update. The function this will trigger will either add createdAt & updatedAt or just update the updatedAt.

ISSUE: This match expression is not filtering out the document.updates where updatedAt was the only field updated. This is causing an endless loop of triggers.

expression ==

{
    "$or": [
        {
            "operationType": "insert"
        },
        {
            "operationType": "update",
            "updateDescription.updatedFields": {
                "$not": {
                    "$eq": [
                        {
                            "updatedAt": "$updateDescription.updatedFields.updatedAt"
                        }
                    ]
                }
            }
        }
    ]
}

Hi, I believe there is a bit of a misunderstanding in how the match expression works (also, I am not convinced it is valid synctax with the $eq). I think this should work, but let me know if it is not what you are looking for:

{
    "$or": [
        {
            "operationType": "insert"
        },
        {
            "operationType": "update",
            "updateDescription.updatedFields.updatedAt": {
               $exists: false,
            }
        }
   ]
}

Hey @Tyler_Kaye , Thank you for the quick reply.

edited: I didn’t have the trigger active, oops. Sorry!

issue:
On insert it triggers the function 3 times. Here is my function:

exports = function(changeEvent) {
  const dbName = changeEvent.ns.db;
  const collectionName = changeEvent.ns.coll;
  const collection = context.services.get('mongodb-atlas').db(dbName).collection(collectionName);
  const currentDate = new Date();

  // Check the operation type
  if (changeEvent.operationType === 'insert') {
    // If it's an insert, set both createdAt and updatedAt
    collection.updateOne(
      { _id: changeEvent.documentKey._id },
      {
        $set: {
          createdAt: currentDate,
          updatedAt: currentDate
        }
      }
    )
    .then(result => {
      console.log(`Inserted document in ${collectionName} collection with createdAt and updatedAt fields.`);
    })
    .catch(error => {
      console.error('Error setting createdAt and updatedAt on insert:', error);
    });
  } else if (changeEvent.operationType === 'update') {
    // If it's an update, only set updatedAt
    collection.updateOne(
      { _id: changeEvent.documentKey._id },
      {
        $set: {
          updatedAt: currentDate
        }
      }
    )
    .then(result => {
      console.log(`Updated document in ${collectionName} collection with updatedAt field.`);
    })
    .catch(error => {
      console.error('Error setting updatedAt on update:', error);
    });
  }

  return;
};

Sorry, having a hard time parsing if you are saying it worked or if it did not work?

Hey @Tyler_Kaye , I just edited my comment above. I didn’t have the trigger enabled lol

The update filter works perfectly! @Tyler_Kaye

1 Like

It happens to us all sometimes :laughing:

Best,
Tyler

I just need help with no having 3 triggers on document insert. @Tyler_Kaye

I have to step out, but I am not sure what you mean?

When I insert a document the triggers gets executed 3 times then it stops. It should only get triggered once @Tyler_Kaye

Can you send a link to your trigger?

@Tyler_Kaye Here you go: App Services

Can you add to the printing to add the document’s _id to the statement (and possibly the updateDescription using EJSON.stringify()). I cant tell if something is going wrong or if there is just something inserting an object and then something updating an object

@Tyler_Kaye Does this work? I couldn’t use EJSON , I got an error “Cannot find module ‘mongodb-extended-json’”. This is the code for the function I wrote with EJSON

exports = function(changeEvent) {
  const dbName = changeEvent.ns.db;
  const collectionName = changeEvent.ns.coll;
  const collection = context.services.get('mongodb-atlas').db(dbName).collection(collectionName);
  const currentDate = new Date();
  const documentId = changeEvent.documentKey._id; // Get the _id of the document

  // Import the EJSON module
  const EJSON = require('mongodb-extended-json');

  // Check the operation type
  if (changeEvent.operationType === 'insert') {
    const updateDescription = {
      $set: {
        createdAt: currentDate,
        updatedAt: currentDate
      }
    };

    // If it's an insert, set both createdAt and updatedAt
    collection.updateOne({ _id: documentId }, updateDescription)
    .then(result => {
      console.log(`Inserted document with _id: ${documentId} in ${collectionName} collection with createdAt and updatedAt fields. Update Description: ${EJSON.stringify(updateDescription)}`);
    })
    .catch(error => {
      console.error('Error setting createdAt and updatedAt on insert:', error);
    });
  } else if (changeEvent.operationType === 'update') {
    const updateDescription = {
      $set: {
        updatedAt: currentDate
      }
    };

    // If it's an update, only set updatedAt
    collection.updateOne({ _id: documentId }, updateDescription)
    .then(result => {
      console.log(`Updated document with _id: ${documentId} in ${collectionName} collection with updatedAt field. Update Description: ${EJSON.stringify(updateDescription)}`);
    })
    .catch(error => {
      console.error('Error setting updatedAt on update:', error);
    });
  }

  return;
};

Here is the code that actually worked

exports = function(changeEvent) {
  const dbName = changeEvent.ns.db;
  const collectionName = changeEvent.ns.coll;
  const collection = context.services.get('mongodb-atlas').db(dbName).collection(collectionName);
  const currentDate = new Date();
  const documentId = changeEvent.documentKey._id; // Get the _id of the document

  // Check the operation type
  if (changeEvent.operationType === 'insert') {
    const updateDescription = {
      $set: {
        createdAt: currentDate,
        updatedAt: currentDate
      }
    };

    // If it's an insert, set both createdAt and updatedAt
    collection.updateOne({ _id: documentId }, updateDescription)
    .then(result => {
      console.log(`Inserted document with _id: ${documentId} in ${collectionName} collection with createdAt and updatedAt fields. Update Description: ${JSON.stringify(updateDescription)}`);
    })
    .catch(error => {
      console.error('Error setting createdAt and updatedAt on insert:', error);
    });
  } else if (changeEvent.operationType === 'update') {
    const updateDescription = {
      $set: {
        updatedAt: currentDate
      }
    };

    // If it's an update, only set updatedAt
    collection.updateOne({ _id: documentId }, updateDescription)
    .then(result => {
      console.log(`Updated document with _id: ${documentId} in ${collectionName} collection with updatedAt field. Update Description: ${JSON.stringify(updateDescription)}`);
    })
    .catch(error => {
      console.error('Error setting updatedAt on update:', error);
    });
  }

  return;
};

Hey, looking at your code / logs everything seems good. It seems like this is what is happening:

  1. You are making a change to the jobs collection
  2. This has a trigger on it autoSelectedBidAt which updates the selectedBidAt field in the document
  3. The trigger called autoSetTimestamps responds to the initial event by updating the updatedAt field
  4. The trigger called autoSetTimestamps responds to the change made by the other trigger (step 2) by updating the updatedAt field

Let me know if this makes sense, but I think everything is working as expected. Perhaps you were not expecting the result of one trigger to send an event for another trigger, but that is how the system works (each trigger is isolated from one another, and a trigger can even cause itself to trigger and recurse if not careful).

Best,
Tyler

Hmm it seems like the issue I was having has gone away. THANK YOU SO MUCH for your help!!! @Tyler_Kaye

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.