Friday, May 30, 2014

BIRT , Spring framework and Neo4j (BSN) - one more thing ~ Cypher

The main intent of the previous blog post (ref.1.: "BIRT , Spring framework and Neo4j (BSN)") was to describe sample BIRT integration into the Spring-Ecosystem with usage of Neo4j GraphDB over spring-data-neo4j project. 

GiTHub project: miko-spring-birt is waiting to be tested

I got some open questions there and some fun with running on different machines. The answer to the FeatureTwo (ref.1.), is still postponed, but it's coming, let's keep in mind. 

This additional blog post I would like to focuse on awesome features of Cypher and show some nice use-cases, how to get POJOs from Neo4j and deal with them. 
(moto: keep it simple just Cars and Garages)

Let's start with few words at the beginning. 
Cypher (intro) is a declarative graph query language which allows us to do lot of things (expressive and efficient queries , creating or updating GraphDB etc.). The main goal has been to create language suitable for developer and professionals (human readable declarations).  Cypher language has been also inspired by couple of different approaches (SQL, SPARQL for expressions and also some semantic details from other languages) as is visible from the updated demo (GiTHub miko-spring-birt)


BSN - Neo4jDB initial state
pic.1.:: initial state of Neo4jDB Cars are located in their Garages

By using Cypher language over native Neo4j interface (http://localhost:7474) written in AngularJS we can employ Cypher abilities to get specific NCar located in NGarage (pic.1.)
Let's find the 'Ford' node with its NGarage
CYPHER $: MATCH MATCH (ncar:NCar {make: 'Ford'})<-[:located]-(garage: NGarage {name:'GarageOne'}) return ncar, garage
Using defined command query we should get the following result (pic.2.)
pic.2.::NCar Ford is located in the GarageOne
We can also check which cars are located in the NGarege {name: 'GarageOne') by doing small changes into the Cypher query:
CYPHER $: MATCH MATCH (ncar:NCar {coreName: 'CAR'})<-[:located]-(garage: NGarage {name:'GarageOne'}) return ncar, garage
At the end we should get expected result (pic.3.):
pic.3.:: GarageOne has some cars
Now is good time to move to the next step. 

Let's merge Cypher abilities with Spring-Data-Neo4j GraphRepository interface implementation. Maybe it can be useful to make small comparison with Spring-Data-JPA project to see tiny differences between passing @Param into the @Query (service.3.)

To see how everything works we will create following simple services: 

1. service will find and return the Car made by Ford by using @Query annotation in repository service. We define 
public interface NeoCarService {
NCar findByMakeFord();
method which calls repository implemented service
@Query("MATCH (ncar:NCar) WHERE ncar.make = 'Ford' return ncar")
NCar findByMakeFord();
2. service is almost similar but the difference is new condition. It will return the car made by 'Ford' only if the car is located in the garage and return.  In this case we extend our service 1. by adding also reference to the car garage : 
public interface NeoCarService {
  ...
  NCar findByMakeFord();
  NCar findByMakeForWithGarage();
  ...
the service implementation
...
@Override
public NCar findByMakeForWithGarage() {
  NCar car = nCarEntityRepository.findByMakeFordWithGarage();
  car.setGarage(nGarageEntityRepository.findOne(car.getGarage().getId()));
  return car;
}
...
now do update to the related repository
public interface NCarEntityRepository extends GraphRepository<NCar> {
...
@Query("MATCH (ncar:NCar {make:'Ford'})<-[:located]-(garage) return ncar")
NCar findByMakeFordWithGarage();
...   
3. service will return the number of located cars in the chosen Garage by name. To do so is necessary to define new method provided by NeoGarageService
public interface NeoGarageService {
  ...
  int getCarNumberByName(String garageName);
  ...
and we can call over the repository directly Cypher query:
public interface NGarageEntityRepository extends GraphRepository<NGarage> {
  ...
  @Query("MATCH (g:NGarage)-->(nCar: NCar)-->(g_of_nCar) WHERE g.name = {name} RETURN count(g_of_nCar)")
  int getCarNumberByName(@Param("name") String name);
  ...
 
When you try Cypher query over Neo4j AngularJS interface you should see the following result (pic.4.)


pic.4.:: In the GarageOne are located following number of cars

Here is visible the tiny difference between passing @Param int the query in case of JPA CrudRepository
@Query("select c from Car c where u.make in :names")
Iterable<Car> findByNames(@Param("names")Set<String> makeList);
At the end of the story we create simple BIRT report by creating simple Report Design File (SampleNeCarGarage.rptdesing) and new link to the hellp.jsp to the BirtController.
The final result looks pretty cool, we know which model of Ford we do have. Additionally we know his location and Total number of Cars located at the same Garage (pic.5.). 

pic.5.:: Cypher in Action and giving us information where the Ford is and how many cars are located in the GarageOne => 3 (pic.3.)
Conclusion: 
Enjoy and have a fun with BIRT, Neo4j and Spring 

No comments: