Home

Awesome

Erlang client library for Neo4J

This is a lightweight wrapper for Neo4j REST API.

Current versions: 0.2.1 and 0.3

If you want to use Basic Auth, use v0.3. Otherwise use 0.2.1. Read on for more info.

v0.3

Thanks to work by @dethtron5000, neo4j-erlang now supports Basic Auth. This means:

Changes

All API calls now require an additional parameter that contains request options. The options may contain the following parameters for Basic Auth:

Sample session for v0.3 (read below for v0.2.1)

### Sample session

```erlang

Options = [ {base_uri, <<"http://localhost:7474/db/data/">>}
          , {user, <<"user">>}
          , {password, <<"password">>}
          ],

Neo = neo4j:connect(Options),

StartNode = neo4j:get_node(Neo, 101, Options),
EndNode = neo4j:create_node(Neo, {[{<<"prop1">>, <<"key1">>}]}, Options),

Relationship1 = neo4j:create_relationship(StartNode, EndNode, <<"KNOWS">>, Options),
Relationship2 = neo4j:create_relationship(StartNode, EndNode, <<"KNOWS">>, {[{<<"prop2">>, <<"value2">>}]}, Options),

ok = neo4j:delete_relationship(Relationship1, Options).

Except for the new required parameter the rest of this documentation remains unchanged for v0.3.

v0.2.1

All the information below is unchanged for version 0.2.1.

Thanks to:

Breaking changes from 0.1

What?

How?

Sample session


Neo = neo4j:connect([{base_uri, <<"http://localhost:7474/db/data/">>}]),

StartNode = neo4j:get_node(Neo, 101),
EndNode = neo4j:create_node(Neo, {[{<<"prop1">>, <<"key1">>}]}),

Relationship1 = neo4j:create_relationship(StartNode, EndNode, <<"KNOWS">>),
Relationship2 = neo4j:create_relationship(StartNode, EndNode, <<"KNOWS">>, {[{<<"prop2">>, <<"value2">>}]}),

ok = neo4j:delete_relationship(Relationship1).

Read on for more details, or refer to comments in code or to the test suite.

Details

The wrapper follows Neo4j's REST API as close as possible. See code comments for direct links to each method/feature implemented. For example:

%%
%% http://docs.neo4j.org/chunked/stable/rest-api-nodes.html#rest-api-create-node-with-properties
%%
-spec create_node(neo4j_root(), proplists:proplist()) -> neo4j_node() | {error, term()}.
create_node(Neo, Props) ->
  {_, URI} = find(<<"node">>, 1, Neo),
  Payload = jiffy:encode(Props),
  create(URI, Payload).

That link will tell you exactly what's going on and what you should expect.

Errors

There are two types of errors the wrapper returns:

> neo4j:get_node(Neo, 10000).
{error,not_found}
> neo4j:unique_create_node(Neo, [{<<"prop">>, <<"val">>}], <<"index">>, <<"key">>, <<"value">>, <<"create_or_fail">>).
[{<<"self">>,
  <<"http://localhost:7474/db/data/index/node/index/key/value/191">>},
 {<<"extensions">>,[]},
 {<<"paged_traverse">>,
  <<"http://localhost:7474/db/data/node/191/paged/traverse/{returnType}{?pageSize,leaseTime}">>},
 {<<"labels">>,
...

> neo4j:unique_create_node(Neo, [{<<"prop">>, <<"val">>}], <<"index">>, <<"key">>, <<"value">>, <<"create_or_fail">>).

error,{409,
        <<"http://localhost:7474/db/data/index/node/index?uniqueness=create_or_fail">>,
        [{<<"extensions">>,[]},
         {<<"paged_traverse">>,
...

As expected, Neo4j returned a HTTP 409 Conflict code.

Assumptions

Location header

If an operation returns a HTTP 201 Created with a Location header, the wrapper will prepend a {<<"self">>, Location} to the proplist returned. Be wary of this especially when using paged traversals.

> Node = neo4j:get_node(Neo, 101).
> Body = {[ {<<"order">>, <<"breadth_first">>}
          , {<<"uniqueness">>, <<"none">>}
          , {<<"return_filter">>, {[ {<<"language">>, <<"builtin">>}
                                   , {<<"name">>, <<"all">>}
                                   ]}
            }
          ]}.
> PT = neo4j:paged_traverse(Node, Body).
[ %% <<"self">> is prepended
 {<<"self">>,
  <<"http://localhost:7474/db/data/node/101/paged/traverse/node/2e23bfca61144b0f91b446fb6be562b6">>},
 %% actual data
 {[{<<"labels">>,
 ...

No JSON, just EEP0018 structures

Even for complex queries (such as Cypher queries or transactions) you never send in raw JSON, only proplists representing your objects:

See the example in "Binaries" below

Binaries

All string data and all URL parameters sent to Neo4J are assumed to be binaries.

As an example, let's create a paged traverser

Neo = neo4j:connect([{base_uri, BaseUri}]),
Node = neo4j:get_node(Neo, 101),
Body = {[ {<<"order">>, <<"breadth_first">>}
        , {<<"uniqueness">>, <<"none">>}
        , {<<"return_filter">>, {[ {<<"language">>, <<"builtin">>}
                                 , {<<"name">>, <<"all">>}
                                 ]}
          }
        ]},
PT = neo4j:paged_traverse(Node, Body, [ {<<"returnType">>, ReturnType}
                                      , {<<"leaseTime">>, LeaseTime}
                                      , {<<"pageSize">>, PageSize}
                                      ]).

Does not do stuff for you

Neo = neo4j:connect([{base_uri, <<"http://localhost:7474/db/data/">>}]),

%% will not work:
neo4j:get_node_properties(101).

%% will not work:
neo4j:get_node_properties(<<"http://localhost:7474/db/data/node/101">>).

%% correct way:
Node = neo4j:get_node(N, 101),
neo4j:get_node_properties(Node).

%% also correct:
Node2 = neo4j:get_node(N, <<"http://localhost:7474/db/data/node/101">>),
neo4j:get_node_properties(Node2).

Contributing

Yes, please! :) If you have ideas, suggestions, pull requests or issues, do not hesitate to send them my way