NEORM
Description
A very simple neo4j javascript orm
Installation
npm install -S @jekel18/neorm
Import
const { Sequence, Graph } = require('@jekel18/neorm)
Local build and test
npm run transpile
npm start
Usage
Sequence
The Sequence object is used to represent a cypher pattern. For example the following pattern:
MATCH (n:MOVIE) RETURN n
would be reprensent this way using the Sequence object
Sequence.node('Movie', 'n')
The node function allow two arguments: A descriptor and a tag. The tag is the name that will be use to identify the element inside the cypher query.
The descriptor can have multiple formats:
Sequence.node('Movie') // String representing a label
Sequence.node(['Movie', 'Person']) // Array of labels
const descriptor = {
labels: ['Person'],
born: 1954,
name: 'Bob'
};
Sequence.node(descriptor) // An object
To represent relationships, you can use the following method: relationship(descriptor, tag, direction).
Sequence.node('Person', 'person').relationship({labels: ['ACTED_IN'], 'rel', INCOMING).node('Movie', 'movie').relationship({labels: ['REVIEWED_BY'], 'rel', INCOMING).node('Person')
This would result in the following cypher
MATCH (person:Person)-[ACTED_IN]->(movie:Movie)<-[REVIEWD_BY]-(Person) RETURN person, movie
- There is no limitation to the number of chained methods
- Notice the how the tag arguments are used for the return statement of the cypher query
- If the last method of the chain is a relationship, the pattern will be padded with an empty node
Conditional statement
A syntax similar to Mongoose and Sequelize can be used in the descriptor
const theMatrix = {
title: { $or: ['The Matrix', 'The Matrix Reloaded']}
};
const actor = {
labels: ['Person'],
born: { $gte: 1960 }
};
const seq = Sequence.node(theMatrix, 'movies').relationship({labels: ['ACTED_IN'], 'rel', INCOMING).node(actor, 'actors');
Some operators can be used together:
// Find the actors born betweem 1950-1960 or 1970-1980
const theMatrix = {
born: { $or: [
{ $and: [ { $gte: 1950 }, { $lte: 1960 } ] },
{ $and: [ { $gte: 1970 }, { $lte: 1980 } ] },
]}
};
Those are the supported operator so far:
- $or
- $and
- $lt
- $lte
- $gt
- $gte
##Graph
The Graph object is used to interact with the database.
Graph.connect('bolt://localhost', 'neo4j', 'mo49xw71');
####find var rel_def = { my_prop1: { $eq: 10}, my_array: { $eq: ['hi', 'hello'] }, labels: ['ACTED_IN'] };
Find node or relationship using a Sequence (note that the valid directions are ANY, INCOMING, OUTGOING)
const seq = Sequence.node(theMatrix, 'movies').relationship(rel_def, 'rel', INCOMING).node(actor, 'actors');
Graph
.find(seq)
.then(({ movies, actors }) => {
// I am not sure this is the best way to return data, maybe return the row instean
})
.catch(ex => {})
You can also use other conditions on the query
Graph
.find(seq)
.limit(3)
.skip(2)
.sort('actors.name')
.then(({ movies }) => {});
###findOne To find only one node
Graph
.findOne(Sequence.node({ id: 21 }, 'cuba'))
.then(({ cuba }) => Array.isArray(cuba)) // false
###createNode To create a new node
const Bill = {
labels: ['Person'],
born: 1986,
name: 'Bill'
};
Graph.createNode(Bill)
.then(console.log);
##The Node Object Each call to createNode, findOne or find return a node proxy around the original driver implementation https://neo4j.com/docs/api/javascript-driver/current/class/src/v1/graph-types.js~Node.html
The proxy has the following properties:
- to access id: node.id
- to access labels: node.labels
- to access any other properties: node.property_name
- to access the original object: node._target
Also all the value of type Integer are converted on access
The proxy also have a save function to update the node
const Bill = {
labels: ['Person'],
born: 1986,
name: 'Bill'
};
Graph.createNode(Bill)
.then(bill => {
bill.lastName = 'bob';
return bill.save() // Update the node in the DB
})
.then(newBill => {
console.log(newBill.lastName) // bob
});
Furthermore, the proxy has a "del()" method that enables you to delete the proxied node or relationship. Nodes also have a "addRelation(data, direction, label, other_node, properties = {})" method to add relationships:
const seq = Sequence.node(theMatrix, 'movies').relationship({labels: ['ACTED_IN'], 'rel', INCOMING).node(
{
labels: ['Person'],
},
'actors'
);
Graph
.find(seq)
.then(({ actors }) => {
var actor1 = actors[0];
var actor2 = actors[1];
actor1.addRelation(OUTGOING, 'LOVES', actor2);
actor1.addRelation(INCOMING, 'HATES', actor2);
console.log('done');
});
The node object also has a getRelation(data, tag, direction, label, properties = {}) method which allows you to fetch relationships from an existing node: var seq = Sequence.node(theMatrix, 'movies').relationship({labels: ['ACTED_IN']}).node( { labels: ['Person'], }, 'actors' );
var rel_def = {
roles: { $eq : ["Neo"] },
};
Graph
.find(seq)
.then(({ actors }) => {
var actor1 = actors[0];
actor1.getRelation('role', OUTGOING, 'ACTED_IN', rel_def)
.then(({ role }) => {
console.log(role);
});
});