fluent-cypher
v0.9.2
Published
Builds parametrized Cypher queries, with a fluent API
Downloads
21
Maintainers
Readme
Fluent Cypher
This package allows you to build any cypher query you like and get both the query string and the parameters as an object to be used with the official neo4j driver.
If you want to be able to connect seamlessy to your Neo4j instance have a look at fluent-neo4j otherwise you can always use this package with your own driver/connector.
What is Cypher
This guide explains the basic concepts of Cypher, Neo4j’s query language.
Following the official documentation it is always better to avoid literals so everything is treated as a parameter.
Table of Contents
Usage
const CypherQuery = require('fluent-cypher');
//or
import CypherQuery from 'fluent-cypher'
var query = new CypherQuery();
query.match({$: 'node'}, ['code', {type: 'KNOWS', direction: 'left'}, {}])
.where({$: 'node', value: {'<=': 25}}, 'OR', {$: 'node', value: 28})
.return({$: 'node', as: 'myValue')
.orderBy('myValue')
.limit(5)
/*
query.log() =>
MATCH (node), (code)<-[]-()
WHERE node.value <= 25 OR node.value = 28
RETURN node AS myValue
ORDER BY myValue
LIMIT 5
query.queryString => "MATCH (node), (code)<-[]-() WHERE node.value <= {value1} OR node.value = {value2} RETURN node AS myValue ORDER BY myValue LIMIT 5"
query.queryParams => {value1: 25, value2: 28}
*/
constuctor([config])
| Option | Type | Description
| ------------- |:-------------:| :-----|
| onCreateSetTimestamp
| Boolean
| timestamps will be added for you like node.createdAt = timestamp()
|
| onUpdateSetTimestamp
| Boolean
| timestamps will be added for you like node.updatedAt = timestamp()
|
| userId
| String
| Property will be set like node.createdBy = {userId}
and node.updatedBy = {userId}
| defaultNodeProps
| Object
| default props for every node
| forcetNodeProps
| Object
| force props for every node
| defaultRelProps
| Object
| default props for every relationship
| forcetRelProps
| Object
| force props for every relationship
Building the query
.create(Pattern[, Pattern])
See Pattern for accepted arguments
query.create("(node)", "()->[rel]->()") //CREATE (node), ()->[rel]->()
query.create({$: 'node1', prop: false}, {$: 'node2', val: 12}) //CREATE (node1{prop:false}), (node2{val:12})
query.create([{$: 'parent'}, {type: 'has'}, {$: 'child'}]) // 'CREATE (parent)-[:has]->(child)'
.match(Pattern[, Pattern])
See Pattern for accepted arguments
query.match("(node)") // MATCH (node)
query.match("(node)", "()->[rel]->()") // MATCH (node), ()->[rel]->()
query.match({$: 'node1', prop: false}, {$: 'node2', val: 12}) //MATCH (node1{prop:false}), (node2{val:12})
query.match([{$: 'parent'}, {type: 'has'}, {$: 'child'}]) // 'MATCH (parent)-[:has]->(child)'
.optionalMatch(Pattern[, Pattern])
See Pattern for accepted arguments
query.optionalMatch("(node:Stuff)") // MATCH OPTIONAL (node:Stuff)
.where(WhereItem[, WhereItem])
query.where({$: 'user', fullName: {'=~': `(?i).*tom.*`}})
// WHERE user.fullName =~ (?i).*tom.*
.merge(Pattern[, Pattern])
See Pattern for accepted arguments
query.merge("(node)") // MERGE (node)
query.merge("(node)", "()->[rel:`type`]->()") // MERGE (node), ()->[rel:`type`]->()
.set(PropItem[, PropItem])
query.set('friend.rating = 5') // SET friend.rating = 5
query.set({
$: 'friend',
labels: ['lol', 'lel'],
wow: '$rating' // <= access the variable with $
}) // SET friend:lol:lel, friend.wow = rating
.onCreateSet(PropItem[, PropItem])
query.onCreateSet('friend.rating = 5') // ON CREATE SET friend.rating = 5
.onMatchSet(PropItem[, PropItem])
query.onCreateSet('friend.rating = 5') // ON MATCH SET friend.rating = 5
.remove(PropItem[, PropItem])
query.remove({
$: 'p',
prop: 't',
props: ['lel', 'lol'],
label: 'one',
labels: ['may', 'april']
})
// REMOVE p:one:may:april, p.t, p.lel, p.lol
.delete(DeleteItem[, DeleteItem])
query
.match({$: 'lonely'})
.where('NOT', ['lonely', {type: 'has'}, {label: 'Friend'}])
.delete({$: 'lonely'})
/*
MATCH (lonely)
WHERE NOT (lonely)-[:has]->(:Friend)
DELETE friend
*/
.detachDelete(DeleteItem[, DeleteItem])
query
.match(['me', ':knows', {$: 'friend'})
.detachDelete('friend')
/*
MATCH (me)-[:knows]->(friend)
DETACH DELETE friend
*/
.return(ReturnItem[, ReturnItem])
query.return('*') // RETURN *
query.return('node') // RETURN node
query.return('node.prop') // RETURN node.prop
query.return({$: 'node', prop: 'p', as: 'that'}) // RETURN node.p as that
.returnDistinct(ReturnItem[, ReturnItem])
query.returnDistinct('*') // RETURN DISTINCT *
.limit(Integer)
query.limit(1) // LIMIT 1
.skip(Integer)
query.skip(1) // LIMIT 1
.orderBy(Integer)
query.orderBy({$: 'node', key: 'ASC'}) // ORDER BY node.key ASC
.unwind(UnwindItem)
query.unwind(['[1,2,3] as number') //UNWIND [1,2,3] as number
query.unwind({$: [1,2,3], as: 'number'}) //UNWIND [1,2,3] as number (parameterized)
query.unwind({$: 'collection', as: 'list'}) //UNWIND collection as list
query.unwind({$: '$param', as: 'entry'}) //UNWIND $param as entry
.with(AliasedItem[, AliasedItem])
query.with('this as that', {$: 'node', as: 'something'})
//WITH this as that, node AS something
.union()
query.union()
//UNION
.unionAll()
query.unionAll()
//UNION ALL
.loadCsv(url, options)
q.loadCsv('https://neo4j.com/docs/cypher-refcard/3.2/csv/artists.csv', {as: 'row', withHeaders: false})
//LOAD CSV FROM "https://neo4j.com/docs/cypher-refcard/3.2/csv/artists.csv" AS row
.call(string)
q.call('dbms.procedures()')
//CALL dbms.procedures()
.yield(string)
q.yield('name, signature')
//YIELD name, signature
Argument Types
Pattern
As String see Cypher
As Object see Node
As Array see Path
Cypher
String only
This is not manipulated at all and gets inserted in the context as is
'node' //(node) if in node context
'rel:type' //...-[rel:type]->... if in rel context
'CASE WHEN 1=1 THEN this ELSE that END as what' //CASE WHEN 1=1 THEN this ELSE that END as what
Node
As String
see Cypher
As Object
|Key | Required | Type | Description |
| --- |:----------:| -------|---------------|
|$
| no | String | Variable name for node (must be valid variable name) |
|label
| no | String | Label for node |
|labels
| no | Array | Label for node |
|...rest
| no | String|Arrray | Other properties for the node |
{
$: 'node',
label: 'Cat',
labels: ['Animal', 'Living'],
this: 'that',
something: ['li', 'la']
} //(node:Cat:Animal:Living)
Rel
As String
Interpreted as Cypher
As Object
Props
|Key | Required | Type | Description |
| --- |:----------:| -------|---------------|
|$
| no | String
| Variable name for rel (must be valid variable name) |
|type
| yes (in merge) | String
| Type of rel |
|depth
| no | Integer|String
| Eiter *
or 4
or 1..2
|
|direction
| no | String
| Eiter left
or right
(default) or both
|
|...rest
| no | String|Arrray
| Actual properties of the rel |
Example
{
$: 'rel',
type: 'Follows',
depth: '..5',
direction: 'both',
something: ['amigo']
} //...)-(rel:Follows*..5{something:['amigo']})-(...
Path
As Array
If the number of elements is even, the first Object is used for Path options
Props
|Key | Required | Type | Description |
| --- |:----------:| -------|---------------|
|$
| no | String | Variable name for path (must be valid variable name) |
|shotestPath
| no | Bool | Whether to use the shortestPath or not |
Example
[
{ $: 'myPath', shotestPath: true },
{ $: 'start' },
{},
'final'
] // myPath = shortestPath((start)-[]->(final))
.log()
As query.queryString
is a parametrised string you may want to print a string that you can copy and paste in the browser console.
query
.match('(node)')
.log() // => MATCH (node)
.match('()-[rel]->()')
.log() // => MATCH (node) MATCH ()-[rel]->()
Test
Tests are written in ava, run the following command
npm t