Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
UweFetzer_se38
Active Contributor


(Edit Jan 24, 2015: added Transaction handling and side note (this is the last update here, all further tutorial versions you'll find here.)

The other day I've played a bit with the Open Source Graph database Neo4j (for no reasons, just to learn something new), and the usual "what if..." came to my mind and I've started to code.

 

The result is a Neo4j ABAP Connector called Neo4a, available under Apache License 2.0 on Github: Neo4a

The whole project is coded under Netweaver ABAP Stack 7.40 SP8. It will not work on lower releases and will not be downported (sorry).

Installing and running Neo4j (example for Linux)


In my case I've created a new virtual Linux machine (CentOS) under VMware to separate the database from my SAP server, but it should work also, if you are installing Neo4j on the same server (don't do this in production).


Just download the tar (choose your OS: http://neo4j.com/download/other-releases/), extract into a folder of your choice, call './bin/neo4j start' and you are done.

 

Installation of Neo4a


You need the most current ABAP JSON Document class (zJSON Version 2.28, available on Github https://github.com/se38/zJSON ).

Install the zJSON and Neo4a nuggets via SAPlink and activate the sources. It may be a good idea to move the classes into a new development package, but this is not required.

A small Tutorial for Neo4a


The following examples are taken from the Neo4j tutorial and are "translated" into ABAP.

 

(side note: if you screwed up and want to start all over again, just stop Neo4j with './bin/neo4j stop' and delete the content of the 'data' folder with 'rm -rf data/*'. An empty database will be recreated automatically if you start the database again)

Create nodes

    "*--- points to the Neo4j DB host ---*


    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    TYPES: BEGIN OF ty_actor,


             name TYPE string,


           END OF ty_actor,


           BEGIN OF ty_movie,


             title TYPE string,


           END OF ty_movie.


    DATA(actor) = VALUE ty_actor( name = 'Tom Hanks' ).


    DATA(movie) = VALUE ty_movie( title = 'Sleepless IN Seattle' ).


    TRY.


        DATA(node_actor) = neo4a->create_node(


                       i_properties = actor


                       i_label     = 'Actor'


                   ).


        DATA(node_movie) = neo4a->create_node(


                       i_properties = movie


                       i_label     = 'Movie'


                   ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.




Result: Two nodes


Create a relationship between the two nodes



    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    TRY.


        DATA(node_actor) = neo4a->get_node( 1 ).


        DATA(node_movie) = neo4a->get_node( 2 ).


        DATA(relationship) = node_actor->create_relationship(


                         i_type        = 'acted_in'


                         i_to_node     = node_movie


                     ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.



Result: the two nodes are conected


Set/Get properties



    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    TRY.


        DATA(node) = neo4a->get_node( 1 ).


        node->set_property(


          EXPORTING


            i_name   = 'year_of_birth'


            i_value  = '1944'


        ).


        DATA(name) = node->get_property( 'name' ).


        DATA(yob) = node->get_property( 'year_of_birth' ).


        cl_demo_output=>display( |{ name } : { yob }| ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.




Get all properties


You can get the properties of a node as data object, JSON string of Name/Value pairs.

    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    DATA: BEGIN OF actor,


            name          TYPE string,


            year_of_birth TYPE string,


          END OF actor.


    TRY.


        neo4a->get_node( 1 )->get_properties(


          IMPORTING


            e_properties      = actor


            e_properties_json = DATA(properties_json)


            e_properties_table = DATA(properties_table)


        ).


        cl_demo_output=>begin_section( 'DATA' ).


        cl_demo_output=>write( actor ).


        cl_demo_output=>begin_section( 'JSON' ).


        cl_demo_output=>write( properties_json ).


        cl_demo_output=>begin_section( 'Name/Value pairs' ).


        cl_demo_output=>write( properties_table ).


        cl_demo_output=>display( ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.



Result:


Get all nodes with a label



DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    TYPES: BEGIN OF ty_movie,


             title TYPE string,


           END OF ty_movie.


    DATA(movie) = VALUE ty_movie( title = 'Forrest Gump' ).


    TRY.


        "*--- create another movie ---*


        DATA(node_movie) = neo4a->create_node(


                 i_properties = movie


                 i_label     = 'Movie'


             ).


        "*--- and connect to Tom ---*


        neo4a->get_node( 1 )->create_relationship(


          EXPORTING


            i_type        = 'acted_in'


            i_to_node     = node_movie


        ).


        "*--- get all movies ---*


        neo4a->get_nodes_with_label(


          EXPORTING


            i_label     = 'Movie'


          IMPORTING


            e_nodes     = DATA(movies)


        ).


        LOOP AT movies ASSIGNING FIELD-SYMBOL(<movie>).


          cl_demo_output=>write_text( <movie>->get_property( 'title' ) ).


        ENDLOOP.


        cl_demo_output=>display( ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.



The second movie is created and connected to Tom:



The list contains all movies:


Queries


To submit queries to the database we are using the Neo4j Cypher Query Language

    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    "*--- get all movies where Tom acted in ---*


    DATA(query) = `MATCH (actor:Actor {name:'Tom Hanks'})-[r:acted_in]->(movie:Movie) RETURN movie.title`.


    TRY.


        neo4a->query(


          EXPORTING


            i_cypher = query


          IMPORTING


            e_result = DATA(result)


        ).


        cl_demo_output=>display_json( result ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.



Result:


Transactions


(time to remember the side note at the top of this tutorial)

 

With transactions you can make a number of requests, each of which executes additional statements, and keeps the transaction open by resetting the transaction timeout. After the timeout an automatically rollback occurres. You can see the timeout in every response of a request to a new or open transaction.

  "transaction" : {


    "expires" : "Fri, 24 Jan 2015 22:53:51 +0000"


  },



Now let's have a look at the following example:

    DATA(neo4a) = NEW zcl_neo4a( '192.168.38.52' ).


    DATA(statements) = VALUE string_table(


      (


       `CREATE (matrix1:Movie { title : 'The Matrix', year : '1999-03-31' })` &&


       `CREATE (matrix2:Movie { title : 'The Matrix Reloaded', year : '2003-05-07' })` &&


       `CREATE (matrix3:Movie { title : 'The Matrix Revolutions', year : '2003-10-27' })` &&


       `CREATE (keanu:Actor { name:'Keanu Reeves' })`  &&


       `CREATE (laurence:Actor { name:'Laurence Fishburne' })` &&


       `CREATE (carrieanne:Actor { name:'Carrie-Anne Moss' })` &&


       `CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix1)` &&


       `CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix2)` &&


       `CREATE (keanu)-[:ACTS_IN { role : 'Neo' }]->(matrix3)` &&


       `CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix1)` &&


       `CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix2)` &&


       `CREATE (laurence)-[:ACTS_IN { role : 'Morpheus' }]->(matrix3)` &&


       `CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix1)` &&


       `CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix2)` &&


       `CREATE (carrieanne)-[:ACTS_IN { role : 'Trinity' }]->(matrix3)`


      )


      (


       `CREATE (whatever:Movie { title : 'Just another Movie', year : '2015-01-24' })`


      )


    ).


    DATA(next_statements) = VALUE string_table(


      (


       `CREATE (whatever2:Movie { title : 'Yet another Movie', year : '2015-01-24' })`


      )


    ).


    TRY.


        "*--- create the transaction ---*


        DATA(transaction) = neo4a->create_transaction( ).


        "*--- send the DB statements ---*


        transaction->send( i_statements = statements ).


        transaction->send( i_statements = next_statements ).


        "*--- placeholders without properties should be visible now ---*


        cl_demo_output=>display( 'have a look in the Neo4j browser' ).


        "*--- and rollback the work ---*


        transaction->rollback( ).


        "*--- look Mom, they are gone ---*


        cl_demo_output=>display( 'and look again' ).


      CATCH zcx_neo4a INTO DATA(n4a_ex).


        WRITE😕 n4a_ex->get_text( ).


        RETURN.


    ENDTRY.



After the first stop, you can see the placeholders in the Neo4j browser



Close the output window. After the next stop look again -> the placeholders are gone.

Now replace the last two statements with:

        "*--- commit the work ---*


        transaction->commit( ).


        "*--- tahtah, the Matrix ---*


        cl_demo_output=>display( 'the red pill please' ).



and try again -> after the last stop the Matrix is alive...



http://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Red_and_blue_pill.jpg/320px-Red_and_blue_pill.jpg

Image by W. Carter (Wikimedia)

More methods are already implemented (ie. deletes, just have a look at the class interfaces), others are in the pipeline (see the open issues on Github)

You can find me on Twitter and G+

7 Comments