Quantcast
Channel: Cross-Technology Developer Center
Viewing all articles
Browse latest Browse all 14

Neo4a: The Neo4j ABAP Connector

$
0
0

(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: BEGINOF ty_actor,

             name TYPE string,

           ENDOF ty_actor,

           BEGINOF ty_movie,

             titleTYPE string,

           ENDOF 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 INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

 

Result: Two nodes

neo4a-1.PNG

 

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 INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

Result: the two nodes are conected

 

neo4a-2.PNG

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 INTODATA(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: BEGINOF actor,

            name          TYPE string,

            year_of_birth TYPE string,

          ENDOF 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 INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

Result:

 

neo4a-5.PNG

 

Get all nodes with a label

 

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

 

    TYPES: BEGINOF ty_movie,

             titleTYPE string,

           ENDOF 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)

        ).

 

        LOOPAT movies ASSIGNING FIELD-SYMBOL(<movie>).

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

        ENDLOOP.

 

        cl_demo_output=>display().

 

      CATCH zcx_neo4a INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

The second movie is created and connected to Tom:

 

neo4a-3.PNG

 

The list contains all movies:

 

neo4a-4.PNG

 

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 INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

Result:

 

neo4a-6.PNG

 

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 INTODATA(n4a_ex).

        WRITE:/ n4a_ex->get_text().

        RETURN.

    ENDTRY.

 

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

 

neo4a-7.PNG

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...

 

neo4a-8.PNG

 

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+


Viewing all articles
Browse latest Browse all 14

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>