Saturday, February 25, 2012

Neo4J with Scala Play! 2.0 on Heroku (Part 7) :: DSM+DAO+Neo4J+Play


Note

This post is a continuation of this post, which is the sixth part of a blog suite that aims the use of Neo4j and Play2.0 together on Heroku.

Using Neo4J in Play 2.0... and simple DAO

What I'll intent to show is a way to use a Domain Specific Model, persisted in a Neo4J back end service. For such DSM, we'll have an abstract magic Model class that defines generic DAO operations.

For simplicity, we'll try to link each category of classes to the root/entry node. For instance, all the Users will be bound to the entry node by a reference of kind user.

Model

I'll choose the very common use case, that is, Users and Groups. Here is its shape:
  • A User has a first name
  • A Group has a name
  • A User can be in several Groups
  • A Group can contain several Users
  • A User can know several Users
Let's keep the classes definition aside for a few, and stick to the persistence service.

Graph Service

The Graph Service is an abstraction of what is needed for a Graph Persistence Layer. It is bound to a generic type that defines the model implementation and defines traversal and persistence operations of graph's nodes.

Graph Service for Neo4J

Let's update now, the service that has been used in the previous post, for Neo4J persistence, in order to have it able to deal with model instance.

Let's start with the saveNode operation to see what is needed in the model and elsewhere.

In this Gist above, I've enlighted some points that must be found around the Model construction. (A) and (C) are composing a Json Format (as SJson propose), (B) is more related to model abstraction.

(C) has a special need when used with Dispatch, we could have a Dispatch Handler that can do both action parsing/unmarshalling and direct use in a continuation.

Model

Now, we are at the right point to talk the Model, since we've met almost all its requirement. So let's build a Magic Model class that can be extended by all concrete model classes.
Skeleton
That's the easy part, we just define the id property that is an id (part of the Rest Url in Neo4J).
Formatter
Ok, this part is simple too in this abstract Model definition because, a Format implementation must be part of the concrete DSM classes. That is, User that extends Model must define a Format[User] instance, and put it in the implicit context.
So, at this stage we have Model and User like this:

Class -- Relation's kind : F-Bounded
As we saw in the saveNode method needs to associate the concrete class to a relation kind. But what I wanted is to have a save method in Model, that implies that we cannot (at first glance) give the saveNode the information needed, that is the concrete class.

For that, we'll use a F-Bounded type for Model, that way we'll be able to give the saveNode method what is the really class... Mmmh ok, let me show you: But that's not sufficient, the saveNode method will need to use such available ClassManifest to find the relation it must create.

I choose a very common and easy solution, which is having a function in the Model companion that helps in registering classes against relation kind.

Model Dispatch Handler

Now we'll discuss something I find really useful and easy in Dispatch, create a Handler that can handle a Json response from Neo4J into a Model instance.
For that, we have already defined in previous post a way to handle json response into Play's JsValue.

Now, what we need is to use the implicit formatter of all model concrete classes to create instances. And it'll be the way to reach the goal, except that a problem comes from the Json response of Neo4J: the data is not present at the Json root, but is the value of the data property.
So it breaks our Format if we use it directly.

That's why the above definition of the Handler takes an extra parameter which is the conversion between JsValue to JsValue, that is to say, a function that goes directly to the data definition.

saveNode

Finally, let's gather all our work in a simple implementation of a generic saveNode function: As we can see, it's very easy to handle Neo4J response as DSO and use them directly in the continuation method of the Handler.

Usage

having all pieces in places (check out the related Git repo here). We can now really simply create a User and retrieve it updated with its id, or even get it from the database using its id.

In the next Post, we'll create some Play template for viewing such data, but create them also.