EJSON / JSON problem with clusterTime

Hoping to get some help with an Atlas / Realm Function that is a database trigger for a changed record.


Here is the function that is called by the changed record trigger:

exports = async function(changeEvent) {
  
  let fullDocument = JSON.parse(JSON.stringify(changeEvent.fullDocument));
  fullDocument.clusterTime = changeEvent.clusterTime;
  
  const send = await context.http.post({
    url: "some url",
    body: fullDocument,
    encodeBodyAsJSON: true
  })
  
  return "OK"
  
};

This sends:

{"_id":"636856174a7b34feece637b6","someKey":"blah","NewKey":"abc","someTime":"2010-01-01T05:11:00.000Z","clusterTime":{"$timestamp":{"t":1667868115,"i":19}}}

How do I get it to send normal JSON for clusterTime?

Even if I try to access the “t” or “i” like:

let clusterTime = changeEvent.clusterTime.$timestamp

I get back clusterTime: {"$undefined", true}

My questions are:

  • How can I easily convert EJSON to standard JSON. I tried compounding stringify and parse for clusterTime but it didn’t work
  • How can I access the $timestamp object using dot notation from this function without it giving a result of $undefined

Many thanks!

Hi @Christopher_Barber ,

The following should do:

  const timestamp = changeEvent.clusterTime.toJSON()["$timestamp"];
  
  console.log(JSON.stringify(timestamp));
  
  const changeTime = new Date(timestamp.t * 1000 + timestamp.i);  // Resolution of 1ms - i is just a progressive number

The i part is a progressive number, so the time itself has a 1s resolution. Assuming you never have more than 1000 events in the same second, the above should however provide the proper sequence.

Many thanks!

const timestamp = changeEvent.clusterTime.toJSON()["$timestamp"];

returns

"timestamp":{"t":{"$numberInt":"1667923613"},"i":{"$numberInt":"29"}}

This is an improvement, for sure! However…

I understand that this is the expanded JSON that mongo uses, but how do I completely get outside of that and get into standard JSON?

This still returns an object, so if you assign it directly to a field, it will retain its specific data types (long for t, int for i), and that’s fine if you want to store it in MongoDB properly. My example was using a conversion to Date type instead.

In your specific example, if you want to retain the timestamp format, you can try the following:

  const send = await context.http.post({
    url: "some url",
    body: JSON.stringify(fullDocument),
    encodeBodyAsJSON: false
  });
1 Like

Thanks again. Is there some documentation you could point me to so I can understand better

  • toJSON()["$timestamp"];

  • why this works even though the timestamp object contains $numberInt in it?

changeTime = new Date(timestamp.t * 1000 + timestamp.i)

Hi @Christopher_Barber,

  • why this works even though the timestamp object contains $numberInt in it?

I’ll answer this first, as there’s a misconception here: $numberInt (or any other $xx property) is only added when you represent the timestamp in Extended JSON, it’s not part of what the underlying Javascript object really is in memory (or in the database)!

In fact, when you do
console.log(`t is a ${typeof timestamp.t}`);
what you get is
t is a number
so it’s perfectly fine to use it in calculations.

  • toJSON()[“$timestamp”];

Per the reason outlined above, to extract the inner representation of the Timestamp as an object, we need to convert it, and this has that purpose: apologies, but I haven’t been able to find the exact documentation.

Thanks so much for pointing me in the right direction!