Polymorphism in the Database: Lift and Mongo

There are a number of reasons I love Lift. I was thinking through some architectural changes that are coming with Anchor Tab soon and I realized that the integration between Lift (the web framework), lift-json (the JSON parsing library), and MongoDB are is pretty sweet. Let’s say that you’re working on a pet-themed website and you need to store information about some cats and dogs. You would probably define something similar to the following data model.

This is an entirely sensible data model. (Albeit, my naming of classes could probably use some work, but this’ll suffice.) So, what would it look like if I wanted to take an instance of one of these objects and turn it into something that could be then turned into JSON? (Let’s say, for an API.) That code would look something like this.

In the example above, the jvalueDog is an instance of JValue. You can think of JValues as lift-json’s intermediary format between real domain model objects and actual, stringified JSON. (If we were to be proper, we’d say it’s a part of the Abstract Syntax Tree, but that’s a mouthful.) You’re able to query them for their content, manipulate their structure, and do all varieties of fun things with them – including return them as the result of a function that’s exposed as a REST API, and have Lift handle the serialization processes for you.

Part of what makes lift-json so beautiful is it’s adept use of reflection to automatically figure out what things in a JValue should be named and how things fit together during the deserialization process. But there’s a rub with this reflection and our data model. What if we want to serialize and deserialize an entire AnimalInfo object? We’ll see that we run into a problem. Take, for example, the following code:

As noted in the comments, the code above will fail to execute. In fact, not only will it fail, it’ll blow up pretty spectacularly because it can’t directly instantiate an Animal.

The solution to this problem lies in a concept in lift-json called type hints. Type hints are attached to the serialization/deserialization chain of lift-json. During serialization, if a class that is being serialized matches one of the classes that we indicate we want a type hint for, lift-json will add a jsonClass parameter to the JValue as it’s being built. To define type hints, all we have to do is adjust our formats definition, and things will start working for us.

The above example will work because on deserialization, lift-json sees the jsonClass parameter attached to the JValues, sees that they fit the interface Animal, and instantiates them correctly without fuss. This functionality sets us up pretty nicely for seamless polymorphism in the database.

Mixing in Mongo

The next step that we’d want to implement in this setup is to mix in MongoDB. Since MongoDB is a document store that (basically) stores JSON objects, lift-json and lift-mongodb end up working pretty well together. Also, unlike a conventional relational database, MongoDB’s non-relational nature means it doesn’t require that each document have the same fields. We can store our cats and dogs side-by-side in our AnimalInfo with no-fuss even though they don’t have the same properties, and without having to drop NULLs in our dataset.

Converting the example code above to a functioning MongoDB representation is pretty simple with lift-mongodb’s MongoDocument and MongoDocumentMeta traits. (We, of course, assume in this code that you’ve already connected to the database elsewhere.)

When you go back into your Mongo collection later and pull out that AnimalInfo, you’ll find everything is the correct instance automagically.

Isn’t life grand? This was just something that was on my mind today, so I thought I’d share the joy around.

Sound interesting?

This was a whirlwind, isolated example of what lift-json and lift-mongodb are capable of, and how little code is required to get something meaningful happening in the Lift/Scala ecosystem. It’s just the tip of the iceberg. If you’re interested in digging into these libraries, or Lift, here are some useful springboards:

As always, leave me some comment love with thoughts, errors discovered, or locations of free cookies. See you shortly. 🙂