Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
GrahamRobbo
Active Contributor

Included as part of HANA SP07, due very soon, comes “River”.

River, or River Definition Language (RDL), is a new programming paradigm that is very different from what traditional SAP developers have been used to. The antecedents of River include another "River" that was floated about 3 years ago and technology acquired by SAP from a company called Coghead.

The goal of River is that developers will not so much code exactly what an application does, but rather describe its entity relationship model, associated behaviours and other aspects at a high level and then the RDL compiler will produce suitable artefacts to implement the application on the underlying platform. Importantly the entire implementation of the application is handled by the River compiler.  Let me show you what I mean.

*Note: It is not my intention to provide an exhaustive description of how to setup your River development environment nor describe the developer workflow. This is covered in the River documentation from where I took most of my examples used in this blog.

Let's start by defining an entity called "Actor" with three elements - name, emailAddress and age. The element “name” is the key of the entity. It is not compulsory to define an entity key (or keys). If you omit this then RDL will automatically add an element called “__ID” that becomes the key.

     entity Actor {
          key element name : String;
          element emailAddress : String;
          element age : Integer;
     }

When we activate this code it generates a number of HANA artefacts. This includes a table to hold the persisted entity data. Depending on the complexity of the entity there may be many more HANA artefacts as well.

The RDL compiler also generates the code that exposes the entity as an OData service with full CRUD - Create, Read, Update, Delete - functionality.

So I can make an OData request to the service to get a list of available entities. This shows me the Actor entity.

And I can use a HTTP POST request to create an new Actor entity by passing in this payload.

{

"name": "Tom Cruise",

"age": 51,

"emailAddress": "tom dot cruise at gmail dotcom"

}

Then I can do a HTTP GET request to the Actor entity to get the complete entity set returned - all 1 of them. :wink:

Now lets enhance our River application a bit to see some of the other features of RDL.

First we add a Movie and Producer entity. Here also are some examples of some options we can use when defining elements. The 'year' element of the Movie entity is defined as @required - so it must be supplied when you create a new instance of the Movie entity. Also the 'cost' element has been defined with a default value of 1 million.

entity Movie {
          key element title : String(100);
          @required element year : Integer;
          element cost : DecimalFloat default 1000000.0;
          element income : DecimalFloat;
          element producer : Association to Producer;
     }
    
     entity Producer {
          key element name : String;
          element movies : Association[0..*] to Movie via backlink producer;
     }

But most importantly you can see above how to establish associations between entities.

Because every Movie has one Producer the element 'producer' of the Movie entity is defined as a 1 to 1 association to a Producer entity. Similarly we define the element 'movies' in the Producer entity as a 0 to many association to the Movie entity set using the existing association (backlink) from the producer element of the Movie entity set. In a traditional database we would have to define foreign key relationships explicitly and when interacting with the database we would need an intimate knowledge of these relationships to be able to retrieve the relevant data. River removes the need for us to worry about this plumbing entirely.

What about many to many associations I hear you ask? Look carefully at the below code to see how this is done. First we need another entity to manage this more complex association which we will call 'Casting'. We define elements in this entity that are one to one associations to the Movie and Actor entities. And we define the appropriate elements in the Actor and Movie entities to each other using the "via entity Casting" option.

entity Movie {
          key element title : String(100);
          @required element year : Integer;
          element cost : DecimalFloat default 1000000.0;
          element income : DecimalFloat;
          element producer : Association to Producer;
          element starring : association[0..*] to Actor via entity Casting;
     }
    
     entity Producer {
          key element name : String;
          element movies : Association[0..*] to Movie via backlink producer;
     }

     entity Actor {
          key element name : String;
          element emailAddress : String;
          element age : Integer;
          element movies : association[0..*] to Movie via entity Casting;
     }

     entity Casting {
          @required element movie : Association to Movie;
          @required element star : Association to Actor;
          element salary : DecimalFloat;
     }

Again I think it is important to point out how little code we have had to write to define what are becoming increasingly sophisticated entities and associations. This is the goal of River.

Another cool thing is that you can make a single HTTP POST call to create an entity and other entities it is associated with. For example a HTTP POST to the Movie entity with the following payload will create both the "Top Gun" Movie entity, the "Jerry Bruckheimer" Producer entity and the Casting entity that manages the many to many association between them.

{

"title": "Top Gun",

"year": 1986,

"genre": "DRAMA",

"producer": {

"name": "Jerry Bruckheimer"

},

"income": 200

}

