A flexible and intuitive query builder for Neo4j and Cypher. Write queries in Javascript just as you would write them in Cypher.
let results = await db.matchNode('user', 'User', { active: true })
.where({ 'user.age': greaterThan(18) })
.with('user')
.create([
cypher.node('user', ''),
cypher.relation('out', '', 'HasVehicle'),
cypher.node('vehicle', 'Vehicle', { colour: 'red' })
])
.ret(['user', 'vehicle'])
.run();
// Results:
// [{
// user: {
// identity: 1234,
// labels: [ 'User' ],
// properties: { ... },
// },
// vehicle: {
// identity: 4321,
// labels: [ 'Vehicle' ],
// properties: { ... },
// },
// }]
npm install --save cypher-query-builder
or
yarn add cypher-query-builder
CommonJS/Node
const cypher = require('cypher-query-builder');
// cypher.Connection
// cypher.greaterThan
// ....
ES6
import { Connection, greaterThan } from 'cypher-query-builder';
const cypher = require('cypher-query-builder');
// Make sure to include the protocol in the hostname
let db = new cypher.Connection('bolt://localhost', {
username: 'root',
password: 'password',
});
Cypher query builder uses the official Neo4j Nodejs driver over the bolt protocol in the background so you can pass any values into connection that are accepted by that driver.
ES6
db.matchNode('projects', 'Project')
.return('projects')
.run()
.then(function (results) {
// Do something with results
});
ES2017
const results = await db.matchNode('projects', 'Project')
.return('projects')
.run();
run
will execute the query and return a promise. The results are in the
standardish Neo4j form an array of records:
const results = [
{
projects: {
// Internal Neo4j node id, don't rely on this to stay constant.
identity: 1,
// All labels attached to the node
labels: [ 'Project' ],
// Actual properties of the node.
// Note that Neo4j numbers will automatically be converted to
// Javascript numbers. This may cause issues because Neo4j can
// store larger numbers than can be represented in Javascript.
// This behaviour is currently in consideration and may change
// in the future.
properties: { name: 'Project 1' },
},
},
// ...
]
You can also use the stream
method to download the results as an observable.
const results = db.matchNode('project', 'Project')
.ret('project')
.stream();
results.subscribe(row => console.log(row.project.properties.name));
To extract the results, you can use ES5 array methods or a library like lodash:
// Get all the project nodes (including their id, labels and properties).
let projects = results.map(row => row.projects);
// Get just the properties of the nodes
let projectProps = results.map(row => row.projects.properties);
All the reference documentation can be found here. However, the two most useful pages are probably:
Please feel free to submit any bugs or questions you may have in an issue. I'm very open to discussing suggestions or new ideas so don't hesitate to reach out.
Maintaining the library does take some time out of my schedule so if you'd like to show your appreciation please consider donating. Even the smallest amount is really encouraging.
MIT License
Copyright (c) 2018 James Ferguson
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Between comparator for use in where clauses. This comparator uses Neo4j's
shortcut comparison syntax: 18 <= age <= 65
.
The lower
and upper
are the bounds of the comparison. You can use
lowerInclusive
and upperInclusive
to control whether it uses <=
or <
for the comparison. They both default to true
.
If you pass only lowerInclusive
then it will use that value for both.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: between(18, 65) })
// WHERE age >= 18 AND age <= 65
query.where({ age: between(18, 65, false) })
// WHERE age > 18 < AND age < 65
query.where({ age: between(18, 65, true, false) })
// WHERE age >= 18 AND age < 65
query.where({ age: between('lowerBound', 'upperBound', true, false, true) })
// WHERE age >= lowerBound AND age < upperBound
Contains comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ name: contains('steve') })
// WHERE name CONTAINS 'steve'
query.where({ name: contains('clientName', true) })
// WHERE name CONTAINS clientName
Ends with comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ name: endsWith('steve') })
// WHERE name ENDS WITH 'steve'
query.where({ name: endsWith('clientName', true) })
// WHERE name ENDS WITH clientName
Equals comparator for use in where clauses. This is the default so you will probably never need to use this.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: equals(18) })
// WHERE age = 18
query.where({ name: equals('clientName', true) })
// WHERE age = clientName
Exists comparator for use in where clauses. Note that this comparator does not accept any arguments
query.where({ person: exists() })
// WHERE exists(person)
Greater or equal to comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: greaterEqualTo(18) })
// WHERE age >= 18
query.where({ age: greaterEqualTo('clientAge', true) })
// WHERE age >= clientAge
Greater than comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: greaterThan(18) })
// WHERE age > 18
query.where({ age: greaterThan('clientAge', true) })
// WHERE age > clientAge
Has label comparator for use in where clauses.
query.where({ person: hasLabel('Manager') })
// WHERE person:Manager
In comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ name: inArray([ 'steve', 'william' ]) })
// WHERE name IN [ 'steve', 'william' ]
query.where({ name: inArray('clientNames', true) })
// WHERE name IN clientNames
Is null comparator for use in where clauses. Note that this comparator does not accept any arguments
query.where({ name: isNull() })
// WHERE name IS NULL
Less or equal to comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: lessEqualTo(18) })
// WHERE age <= 18
query.where({ age: lessEqualTo('clientAge', true) })
// WHERE age >= clientAge
Less than comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ age: lessThan(18) })
// WHERE age < 18
query.where({ age: lessThan('clientAge', true) })
// WHERE age < clientAge
Creates a node pattern like (parent:Person { name: 'Gwenn' })
.
All of the arguments are optional and most of the time you can supply only the ones you want, assuming you keep the order the same of course.
Use the following signatures as a reference:
node(conditions: Dictionary<any>)
node(labels: string[], conditions?: Dictionary<any>)
node(name: string, conditions?: Dictionary<any>)
node(name: string, labels?: string | string[], conditions?: Dictionary<any>)
Note that labels must be an array when it is the first argument.
Some examples
node()
// ()
node('parent')
// (parent)
node('parent', 'Person')
// (parent:Person)
node([ 'Person' ])
// (:Person)
node('parent', [ 'Person', 'Adult' ])
// (parent:Person:Adult)
node({ name: 'Gwenn' })
// ({ name: 'Gwenn' })
node('parent', { name: 'Gwenn' })
// (parent { name: 'Gwenn' })
node([ 'Person' ], { name: 'Gwenn' })
// (:Person { name: 'Gwenn' })
node('parent', 'Person', { name: 'Gwenn' })
// (parent:Person { name: 'Gwenn' })
For more details on node patterns see the cypher docs
A dictionary of conditions to attach to the node. These are stored as parameters so there is no need to worry about escaping.
An object representing the node pattern.
NOT
operator to use in where clauses.
query.where(not([
{ 'person.name': 'Steve' },
{ 'person.age': greaterThan(18) },
]));
// WHERE NOT (person.name = 'Steve' AND person.age > 18)
Note that this method only accepts an array of conditions.
OR
operator to use in where clauses. This is the default operator when
supplying an array to where so you will probably never need to use this
unless you'd like to make it explicit.
query.where(or([
{ 'person.name': 'Steve' },
{ 'person.age': greaterThan(18) },
]));
// WHERE person.name = 'Steve' OR person.age > 18
Note that this method only accepts an array of conditions.
Regexp comparator for use in where clauses. Also accepts a case insensitive
to make it easier to add the '(?i)'
flag to the start of your regexp.
If you are already using flags in your regexp, you should not set insensitive
to true because it will prepend '(?i)'
which will make your regexp
malformed.
For convenience you can also pass a Javascript RegExp object into this comparator, 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. For example, Javascript RegExp flags will not be preserved when sent to cypher.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ name: regexp('s.*e') })
// WHERE name =~ 's.*e'
query.where({ name: regexp('s.*e', true) })
// WHERE name =~ '(?i)s.*e'
query.where({ name: regexp('clientPattern', false, true) })
// WHERE name =~ clientPattern
Creates a relation pattern like -[rel:FriendsWith { active: true }]->
.
The only required argument is direction. All other arguments are optional and all combinations of them are valid. The only exception is that when labels is the first argument after direction, it must be an array, otherwise it will be interpreted as the relation name.
Some examples
relation('either')
// --
relation('out', 'rel')
// -[rel]->
relation('out', 'rel', 'FriendsWith')
// -[rel:FriendsWith]->
relation('in', [ 'FriendsWith', 'RelatedTo' ])
// <-[:FriendsWith|RelatedTo]-
// Note that this will match a relation with either the FriendsWith label or
// the RelatedTo label. You cannot use this syntax when creating relations.
relation('in', [4, 10])
// <-[*4..10]-
relation('in', { active: true })
// <-[{ active: true }]
relation('in', 'rel', { active: true })
// <-[rel { active: true }]-
relation('either', [ 'FriendsWith' ], { active: true })
// -[:FriendsWith { active: true }]-
relation('either', 'rel', 'FriendsWith', { active: true }, 3)
// -[rel:FriendsWith*3 { active: true }]-
relation('either', 'rel', 'FriendsWith', { active: true }, [ 3 ])
// -[rel:FriendsWith*3.. { active: true }]-
relation('either', 'rel', 'FriendsWith', { active: true }, [ 3, 5 ])
// -[rel:FriendsWith*3..5 { active: true }]-
relation('either', 'rel', 'FriendsWith', { active: true }, '*')
// -[rel:FriendsWith* { active: true }]-
For more details on relation patterns see the cypher docs.
Direction of the relation. in
means to the left, out
means to
the right and either
means no direction.
Length of the relation for flexible length paths. Can be the
string '*'
to represent any length, a single number 3
to represent the
maximum length of the path, or an array of two numbers which represent the
minimum and maximum length of the path. When passing an array, the second
number is optional, see the examples above.
An object representing the relation pattern.
Starts with comparator for use in where clauses.
If you want to compare against a Neo4j variable you can set variable
to
true and the value will be inserted literally into the query.
query.where({ name: startsWith('steve') })
// WHERE name STARTS WITH 'steve'
query.where({ name: startsWith('clientName', true) })
// WHERE name STARTS WITH clientName
XOR
operator to use in where clauses.
query.where(xor([
{ 'person.name': 'Steve' },
{ 'person.age': greaterThan(18) },
]));
// WHERE person.name = 'Steve' XOR person.age > 18
Note that this method only accepts an array of conditions.
Generated using TypeDoc
AND
operator to use in where clauses. This is the default operator when using conditions so you will probably never need to use this unless you'd like to make it explicit.query.where(and({ 'person.name': 'Steve', 'person.age': greaterThan(18), })); // WHERE person.name = 'Steve' AND person.age > 18
Note that this method only accepts a dictionary of conditions.