Realm Triggers Treats and Tricks - Document-Based Trigger Scheduling
Rate this article
In this blog series, we are trying to inspire you with some reactive Realm trigger use cases. We hope these will help you bring your application pipelines to the next level.
Essentially, triggers are components in our Atlas projects/Realm apps that allow a user to define a custom function to be invoked on a specific event.
- Database triggers: We have triggers that can be scheduled based on database events—like
deletes
,inserts
,updates
, andreplaces
—called database triggers. - Authentication triggers: These triggers are only relevant for Realm authentication. They are triggered by one of the Realm auth providers' authentication events and can be configured only via a Realm application.
For this blog post, I would like to focus on trigger scheduling patterns.
Let me present a use case and we will see how the discussed mechanics might help us in this scenario. Consider a meeting management application that schedules meetings and as part of its functionality needs to notify a user 10 minutes before the meeting.
How would we create a trigger that will be fired 10 minutes before a timestamp which is only known by the "meeting" document?
First, let's have a look at the meetings collection documents example:
1 { 2 _id : ObjectId("5ca4bbcea2dd94ee58162aa7"), 3 event : "Mooz Meeting", 4 eventDate : ISODate("2021-03-20:14:00:00Z"), 5 meetingUrl : "https://mooz.meeting.com/5ca4bbcea2dd94ee58162aa7", 6 invites : ["jon.doe@myemail.com", "doe.jonas@myemail.com"] 7 }
I wanted to share an interesting solution based on triggers, and throughout this article, we will use a meeting notification example to explain the discussed approach.
First, verify that you have an Atlas project with owner privileges to create triggers.
If you haven't yet set up your free cluster on MongoDB Atlas, now is a great time to do so. You have all the instructions in this blog post.
I will use the event example as a source document initiating the flow
with an insert to a
meetings
collection:1 { 2 _id : ObjectId("5ca4bbcea2dd94ee58162aa7"), 3 event : "Mooz Meeting", 4 eventDate : ISODate("2021-03-20:11:00:00Z"), 5 meetingUrl : "https://mooz.meeting.com/5ca4bbcea2dd94ee58162aa7" 6 invites : ["jon.doe@example.com"], 7 phone : "+123456789" 8 }
Once we insert this document into the
meetings
collection, it will create the following record in a helper collection called notifications
using an insert trigger:1 { 2 _id : ObjectId("5ca4bbcea2dd94ee58162aa7"), 3 triggerDate : ISODate("2021-03-10:50:00:00Z") 4 }
The time and
_id
are calculated from the source document and aim to fire once 2021-03-10:50:00:00Z
arrives via a fireScheduleTasks
trigger. This trigger is based on a delete operation out of a TTL index on the triggerDate
field from the notifications
.This is when the user gets the reminder!
On a high level, here is the flow described above.
There are three main components that allow our system to trigger based on our document data.
First, we need to prepare our
notifications
collection. This collection will be created implicitly by the following index creation command.Now we will create a TTL index. This index will cause the schedule document to expire when the value in
triggerDate
field arrives at its expiry lifetime of 0 seconds after its value.1 db.notifications.createIndex( { "triggerDate": 1 }, { expireAfterSeconds: 0 } )
When setting up your
scheduleTasks
trigger, make sure you provide the following:- Linked Atlas service and verify its name.
- The database and collection name we are basing the scheduling on, e.g.,
meetings
. - The relevant trigger operation that we want to schedule upon, e.g., when an event is inserted.
- Link it to a function that will perform the schedule collection population.
To populate the
notifications
collection with relevant triggering dates, we need to monitor our documents in the source collection. In our case, the user's upcoming meeting data is stored in the "meeting" collection with the userId field. Our trigger will monitor inserts to populate a Scheduled document.1 exports = function(changeEvent) { 2 // Get the notifications collection 3 const coll = context.services.get("<ATLAS-SERVICE>").db("<DATABASE>").collection("notifications"); 4 5 // Calculate the "triggerDate" and populate the trigger collection and duplicate the _id 6 const calcTriggerDate = new Date(changeEvent.fullDocument.eventDate - 10 * 60000); 7 return coll.insertOne({_id:changeEvent.fullDocument._id,triggerDate: calcTriggerDate }); 8 };
Important: Please replace <ATLAS-SERVICE> and <DATABASE> with your linked service and database names.
To react to the TTL "delete" event happening exactly when we want our scheduled task to be executed, we need to use an "on delete" database trigger I call
fireScheduleTasks
.When setting up your
fireScheduleTasks
trigger, make sure you provide the following:- Linked Atlas service and verify its name.
- The database and collection for the notifications collection, e.g.,
notifications
. - The relevant trigger operation that we want to schedule upon, which is "DELETE."
- Link it to a function that will perform the fired task.
Now that we have populated the
notifications
collection with the triggerDate
, we know the TTL index will fire a "delete" event with the relevant deleted _id
so we can act upon our task.In my case, 10 minutes before the user's event starts, my document will reach its lifetime and I will send a text using Twilio service to the attendee's phone.
A prerequisite for this stage will be to set up a Twilio service using your Twilio cloud credentials.
- Set up the SID and Auth token into the Realm Twilio service configuration.
Once we have it in place, we can use it to send SMS notifications to our invites.
1 exports = async function(changeEvent) { 2 // Get meetings collection 3 const coll = context.services.get("<ATLAS-SERVICE>").db("<DATABASE>").collection("meetings"); 4 5 // Read specific meeting document 6 const doc = await coll.findOne({ _id: changeEvent.documentKey._id}); 7 8 // Send notification via Twilio SMS 9 const twilio = context.services.get("<TWILIO-SERVICE>"); 10 twilio.send({ 11 to: doc.phone, 12 from: "+123456789", 13 body: `Reminder : Event ${doc.event} is about to start in 10min at ${doc.scheduledOn}` 14 }); 15 };
Important: Replace <ATLAS-SERVICE> and <DATABASE> with your linked service and database names.
That's how the event was fired at the appropriate time.
With the presented technique, we can leverage existing triggering patterns to build new ones. This may open your mind to other ideas to design your next flows on MongoDB Realm.
In the following article in this series, we will learn how we can implement auto-increment with triggers.
If you have questions, please head to our developer community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.