# Rainbird Neo4j

![Build Status](https://codeship.com/projects/415e09a0-71a3-0132-22e9-028f765b4235/status?branch=master)

![Package Dependencies](https://david-dm.org/RainBirdAi/rainbird-neo4j.svg)

Thin wrapper around the [Neo4j Transactional Cypher HTTP][REST] REST endpoint
that adds the ability to perform client side substitutions in queries. It also
returns results in a slightly saner fashion than the raw endpoint.

`rainbird-neo4j` makes use of Neo4j `2.x` features and will not provide legacy
support for Neo4j `1.x`.

**Note:** This driver doesn't provide support to change passwords. You will need
to alter the default password using the web console before you can connect to an
instance that requires authentication.

## Installation

```bash
npm install --save rainbird-neo4j
```

## Basic Usage

```javascript
var Neo4j = require('rainbird-neo4j');
var db = new Neo4j('http://localhost:7474', username, password);

db.query('MATCH (n) RETURN n', function(err, results) {
    if (err) {
        console.log(err);
    } else {
        console.log(JSON.stringify(results, null, 4));
    }
});
```

If your Neo4j instance doesn't use authentication then `username` and `password`
can be omitted when creating the Neo4j object:

```
var db = new Neo4j('http://localhost:7474');
```

## Queries

All functions that can perform queries accept them in the same format. At its
most basic a query is just a query string. A query string can also be presented
as an array of strings. These will be concatenated into a single query string.

Providing a parameters object with the query string allows for queries with
parameters. See the [Neo4j documentation on parameters][parameters] for more
details on the _parameters object_.

Providing a substitutions object with the query string allows for
[substitutions](#substitutions) to be performed on the query string. **Note:**
If only one object is passed to a query it's assumed to be a parameters object.

Multiple queries can be run from a single function call by providing them as
_[statements](#statements)_. Statements do not support
[substitutions](#substitutions). Use the `[compose](#compose)` function if you
need a to perform substitutions on elements in a statements object.

## <a name="statements"></a>Statements

_Statements_ are an array of _statement objects_ which themselves contain a
`statement` property and a `parameters` property. `statement` is any valid
Cypher statement. `parameters` is a [parameters object][parameters].

## <a name="substitutions"></a>Client Side Parameters (Substitutions)

Client side parameters, or _substitutions_, are simply a convenience mechanism
that allow complex query strings to be reused within the code by use of
substitutions. _Substitutions_ do not provide the performance gains that normal
parameters give as they are parsed out before being sent to Neo4j.

Substitutions are defined in the query string using the format `${var}`. The
substitution will be replaced with the value of `var` in the _substitutions
object_.

For example, the following code:

```javascript
var template = `MATCH (:${foo} {value: {value}})`;
var substitutions = { 'foo': 'Baz'};
var parameters = { 'value': 'bar' };
neo4j.query(template, substitutions, parameters, callback);
```

Will pass the following statement object to Neo4J:

```JSON
[{
    "statement": "MATCH(:Baz {value: {value}})",
    "parameters": { "value": "bar" }
}]
```

**Warning:** _substitutions should be used with care as they can leave you open
to injection attacks. Always be sure you're passing in known strings._

## Transactions

Queries are always run within the context of a transaction. Where transactions
are not explicitly stated the queries will be wrapped in a _begin_ and _commit_.
Any errors will cause the transaction to _rollback_.

For transactions that span multiple function calls a call to `begin` will open
a transaction and return a transaction ID. This is then used to run multiple
queries before `commit` or `rollback` are called. Both `begin` and `commit` can
also run queries.

Transactions time out after a period of time. The timeout is reset each time a
call is made on that transaction. An empty query can be used to reset the
timeout.

## Callback

All functions that take a callback expect it to be in the form:

```javascript
callback(err, results, info)
```

If `err` is set then `results` will be an empty array.

### Results format

Results are returned as a list containing one element per query run. Each
individual result in this list is itself a list of rows returned. Each row is
an object containing the column names as properties and the row data as objects.

For example, if you run the following query as a single statement:

```
CREATE (foo:Foo {name: 'baz'})
SET foo.type = 'String'
CREATE (bar:Bar {name: 'baz'})
SET bar.type = 'String'
RETURN foo, bar
```

Will return the result:

```json
[
    [
        {
            "foo": {
                "name": "baz",
                "type": "String"
            },
            "bar": {
                "name": "baz",
                "type": "String"
            }
        }
    ]
]
```

If you run the two following queries as separate statements:

```
CREATE (foo:Foo {name: 'baz'}) RETURN foo
CREATE (bar:Bar {name: 'baz'}) RETURN bar
```

You will get the result:

```json
[
    [
        {
            "foo": {
                "name": "baz"
            }
        }
    ],
    [
        {
            "bar": {
                "name": "baz"
            }
        }
    ]
]
```

With the more complex query:

```
CREATE ({name: 'subject'})-[:R {name: 'relationship'}]->({name: 'object'})
MATCH (s)-[r]-(o) RETURN s, r, o
```

You will get the result:

```json
[
    [],
    [
        {
            "s": { "name": "object" },
            "r": { "name": "relationship" },
            "o": { "name": "subject" }
        },
        {
            "s": { "name": "subject" },
            "r": { "name": "relationship" },
            "o": { "name": "object" }
        }
    ]
]
```

### Info Format

The `info` object will contain any extra information returned by the function
and will vary depending on the exact context.

The `statements` parameter will always be set and will contain the
[statements](statements) sent to Neo4j.

The `errors` parameter will contain an array of any errors returned by Neo4j. If
no errors occurred then this list will be empty.

The `timeout` parameter is only set if `begin`, or `query` within the context of
a transaction has been called. It contains the datetime stamp when the
transaction will time out. See the [Neo4j REST][REST] documentation for more
details on the timeout.

The `transactionID` parameter is only set in the context of a transaction and
contains the current transaction ID. This is set by a call to `begin`.

## Functions

All functions that accept a `queryString` will also accept an array of query
strings. These will be concatenated into a single query and composed into a
statement. If a _substitutions_ object is provided then
[substitutions](#substitutions) will also be performed. If
[substitutions](#substitutions) are requires for [statements](#statements) the
`compose` function should be used.

### `begin`

Begin a transaction and optionally run a query in that transaction. The returned
transaction ID should be used for all future calls involving the transaction.

```javascript
begin(callback)
begin(queryString, callback)
begin(queryString, parameters, callback)
begin(queryString, substitutions, parameters, callback)
begin(statements, callback)
```

### `query`

Run a query, either as a single transaction, or part of a larger transaction.

```javascript
query(queryString, callback)
query(queryString, parameters, callback)
query(queryString, substitutions, parameters, callback)
query(statements, callback)
```

```javascript
query(transactionID, callback)
query(transactionID, queryString, callback)
query(transactionID, queryString, parameters, callback)
query(transactionID, queryString, substitutions, parameters, callback)
query(transactionID, statements, callback)
```

The signature `query(transactionID, callback)` is provided as a convenience
function to reset the timeout on a transaction. It will pass an empty set of
statements to Neo4j and is the equivalent of
`query(transactionId, [], callback)`

### `commit`

Commit an open transaction, optionally running a query before the transaction is
closed.

```javascript
commit(transactionID, callback)
commit(transactionID, queryString, callback)
commit(transactionID, queryString, parameters, callback)
commit(transactionID, queryString, substitutions, parameters, callback)
commit(transactionID, statements, callback)
```

### `rollback`

Rollback an existing transaction. `rollback` will always return an empty result
set.

```javascript
rollback(transactionID, callback)
```

### `resetTimeout`

Reset the timeout on a transaction without performing a query. Synonym for
`query(transactionID, callback)`.

```javascript
resetTimeout(transactionID, callback)
```

### <a name="compose"></a>`compose`

The `compose` function is a helper function that will construct a valid
_statement object_ for inclusion in an array of [statements](#statements).
It also allows for the use of [substitutions](#substitutions).

```javascript
compose(queryString, callback)
compose(queryString, parameters, callback)
compose(queryString, substitutions, parameters, callback)
```

### `escape`

Identifiers in Neo4j follow the following basic rules:

   * case sensitive
   * can contain underscores and alphanumeric characters (`[a-zA-Z0-9_]`)
   * must always start with a letter. (`[a-zA-Z]+[a-zA-Z0-9_]*`)

More complex identifiers can be quoted using backtick (`) characters.
Backticks themselves can be escaped using a backtick. Identifiers can be easily
escaped using:

```javascript
var identifier = Neo4j.escape('a complex identifier`);
```

## Testing

The package can be tested using:

```bash
npm test
```

This runs the linter, unit tests and creates the `docco` documentation along
with coverage reports and `plato` reports.

To perform full functional tests that connect to a test Neo4j instance that can
be cleared down after each test run:

```bash
export NEO4J_TEST_URL=http://localhost:4747
export NEO4J_TEST_USERNAME=neo4j
export NEO4J_TEST_PASSWORD=password
npm run-script functional-test
```

Replace `http://localhost:4747` and the authentication details with the URL and
login credentials of your test Neo4j instance.

**Note: the functional tests clear _everything_ in the test database. Please use
a stand alone test DB for functional testing.**

### Functional testing with Docker

The best method of providing a test Neo4j instance for functional testing is by
running a throwaway instance in Docker. To run:

```bash
docker run -i -t -d --name neo4j --privileged -p 7676:7474 tpires/neo4j
```

Here the port mapping has been changed from `7676` to `7474` which allows the
Docker instance to live on the same machine as a local instance. You can adjust
`7676` to point to any port, potentially running more than one Docker instance
by providing a different name to the `docker` command.

You can now expose this to the functional tests using:

```bash
export NEO4J_TEST_URL="http:${DOCKER_IP}:7676"
export NEO4J_TEST_USERNAME=neo4j
export NEO4J_TEST_PASSWORD=password
npm run-script functional-test
```

The Docker instance can now be stopped and deleted if it's no longer needed.

# Release Notes

## v0.4.3

  * [Misc] Updates dependencies and node version

## v0.4.2
  * [Misc] Updates dependencies to latest

## v0.4.1
  * [Misc] Minor code enhancement

## v0.4.0
  *  [New] Driver now supports authentication and Neo4j 2.2.

## v0.3.1
  * [Misc] Lock down node version in `package.json` to avoid problems in the 
           latest version of node and npm.

## v0.3.0
  * [Misc] Improve error messages for debugging without the `info` object.
  * [Misc] Errors do not occur at the same index as statements in the `info`
           object so removed this assertion from the README.
  * [Misc] Improve documentation for `compose`
  * [Misc] Add note about Neo4j 2.2 authentication

## v0.2.3
  * [Misc] Update package dependencies.
  * [Misc] Minor cleanup on the README.

## v0.2.2
  *  [Fix] Handle the case when a response is parsed without a body.
  *  [Fix] Neo4j URLs can now have trailing slashes

## v0.2.1
  * [Misc] Add warning about substitutions and injection attacks.

## v0.2.0

  *  [New] Transactions over multiple function calls using
           begin/commit/rollback.
  *  [New] The statements passed to Neo4j are now returned as part of the
           callback.
  *  [New] `buildStatement` and `escapeIdentifier` have been replaced with
           `compose` and `escape` respectively. `compose` is no longer an
           asynchronous function.
  *  [New] The error object passed to the callback no longer contains the
           `errors` and `statements` parameters. These now exist on the `info`
           object which is passed as the third parameter to the callback.
  * [Misc] Complete rewrite of the documentation.
  * [Misc] Internal changes to the way parameters are handled
  * [Note] **This version is a breaking change.**

## v0.1.5

  *  [Fix] Fix errors coming through as the string `[object Object]`
  * [Misc] Give access to the complete error object returned by Neo4j

## v0.1.4

  *  [Fix] `query` now consistently returns an `Error` object on error.
  * [Misc] The `Error` object returned by `query` has a `statements` property
           which contains the statements being sent to Neo4j.

## v0.1.3

  *  [New] When calling `buildStatement` with only two arguments the second
           argument is assumed to be the parameters object.

## v0.1.2

  *  [Fix] Errors are now correctly returned from Neo4j.

## v0.1.1

  * [Misc] Lock down version numbers of package dependencies.

## v0.1.0

  *  [Fix] Fixed the mapping behaviour when returning more than one variable so
           that all variables are mapped into the same object rather than
           multiple objects being returned.

## v0.0.4

  *  [Fix] `buildStatement` no longer needs Neo4J to be initialised.

## v0.0.3

  *  [Fix] `escapeIdentifier` no longer needs Neo4J to be initialised.

## v0.0.2

  *  [New] Add `escapeIdentifier` function.

## v0.0.1

  *  [New] Initial release.

# Licence

Copyright (c) 2014, Rainbird Technologies <follow@rainbird.ai>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

[REST]: http://neo4j.com/docs/stable/rest-api-transactional.html
[parameters]: http://neo4j.com/docs/stable/cypher-parameters.html