We can also create calculated elements in entities.

     entity Actor {
          key element name : String;
          element emailAddress : String;
          element age : Integer;
          element movies : association[0..*] to Movie via entity Casting;
          element totalIncome = SELECT ONE SUM(salary) as sumOfSalary FROM Casting;
          element birthYear = 2013 - age;
     }

The 'birthYear' element of the Actor entity is a simple calculation example. The 'totalIncome' element is a little more sophisticated in that it performs a SQL statement and returns the result whenever it is addressed.

You can also define your own data types, including complex types, and use enumerators as well.

     type Genre : enum { ACTION_GENRE; COMEDY; DRAMA; FAMILY; ADVENTURE; SCI_FI; }
     type Rating : String(5) enum {
          General_Audiences = 'G';
          Parental_Guidance = 'PG';
          Parents_Strongly_Cautioned = 'PG-13';
          Restricted = 'R';
     }
     type Address {
          element city : String (20);
          element country : String(20);
     }

     entity Movie {
          key element title : String(100);
          @required element year : Integer;
          element cost : DecimalFloat default 1000000.0;
          element income : DecimalFloat;    
          element producer : association to Producer;
          element starring : association[0..*] to Actor via entity Casting;
          element rating : Rating default Rating.Restricted;
          element genre : Genre;
          element location : Address;

And you can define River views as well.

     view Blockbusters as
          SELECT TOP 10 FROM Movie {
               title,
               genre,
               income
          }
          ORDER BY
               income DESC;

Notice that SQL syntax in River can be a bit different from the standard - which you can still use as well. SAP claim the new SQL syntax is more logical and easier to read, since you first write what entity you want to query, and then which elements you want to return. Certainly it makes code completion in the editor very effective when looking up elements. It also allows you to build complex queries without having to specify complex joins. For example the following statement implicitly makes a join between the Movie and Producer entities.

SELECT FROM Movie { producer.name, SUM(income - cost) as total} GROUP BY producer.name

Now what about some business logic. You add business logic to entities through Actions. These actions can take input parameters and can return values as well.

Look at the below example where we have added the action 'audition' to the Movie entity.

     entity Movie {
          key element title : String(100);
          @required element year : Integer;
          element cost : DecimalFloat default 1000000.0;
          element income : DecimalFloat;    
          element producer : association to Producer;
          element starring : association[0..*] to Actor via entity Casting;
          element rating : Rating default Rating.Restricted;
          element genre : Genre;
          element location : Address;
         
          action audition(auditionStar : Actor) : String {
               let message : String = 'Dear ' + auditionStar.name +
               '. Thank you for your application for the movie ' + this.title + '. ';
               if( auditionStar.age > 50 && this.genre != Genre.DRAMA) {
                    message = message + ' Unfortunately, you did not get the part.';
               }
               else {
                    message = message +
                         ' Congratulations! You are the perfect casting for this movie.';
                    let tmpMovies = SELECT movie,salary FROM Casting
                         WHERE Casting.star = auditionStar;
                        
                    let fairOffer = SELECT ONE AVG(salary) as offer FROM tmpMovies
                         WHERE movie.genre = this.genre;
                        
                    let c : Casting =
                         Casting{movie: this, star: auditionStar,
                              salary: fairOffer.offer};
                    c.save();
               }
               return message;         
          }
     }

In the first part of the audition action it checks if the actor is right for the part based upon their age. If they are under 50 they can get any part but if they are over 50 they can only get parts from the drama genre. A message variable is built to reflect this decision that is returned when the action completes.

If the actor is accepted the second section of the audition action creates a new Casting entity. First it queries existing Casting entities for this actor and uses the results to calculate the average salary for this actor for the chosen genre. The calculated average salary is passed into the instantiation of the new Casting entity and then the save method is called to persist the new entity.

So by calling the audition action for the move 'Top Gun' with a HTTP POST request to...

http://river.yelcho.com.au/sap/hana/rdl/odata/v1/GrahamRobbo/RiverDemo/Movie('Top Gun')/audition

...and passing in the following payload....

{

"auditionStar": "KEY(Actor('Tom Cruise'))"

}

....I get the result....

{

"d": "Dear Tom Cruise. Thank you for your application for the movie Top Gun. Congratulations! You are the perfect casting for this movie."

}

And more importantly the new Casting entity has been created for me as well.

Again the important message here is that the developer gets to focus on what the application actually has to do rather than the minutiae of the implementation. That job is left the the RDL Compiler.

Last step to complete our application is the user interface. River does not play here - it is only for building the back-end. You can use whatever your favourite UI technology is to build the user experience on top of the OData services that the RDL compiler has built for you.

Perhaps a Fiori-style SAPUI5 experience might suit?

33 Comments
Labels in this area