Creates a new connection to the database.
Url of the database such as 'bolt://localhost'
Auth can either be an object in the form { username: ..., password: ... }
, or a
Neo4j AuthToken object which contains the scheme
, principal
and credentials
properties
for more advanced authentication scenarios. The AuthToken object is what is passed directly to
the neo4j javascript driver so checkout their docs for more information on it.
Additional configuration options. If you provide a function instead of an
object, it will be used as the driver constructor. While passing a driver constructor function
here is not deprecated, it is the legacy way of setting it and you should prefer to pass an
options object with the driverConstructor
parameter.
Used to add an ON CREATE
clause to the query. Any following query will be prefixed with
ON CREATE
.
Example:
query.onCreate.setLabels({ node: 'Active' });
// ON CREATE SET node:Active
query.onCreate.setVariables({ 'node.createdAt': 'timestamp()' });
// ON CREATE SET node.createdAt = timestamp()
The only methods that are available after onCreate
are the set family of clauses.
Used to add an ON MATCH
clause to the query. Any following query will be prefixed with
ON MATCH
.
Example:
query.onMatch.setLabels({ node: 'Active' });
// ON MATCH SET node:Active
query.onMatch.setVariables({ 'node.updatedAt': 'timestamp()' });
// ON MATCH SET node.createdAt = timestamp()
The only methods that are available after onMatch
are the set family of clauses.
Closes this connection if it is open. Closed connections cannot be reopened.
Adds a create clause to the query.
Create accepts a single pattern, a list of patterns or a list of a list of
patterns. Each pattern represents a single part of a cypher pattern. For
example: (people:Person { age: 30 })
would be a node pattern and
-[:FriendsWith]->
would be a relationship pattern.
If an array of patterns is provided, they are joined together to form a composite pattern. For example:
query.create([
node('people', 'Person', { age: 30 }),
relation('out', '', 'FriendsWith'),
node('friend', 'Friend'),
])
Would equate to the cypher pattern
CREATE (people:Person { age: 30 })-[:FriendsWith]->(friend:Friend)
The create method also accepts a unique
option which will cause a CREATE UNIQUE
clause to
be emitted instead.
query.create([node('people', 'Person', { age: 30 })], { unique: true });
// CREATE UNIQUE (people:Person { age: 30 })
Shorthand for create(node(name, labels, conditions), options)
. For more details
the arguments see @{link node}.
Shorthand for create(patterns, { unique: true })
Shorthand for createNode(name, labels, conditions, { unique: true })
Adds a delete clause to the query.
Delete accepts a single string or an array of them and all of them are joined together with commas. Note that these strings are not escaped or passed to Neo4j using parameters, therefore you should not pass user input into this clause without escaping it first.
You can set detach: true
in the options to make it a DETACH DELETE
clause.
Shorthand for delete(terms, { detach: true })
.
Adds a limit clause to the query.
Adds a match clause to the query.
Match accepts a single pattern, a list of patterns or a list of a list of
patterns. Each pattern represents a single part of a cypher pattern. For
example: (people:Person { age: 30 })
would be a node pattern and
-[:FriendsWith]->
would be a relationship pattern.
If an array of patterns is provided, they are joined together to form a composite pattern. For example:
query.match([
node('people', 'Person', { age: 30 }),
relation('out', '', 'FriendsWith'),
node('friends'),
])
Would equate to the cypher pattern
MATCH (people:Person { age: 30 })-[:FriendsWith]->(friends)
If an array of an array of patterns is provided each array is joined together like above, and then each composite pattern is joined with a comma to allow matching of multiple distinct patterns. Note: matching many distinct patterns will produce a cross product of the results as noted in the cypher docs.
You can also provide optional: true
in the options to create and
OPTIONAL MATCH
clause.
List of patterns to be matched.
Shorthand for match(node(name, labels, conditions))
. For more details on
the arguments see node.
Adds a merge
clause to the query. It accepts the same parameters as match
and create
so refer to them
for more information.
query.merge([
node('user', 'User', { id: 1 }),
relation('out', 'rel', 'OwnsProject'),
node('project', 'Project', { id: 20 }),
])
.onMatch.setVariables({ 'rel.updatedAt': `timestamp` });
// MERGE (user:User { id: 1 })-[rel:OwnsProject]->(project:Project { id: 20 })
// ON MATCH SET rel.updatedAt = timestamp()
Shorthand for match(patterns, { optional: true })
.
Adds an order by to the query.
Pass a single string or an array of strings to order by.
query.orderBy([
'name',
'occupation',
])
// ORDER BY name, occupation
You can control the sort direction by adding a direction to each property.
query.orderBy([
['name', 'DESC'],
'occupation', // Same as ['occupation', 'ASC']
])
// ORDER BY name DESC, occupation
The second parameter is the default search direction for all properties that don't have a direction specified. So the above query could instead be written as:
query.orderBy([
'name',
['occupation', 'ASC']
], 'DESC')
// ORDER BY name DESC, occupation
It is also acceptable to pass an object where each key is the property and the value is a direction. Eg:
query.orderBy({
name: 'DESC',
occupation: 'ASC',
})
However, the underlying iteration order is not always guaranteed and it may cause subtle bugs in your code. It is still accepted but it is recommended that you use the array syntax above.
Valid values for directions are DESC
, DESCENDING
, ASC
, ASCENDING
.
true
and false
are also accepted (true
being the same as DESC
and
false
the same as ASC
), however they should be avoided as they are
quite ambiguous. Directions always default to ASC
as it does in cypher.
Returns a new query that uses this connection. The methods such as match
or create
are probably more useful to you as they automatically create a
new chainable query for you.
Adds a clause to the query as is. You can also provide an object of params as well.
query.raw('MATCH (:Event { date: $date }', { date: '2017-01-01' })
raw
can also be used as a template tag
query.matchNode('event', 'Event', { id: 1 })
.raw`SET event.finishedAt = ${Date.now()}`
But note that using template parameters where they are not supported in a query will produce an malformed query.
query.raw`SET node.${property} = 'value'`
// Invalid query:
// SET node.$param1 = 'value'
Adds a remove clause to the query.
Pass objects containing the list of properties and labels to remove from a node. Each key in an object is the name of a node and the values are the names of the labels and properties to remove. The values of each object can be either a single string, or an array of strings.
query.remove({
labels: {
coupon: 'Active',
},
properties: {
customer: ['inactive', 'new'],
},
});
// REMOVE coupon:Active, customer.inactive, customer.new
Both labels and properties objects are optional, but you must provide at least one of them for the query to be syntatically valid.
query.remove({
});
// Invalid query:
// REMOVE
If you only need to remove labels or properties, you may find removeProperties
or
removeLabels
more convenient.
Adds a remove clause to the query.
Pass an object containing the list of labels to remove from a node. Each key in the object is the name of a node and the values are the names of the labels to remove. The values can be either a single string, or an array of strings.
query.remove({
customer: ['Inactive', 'New'],
coupon: 'Available',
});
// REMOVE customer:Inactive, customer:New, coupon:Available
Adds a remove clause to the query.
Pass an object containing the list of properties to remove from a node. Each key in the object is the name of a node and the values are the names of the properties to remove. The values can be either a single string, or an array of strings.
query.remove({
customer: ['inactive', 'new'],
coupon: 'available',
});
// REMOVE customer.inactive, customer.new, coupon.available
Adds a return clause to the query.
There are many different ways to pass arguments to return
so each is
documented in turn below.
A single string:
query.return('people')
// RETURN people
An array of strings to return multiple variables:
query.return([ 'people', 'pets' ])
// RETURN people, pets
A single object to rename variables:
query.return({ people: 'employees' })
// RETURN people AS employees
A single object with an array for each value:
query.return({
people: [ 'name', 'age' ],
pets: [ 'name', 'breed' ],
})
// RETURN people.name, people.age, pets.name, pets.breed
This gives you a shortcut to specifying many node properties. You can also rename each property by adding an object inside the array or by providing an object as the value:
query.return({
people: [{ name: 'personName' }, 'age' ],
})
// RETURN people.name AS personName, people.age
or
query.return({
people: {
name: 'personName',
age: 'personAge',
},
})
// RETURN people.name AS personName, people.age AS personAge
You can also pass an array of any of the above methods.
The return method also accepts a distinct
option which will cause a RETURN DISTINCT
clause
to be emitted instead.
query.return('people', { distinct: true })
// RETURN DISTINCT people
Shorthand for `return(terms, { distinct: true });
Runs the provided query on this connection, regardless of which connection the query was created from. Each query is run on it's own session.
Run returns a promise that resolves to an array of records. Each key of the
record is the name of a variable that you specified in your RETURN
clause.
Eg:
connection.match([
node('steve', { name: 'Steve' }),
relation('out', [ 'FriendsWith' ]),
node('friends'),
])
.return([ 'steve', 'friends' ])
.run();
Would result in the value:
[
{
steve: { ... } // steve node,
friends: { ... } // first friend,
},
{
steve: { ... } // steve node,
friends: { ... } // second friend,
},
{
steve: { ... } // steve node,
friends: { ... } // third friend,
},
]
Notice how the steve record is returned for each row, this is how cypher
works. If you use lodash you can extract all of Steve's friends from the
results like using _.map(results, 'friends')
. If you don't, you can use
ES2015/ES6: results.map(record => record.friends)
.
If you use typescript you can use the type parameter to hint at the type of
the return value which is Dictionary<R>[]
.
Throws an exception if this connection is not open or there are no clauses in the query.
Opens and returns a session. You should never need to use this directly.
Your probably better off with run
instead.
Adds a set clause to the query.
set
lets you updates a nodes labels and properties in one clause. Most of
the time it will be easier to use one of the variants such as setLabels
,
setValues
or setVariables
.
This function accepts three different kind of properties, each of which is described in more detail in the variants.
query.set({
labels: {
sale: 'Active',
},
variables: {
sale: {
activatedAt: 'timestamp()',
},
},
values: {
sale: {
activatedBy: user.id,
},
},
})
// SET sale:Active, sale.activatedAt = timestamp(), sale.activatedBy = $userId
set
also accepts an options object which currently only contains a
single setting: override
. Override controls whether the =
or +=
operator is used in the set clause. true
causes the existing object to be
cleared and replaced by the new object. false
on the other hand will
merge the existing and new objects together, with new properties replacing
the ones on the existing object.
The default value of override is a little inconsistent and it will be
improved in the next major version. If you don't pass any settings object,
override will default to true
. If you pass an options object without an
override
key, override will be false
. In future versions, override will
always default to false
to be more consistent with setVariables
and
setValues
.
Adds labels to a node using a set clause.
query.setLabels({
sale: 'Active',
})
// SET sale:Active
setLabels
accepts a dictionary where the keys are nodes to be updated
and the value is a single label or an array of labels to add to the node.
Updates a node from parameters using a set
clause. This function treats all values as parameters which is different to
setVariables
which assumes values are cypher variables.
query.setValues({
'sale.activatedBy': user.id,
})
// SET sale.activatedBy += $userId
setValues
accepts a dictionary where the keys are nodes or property names
to be updated.
To use the +=
operator to merge properties of a node, you can pass
true
to the merge option.
query.setValues({
'sale': { active: true },
}, true)
// SET sale += $sale
Updates a node from a variable that was previously declared in the query
using a set
clause. This function only accepts strings as its values which are not
escaped in any way so beware. If you want to store some user supplied
information in the database, setValues
is the function you want.
query.setVariables({
'sale.activatedAt': 'timestamp()',
})
// SET sale.activatedAt = timestamp()
Note how values are inserted into the query, as is.
To use the +=
operator to merge properties of a node, you can pass
true
to the merge option.
query.setVariables({
'sale': 'newSaleDetails'
}, true)
// SET sale += newSaleDetails
Adds a skip clause to the query.
Runs the provided query on this connection, regardless of which connection the query was created from. Each query is run on it's own session.
Returns an RxJS observable that emits each record as it is received from the database. This is the most efficient way of working with very large datasets. Each record is an object where each key is the name of a variable that you specified in your return clause.
Eg:
const results$ = connection.match([
node('steve', { name: 'Steve' }),
relation('out', [ 'FriendsWith' ]),
node('friends'),
])
.return([ 'steve', 'friends' ])
.stream();
// Emits
// {
// steve: { ... } // steve node,
// friends: { ... } // first friend,
// },
// Then emits
// {
// steve: { ... } // steve node,
// friends: { ... } // first friend,
// },
// And so on
Notice how the steve record is returned for each row, this is how cypher works. You can extract all of steve's friends from the query by using operators:
const friends$ = results$.map(row => row.friends);
If you use typescript you can use the type parameter to hint at the type of
the return value which is Dictionary<R>
.
Throws an exception if this connection is not open or there are no clauses in the query.
The query is run when you call stream so you should subscribe to the results immediately to prevent missing any data.
Due to the way the Neo4j javascript driver works, once you call stream there is no way to stop the query until it is complete. Even if you unsubscribe from the observable, all the remaining rows will still be parsed by the driver but then immediately discarded.
const results$ = connection.matchNode('records')
.return('records')
.limit(1000) // 1000 records will be loaded and parsed from the database
.stream()
.take(10) // even though you only take the first 10
.subscribe(record => {});
In practice this should never happen unless you're doing some strange things.
Add a union clause to the query.
query.matchNode('people', 'People')
.return({ 'people.name': 'name' })
.union()
.matchNode('departments', 'Department')
.return({ 'departments.name': 'name' });
// MATCH (people:People)
// RETURN people.name AS name
// UNION
// MATCH (departments:Department)
// RETURN departments.name AS name
Add a union all clause to
the query. Just shorthand for union(true)
.
query.matchNode('people', 'People')
.return({ 'people.name': 'name' })
.unionAll()
.matchNode('departments', 'Department')
.return({ 'departments.name': 'name' });
// MATCH (people:People)
// RETURN people.name AS name
// UNION ALL
// MATCH (departments:Department)
// RETURN departments.name AS name
Adds an unwind clause to the query.
Any kind of array to unwind in the query
Name of the variable to use in the unwinding
Adds a where clause to the query.
where
is probably the most complex clause in this package because of the flexible ways to
combine conditions. A handy rule of thumb is when you see an array it becomes an OR
and when
you see a dictionary, it becomes an AND
. The many different ways of specifying your
constraints are listed below.
As a simple object, the comparison of each property is just AND
ed together.
query.where({
name: 'Alan',
age: 54,
})
// WHERE name = 'Alan' AND age = 54
You can wrap your constraints in a top level dictionary in which case the key of the outer dictionary will be considered the name of the node.
query.where({
person: {
name: 'Alan',
age: 54,
},
})
// WHERE person.name = 'Alan' AND person.age = 54
Using an array, you can generate OR
ed conditions.
query.where([
{ name: 'Alan' },
{ age: 54 },
])
// WHERE name = 'Alan' OR age = 54
Arrays can be placed at many levels in the conditions.
query.where({
name: [ 'Alan', 'Steve', 'Bob' ],
})
// WHERE name = 'Alan' OR name = 'Steve' OR name = 'Bob'
query.where({
person: [
{ name: 'Alan' },
{ age: 54 },
],
})
// WHERE person.name = 'Alan' OR person.age = 54
query.where([
{ employee: { name: 'Alan' } },
{ department: { code: 765 } },
})
// WHERE employee.name = 'Alan' OR department.code = 765
For more complex comparisons, you can use the comparator functions such as:
query.where({
age: greaterThan(30),
})
// WHERE age > 30
The full list of comparators currently supported are:
You can import the comparisons one at a time or all at once.
import { greaterThan, regexp } from 'cypher-query-builder';
// or
import { comparisons } form 'cypher-query-builder';
For convenience you can also pass a Javascript RegExp object as a value,
which will then be converted into a string before it is passed to cypher.
However, beware that the cypher regexp syntax is inherited from
java,
and may have slight differences to the Javascript syntax. If you would
prefer, you can use the regexp
comparator and use strings instead of
RegExp objects. For example, Javascript RegExp flags will not be
preserved when sent to cypher.
query.where({
name: /[A-Z].*son/,
})
// WHERE age =~ '[A-Z].*son'
All the binary operators including xor
and not
are available as well and can also be
imported individually or all at once.
import { xor, and } from 'cypher-query-builder';
// or
import { operators } form 'cypher-query-builder';
The operators can be placed at any level of the query.
query.where({
age: xor([lessThan(12), greaterThan(65)])
})
// WHERE age < 12 XOR age > 65
Adds a with clause to the query.
There are many different ways to pass arguments to with
so each is
documented in turn below.
A single string:
query.with('people')
// WITH people
An array of strings to return multiple variables:
query.with([ 'people', 'pets' ])
// WITH people, pets
A single object to rename variables:
query.with({ people: 'employees' })
// WITH people AS employees
A single object with an array for each value:
query.with({
people: [ 'name', 'age' ],
pets: [ 'name', 'breed' ],
})
// WITH people.name, people.age, pets.name, pets.breed
This gives you a shortcut to specifying many node properties. You can also rename each property by adding an object inside the array or by providing an object as the value:
query.with({
people: [{ name: 'personName' }, 'age' ],
})
// WITH people.name AS personName, people.age
or
query.with({
people: {
name: 'personName',
age: 'personAge',
},
})
// WITH people.name AS personName, people.age AS personAge
You can also pass an array of any of the above methods.
Generated using TypeDoc
The Connection class lets you access the Neo4j server and run queries against it. Under the hood, the Connection class uses the official Neo4j Nodejs driver which manages connection pooling on a session basis. It should be enough to have a single Connection instance per database per application.
To create the connection, simply call the constructor and pass in the database url, username and password.
const db = new Connection('bolt://localhost', { username: 'neo4j', password: 'password', })
To use the connection, just start calling any of the clause methods such as
match
,create
ormatchNode
etc. They automatically create a Query object that you can then chain other methods off of.db.matchNode('people', 'Person') .where({ 'people.age': greaterThan(18) }) .return('people') .run()
You can also pass a query to the run method, however, this is probably much less convenient.
db.run( new Query().matchNode('people', 'Person') .where({ 'people.age': greaterThan(18) }) .return('people') .run() );
Once you've finished with the connection you should close the connection.
db.close()
The library will attempt to clean up all connections when the process exits, but it is better to be explicit.