Cosmos DB
Oh how I love thee. For those of you who don’t know - Cosmos is a NoSQL multi-modal database that was built for the cloud. It is also a globally distributed database. Now beware that once you start to enable more than a single region you will see your cosmos cost increase threefold. Another thing we need to understand about cosmos is that you interface with it more like you would interface with a microservices rather than a database. Sure, you can write SQL queries against it with the SQL API. You can also use: the Gremlin API, MongoDB API, Document API, Cassandra, and various others. Best of all - you can research exactly how cosmos works in dotnetcore. This alone has helped me multiple times when trying to figure out exactly why things aren’t working as they shoudl be. Also - Can we actually return the 404 response, so I don’t have to deal with an Exception please? I understand this is to help people migrate from v2 to v3 - but that’s why you version. You can make breaking changes like this!
Server Side Javascript
What this article is really about is the power of the pipelines within Cosmos that we can take full control of and use to our advantage. The Server Side Javascript in Cosmos allows us to do triggers on pre- or post- insertion. It also allows use to create stored procedures and user defined functions to perform ACID operations and also perform calculations inside of the database. But wait aren’t triggers something that we want to avoid? Triggers in Cosmos only run when you specify them to run. You will always know when they run because you must explicitly make them run. A side effect of this is that sometimes it can be very difficult to actually test one of these.
Pre- and Post- Triggers
Cosmos has what I like to call an insertion pipeline. This occurs whenever you Create, Update or Delete an item within the database. As you execute these events you can have trigger execute on any 1 of those operations or on ALL of the operations. Sorry but if you want only 2 of those things then you should just go and include a check for the operation within your trigger to ignore it.
Now the pre and post should be self-explanatory. Pre-triggers come before the operation and the Post-triggers come after the operation. If anything fails within that pipeline then the entire operation will fail. YAY transactional operations! Now for some samples and some documentation.
The power of the Pipeline
So what does this all mean? Well you can calculate meta-data of the collection on an insert/update/delete. Or you can simply calculate field values. When you create a cosmos container you can define required fields. Only issue is that you cannot change these after the fact. What you can do with a pre-trigger is that you can calculate and insert a default value into the cosmos document being inserted.
function SetUpdatedDate() {
let docFromRequest = __.getRequest().getBody();
docFromRequest.UpdatedDate = new Date();
__.setBody(docFromRequest);
}
Sure this is pretty simplistic right? Now you may have notice the __ (double underscore) that was used. This is actually shorthand for the getContext().getCollection() operation. It can get tedious writing that so they gave us a shorthand way of doing it. Thanks for the help! So the above trigger is actually equivalent to the following:
function SetUpdatedDate() {
let docFromRequest = getContext().getCollection().getRequest().getBody();
docFromRequest.UpdatedDate = new Date();
getContext().getCollection().setBody(docFromRequest);
}
Now something else to notice is that this is a pre-trigger since we are getting and editing the request. In a post trigger we actually have the ability to change and modify the response object that is being returned. Now this is sounding a little bit like middleware that exists in ASP.NET. As I learn more and more I’m convinced there’s probably only 20 different concepts in development expressed in 2 bajillion different implementations. Then again that is probably an over simplification of the matter. Then again microservice design mimics SOLID quite a bit but I digress.
I’ll be sure to expand on this in some future posting including how it is possible to create calculated effective start and end dates with insertion triggers in order to create model tracking over time within cosmos. Be sure to stay tuned!