Neo4j – Library for accessing Neo4j database
1 Quick start
1.1 Parameters
1.2 Transactions
1.3 Prepared statements
1.4 Unsupported features
2 Connecting to Neo4j
neo4j-connect
8.1

Neo4j – Library for accessing Neo4j database

Tomasz Barański <tomasz.baranski@posteo.net>

 (require db/neo4j) package: neo4j-lib

The db/neo4j module provides support for querying Neo4j database with Racket db module’s functions.

1 Quick start

The following examples demonstrate how to connect to Neo4j and perform simple queries. The examples assume minimal familiarity with ‘db‘ and Neo4j.

Example:
> (require db db/neo4j)

First, we create a connection. Used without parameters, neo4j-connection connects to the default database without credentials.

Example:
> (define neo4j-c (neo4j-connect))

Use query-exec to create some nodes.

Example:
> (query-exec neo4j-c "CREATE (bob:Person {name: 'Bob'})")

Regular query can also be used. In this case the query statistics are returned, wrapped in simple-result struct.

Example:
> (query neo4j-c "CREATE (dave:Person {name: 'Dave'})")

(simple-result

 '#hasheq((constraints_added . 0)

          (constraints_removed . 0)

          (contains_system_updates . #f)

          (contains_updates . #t)

          (indexes_added . 0)

          (indexes_removed . 0)

          (labels_added . 1)

          (labels_removed . 0)

          (nodes_created . 1)

          (nodes_deleted . 0)

          (properties_set . 1)

          (relationship_deleted . 0)

          (relationships_created . 0)

          (system_updates . 0)))

The query string can include a number of Cypher statements.

Examples:
> (define queries (string-join (list
    "CREATE (john:Person {name: 'John'})"
    "CREATE (joe:Person {name: 'Joe'})"
    "CREATE (steve:Person {name: 'Steve'})"
    "CREATE (sara:Person {name: 'Sara'})"
    "CREATE (maria:Person {name: 'Maria'})"
    "CREATE (john)-[:FRIEND]->(joe)-[:FRIEND]->(steve)"
    "CREATE (john)-[:FRIEND]->(sara)-[:FRIEND]->(maria)")))
> (query neo4j-c queries)

(simple-result

 '#hasheq((constraints_added . 0)

          (constraints_removed . 0)

          (contains_system_updates . #f)

          (contains_updates . #t)

          (indexes_added . 0)

          (indexes_removed . 0)

          (labels_added . 5)

          (labels_removed . 0)

          (nodes_created . 5)

          (nodes_deleted . 0)

          (properties_set . 5)

          (relationship_deleted . 0)

          (relationships_created . 4)

          (system_updates . 0)))

The data can of course be queried.

Example:
> (query neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN john.name, fof.name")

(rows-result '("john.name" "fof.name") '(#("John" "Maria") #("John" "Steve")))

Other functions are also supported.

Examples:
> (query-rows neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN john.name, fof.name")

'(#("John" "Maria") #("John" "Steve"))

> (query-row neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN john.name, fof.name")

'#("John" "Steve")

> (query-list neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN fof.name")

'("Maria" "Steve")

> (query-value neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN fof.name")

"Steve"

> (query-maybe-value neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'B.*' RETURN fof.name")

#f

> (for ([(friend fof) (in-query neo4j-c "MATCH (john {name: 'John'})-[:FRIEND]->(friend)-[:FRIEND]->(fof) RETURN friend.name, fof.name")])
      (printf "John's friend ~a's friend is ~a\n" friend fof))

John's friend Sara's friend is Maria

John's friend Joe's friend is Steve

1.1 Parameters

Queries can include parameters. You can either provide a hash with all params or list their values in the order in which they appear in the query. Keys of the hash must be symbols. When the same parameter occurs more then one time in the query, you must use a hash parameter

Examples:
> (query-rows neo4j-c "MATCH (john {name: $name})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN john.name, fof.name" (hash 'name "John"))

'(#("John" "Steve"))

> (query-rows neo4j-c "MATCH (john {name: $name})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN john.name, fof.name" #hash((name . "John")))

'(#("John" "Steve"))

> (query-rows neo4j-c "MATCH (john {name: $name})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN john.name, fof.name" "John")

'(#("John" "Steve"))

1.2 Transactions

Transactions are supported but with two limitations: only managed transaction can be used (i.e. those created with start-transaction or call-with-transaction) and transaction cannot be nested.

Examples:
> (start-transaction neo4j-c)
> (query-exec neo4j-c "MATCH (bob:Person {name: 'Bob'}) MATCH (dave:Person {name: 'Dave'}) CREATE (dave)-[:FRIEND]->(bob)")
> (query-value neo4j-c "MATCH (dave:Person {name: 'Dave'})-[:FRIEND]->(who) RETURN who.name")

"Bob"

> (rollback-transaction neo4j-c)
> (query-rows neo4j-c "MATCH (dave:Person {name: 'Dave'})-[:FRIEND]->(who) RETURN who.name")

'()

1.3 Prepared statements

Prepared statemtents can be used, although they are handled completely at the client side, no resources are allocated at the server.

Examples:
> (define johns-fof
    (prepare neo4j-c "MATCH (john {name: $name})-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof.name =~ 'St.*' RETURN john.name, fof.name"))
> (query-rows neo4j-c johns-fof "John")

'(#("John" "Steve"))

> (define bound-stmt (bind-prepared-statement johns-fof '("John")))
> (query-rows neo4j-c bound-stmt)

'(#("John" "Steve"))

1.4 Unsupported features

For the obvious reason, list-tables and table-exists? are not supported.

> (list-tables neo4j-c)

list-tables: feature not supported

  feature: listing tables

> (table-exists? neo4j-c "a-table")

table-exists?: feature not supported

  feature: listing tables

When using in-query, #:fetch parameter must always be set to +inf.0. Otherwise db will expect a cursor to be returned from query, and cursors are not supported.

Examples:
> (for/list ([(john fof)
              (in-query
                neo4j-c
                "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN john.name, fof.name")])
    fof)

'("Maria" "Steve")

> (for/list ([(john fof)
              (in-query
                neo4j-c
                "MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN john.name, fof.name"
                #:fetch 1)])
    fof)

in-query: query did not return cursor

  statement: "MATCH (john {name:

'John'})-[:FRIEND]->()-[:FRIEND]->(fof) RETURN john.name,

fof.name"

2 Connecting to Neo4j

procedure

(neo4j-connect [#:server server    
  #:port port    
  #:database database    
  #:user user    
  #:password password    
  #:debug? debug?])  connection?
  server : string? = "localhost"
  port : exact-positive-integer? = 7474
  database : string? = "neo4j"
  user : (or/c string? #f) = #f
  password : (or/c string? #f) = #f
  debug? : boolean? = #f
Opens the connection to Neo4j. The returned connection can be used with db’s query functions.