Trigger To Insert/Update To Another Collection

I’m trying to write a trigger script – I think a very simple one.

First Q: Do manual updates in MongoDB trigger triggers?

Main Q: How do I do this? …

I have a posts_md collection. It has an insert/update trigger. I want to copy every document to another posts_html collection. This is my attempt at code:

  const fullDocument = changeEvent.fullDocument;
  const htmlCollection = context.services.get("Cluster0").db("db_name").collection("post_html");
  if (fullDocument != undefined) {
    fullDocument.contentType = "html";
    htmlCollection.updateOne({'slug': fullDocument.slug, ...fullDocument})
  }

Here’s I’m changing one field: contentType from md to html. The ultimate goal is to convert the markdown to html as wellof course, regular CMS stuff. But first I’m just trying to get the basic document copying over.

So far it just doesn’t work.

Thanks for any help!

Hi @Tim_N_A ,

I noticed that the update does not have the set or replace document but only the criteria.

an updateOne command needs to have updateOne(query, update, options) :

Perhaps the ...fullDocument should be placed as the updates parameter and not as part of the query param.

Thanks,
Pavel

Thanks for the response. I’m also trying it this way:

htmlCollection.updateOne({'slug': fullDocument.slug}, { $set: fullDocument })

Which is closer to has worked for me in the past using the mongodb frontend library. I tried that other way based in the extremely limited examples in the trigger documentation ( https://docs.mongodb.com/realm/triggers/trigger-snippets/ ).

Neither use of the updateOne() arguments has given me any results.

Is there a clear documentation on how trigger scripts are supposed to run?

I really feel like what I’m writing here should be EXACTLY what dozens of other CMS do, isn’t this a standard use case?

Hi @Tim_N_A ,

Can you use a .then statement to verify how much documents are updated? I suspect that number of documents updated is 0.

Btw if you expect this operations to be upsert you need to update an upsert : true.

You can also use async function declaration and await for a synchronous statement and place the output into a variable.

This operations are not complex there are probably something in the data or filter that doesn’t add up.

Thanks
Pavel

Yes, I definitely do want to upsert. Initially the html entry won’t exist, the first time it needs to be created. In the future it matches by slug. (which I get will break if the slug breaks, I should update a reference to an id, but I’m still just trying to get the basics working.)

Still does not work though. How exactly should this update statement work?

htmlCollection.updateOne({'slug': fullDocument.slug}, { $set: fullDocument }, {upsert: true})

Also, how do a make I trigger an async function?

With database operations, await is usually far cleaner than then().

This is what I’m trying now using an htmlId. Still does not work. Am I using the documents/objects correctly?

  const mdDocument = changeEvent.fullDocument;
  if (mdDocument === undefined) { return; }
  
  const mdObject = mdDocument.toObject();
  const htmlObject = mdDocument.toObject();
  htmlObject.contentType = "html";
  
  const htmlCollection = context.services.get("Cluster0").db("awesauce_db").collection("post_html");
  
  if (mdObject.htmlId === undefined) {
    htmlCollection.insertOne(htmlObject).then((res) => {
      mdObject.htmlId = res.insertedId;
      changeEvent.collection.updateOne({_id: mdObject._id}, { $set: mdObject }, {upsert: true});
    });
    
  } else {
    htmlCollection.updateOne({_id: mdObject.htmlId}, { $set: htmlObject }, {upsert: true});
  }

Hi @Tim_N_A ,

This code looks like it has some issues , for example chnageEvent.collection is not a reference to the collection itself and i don’t believe has an updateOne function.

Additionally there are const declared values which you are changing. So there are fundamental js issues here, does this code run without errors??

Please share the trigger web url so I can have a look.

Look at my blog series here it contains some similar trigger manipulation maybe it will provide you with the way to write this one. It has an async and await trigger syntax as it is much cleaner and advised.

Thanks
Pavel

Thanks for the response

This code looks like it has some issues , for example
chnageEvent.collection is not a reference to the collection
itself and i don’t believe has an updateOne function.

I can just grab a reference to the collection from scratch I guess. Surprising the changeEvent doesn’t have one?

Additionally there are const declared values which you are
changing. So there are fundamental js issues here, does this
code run without errors??

In javascript I should be able to change the properties of a const object – and I expect that to be the correct use here? Something seems to definitely be wrong with the fullDocument though.

Please share the trigger web url so I can have a look.

I don’t know the web url? This is a trigger, not a web hook.

Look at my blog series here it contains some similar trigger manipulation
maybe it will provide you with the way to write this one.
It has an async and await trigger syntax as it is much cleaner and advised.

Thanks, ok… yeah await and async work as expected. The trigger web editor just doesn’t like it.

With the help if your examples, I can add a brand new entry to another collection. So it’s definitely the fullDocument that’s failing.

WHERE CAN I get some reference and help how to work with changeEvent and fullDocument?

OK! I finally got it working:

I copy over the fullDocument every time… I know I know only the changed fields. But the point of this is to only run when a blog post is update, rare, so I’d rather make sure everything copied will than care about that level of efficiency?

exports = async function(changeEvent) {
  
  const post = changeEvent.fullDocument;
  if (post === undefined) { return; }
  const mdId = post._id;
  const htmlId = post.hasOwnProperty("htmlId") ? post.htmlId : undefined;
  
  // convert to html post
  delete post._id;
  delete post.htmlId;
  post.contentType = "html";
  
  const htmlCollection = context.services.get("Cluster0").db("database").collection("posts_html");
  
  if (htmlId === undefined) {
    const res = await htmlCollection.insertOne(post);
    const mdCollection = context.services.get("Cluster0").db("database").collection("posts_md");
    mdCollection.updateOne({_id: mdId}, { $set: {htmlId: res.insertedId} });
    
  } else {
    return htmlCollection.updateOne({_id: htmlId}, { $set: post}, {upsert: true});
  }
};

NEXT STEP: Get the markdown library working with a trigger… Twilio is a special third party service?

How do I add a node library for markdown?

Hi @Tim_N_A ,

Consider using the external dependency for node packages :

https://docs.mongodb.com/realm/functions/upload-external-dependencies/

Even if you use atlas trigger you still have a realm app in the realm tab.

What I meant by url of the trigger is basically copy the url in the browser when you are editing the trigger UI/function. This way I cN view code and config…

Thanks
Pavel

@Pavel_Duchovny thanks! Yes, I see that the triggers are also realm apps now. I’m making realm web hooks to access the CMS in second realm app.

I got a node library, markdown-it, working pretty easily just uploading a zip once I understood what it was asking.

FOR ANYBODY WITH SIMILAR ISSUES: My main problem above is that I thought the fullDocument was a MongoDB Document type, not a simple object.