!Please always remove autopublish
package before using this package!
- About
- Example Application
- Installation
- Notes
- API
- More about reactive data
- Usage examples
- Test on your Dev stage
- Get deeper to understanding the package
Neo4j DB reactive layer for Meteor
Neo4jreactivity creates reactive and isomorphic layer between Neo4j and your Meteor based application. All write requests is synchronized between all clients. Please see this package on atmospherejs.com.
Example Application
The basic example is build on top of --example leaderboard
- the Meteor's Neo4j-based Leaderboard App
Install the driver
meteor add ostrio:neo4jreactivity
Several Notes
TTL
If you have many different queries to Neo4j database on production environment, you will probably want to avoid Neo4jCache
collection overwhelming. Make build-in JavaScript-based TTL utility is useless, so we are suggest to take a look on TTL indexes and expire data tutorial. Neo4jCache
records has created
{Date} field, so in our case it will be something like:
1/* run this at mongodb shell */ 2db.Neo4jCache.createIndex({ 3 created: 1 4},{ 5 expireAfterSeconds: 3600 * 24 /* 3600 * 24 = 1 day */ 6});
The way to work with queries
In documentation below you will find two different approaches how to send queries and retrieve data to/from Neo4j database. It is methods
/calls
and collection
/publish
/subscription
.
It is okay to combine them both. Most advanced way is to use methods
/calls
, - using this approach allows to you send and retrieve data directly to/from Neo4j database, our driver will only hold reactive updates on all clients.
But at the same moment collection
/publish
/subscription
approach has latency compensation and let to work with data and requests as with minimongo instance, but limited to simple insert
/update
/remove
operations on data sets, so you can't set relations, indexes, predicates and other Cypher query options (Labels and Properties is well supported. For Labels use __labels
property as {__labels: ":First:Second:Third"}
).
API
Isomorphic
Meteor.neo4j.allowClientQuery
allowClientQuery
{Boolean} - Allow/Deny Cypher queries execution on the client side
Meteor.neo4j.connectionURL = 'http://user:pass@localhost:7474';
- Set connection URL, uncluding login and password to Neo4j DataBase
- Example
Meteor.neo4j.rules.write
- Array of strings with Cypher write operatorsMeteor.neo4j.rules.read
- Array of strings with Cypher read operatorsMeteor.neo4j.set.allow([rules])
- Set allowed Cypher operators for client side
rules
{[String]} - Array of Cyphper query operators Strings
Meteor.neo4j.set.deny([rules])
- Set denied Cypher operators for client side
rules
{[String]} - Array of Cyphper query operators Strings- For example to deny all write queries, use:
Meteor.neo4j.set.deny(Meteor.neo4j.rules.write)
- Example
Meteor.neo4j.query(query, opts, callback)
- Returns reactive {Object} withget()
method.
query
{String} - Name of publish function. Please use same name in collection/publish/subscriptionopts
{Object} - A map of parameters for the Cypher query.callback
{Function} - Callback which runs after each subscriptionerror
{Object|null} - Error of Neo4j Cypher query execution or nulldata
{Object|null} - Data or null from Neo4j Cypher query execution
- Example
Meteor.neo4j.collection(name)
name
{String} - Name of collection.
1users = Meteor.neo4j.collection 'Users'
- This method returns collection with next methods:
publish(name, func, [onSubscribe])
[Server] - Publish dataset to client.name
{String} - Publish/Subscription namefunc
{Function} - Function which returns Cypher queryonSubscibe
{Function} - Callback function called right after data is published- Example
1users.publish 'currentUser', () -> 2 return 'MATCH (user:User {_id: {_id}}) RETURN user;'
subscribe(name, [opts], link)
[Client] - Subscribe on dataset.name
{String} - Publish/Subscription nameopts
{Object|null} - A map of parameters for the Cypher querylink
{String} - Sub object name, to link as MobgoDB row(s). See example below:- Example
1users.subscribe 'currentUser', _id: Meteor.userId(), 'user'
find([selector], [options])
- Example. Use to search thru returned data from Neo4jfetch()
- Use to fetch Cursor data
findOne([selector], [options])
insert(doc, [callback])
- Exampleupdate(selector, modifier, [options], [callback])
- Exampleupsert(selector, modifier, [options], [callback])
remove(selector, [callback])
- Example- Note: All
selector
s anddoc
support__labels
property, - use it to set Cypher label on insert or searching data, see this example - Collection() example
Meteor.neo4j.call(name, [[opts], [link].. ], callback)
- Call server Neo4j method
Call for method registered via Meteor.neo4j.methods
.
name
{String} - Name of method functionopts
{Object} - A map of parameters for the Cypher query.callback
{Function} - Returnserror
anddata
arguments.- Returns {Object} - With
cursor
and reactiveget()
method - Example
Server
Meteor.neo4j.methods(object)
- Create server Cypher queries
object
{Object} - Object of method functions, which returns Cypher query string- Example
Meteor.neo4j.publish(collectionName, name, func, [onSubscribe])
collectionName
{String} - Collection name of method functionname
{String} - Name of publish function. Please use same name in publish/subscriptionfunc
{Function} - Function wich returns Cypher query stringonSubscribe
{Function} - Callback which runs after each subscription- Example
Client
Meteor.neo4j.subscribe(collectionName, name, [opts], [link])
collectionName
{String} - Collection name of method functionname
{String} - Name of subscribe function. Please use same name in publish/subscriptionopts
{Object} - A map of parameters for the Cypher query.link
{String} - Sub object name, to link as MobgoDB row(s)- Example
- Note: Wrap
Meteor.neo4j.subscribe()
intoTracker.autorun()
Predefined Cypher Operators:
- Allow:
RETURN
MATCH
SKIP
LIMIT
OPTIONAL
ORDER BY
WITH
AS
WHERE
CONSTRAINT
UNWIND
DISTINCT
CASE
WHEN
THEN
ELSE
END
CREATE
UNIQUE
MERGE
SET
DELETE
REMOVE
FOREACH
ON
INDEX
USING
DROP
-
Deny: None
-
Write:
CREATE
SET
DELETE
REMOVE
INDEX
DROP
MERGE
About reactive data and queries
Note: This is very important to use same node's link names for same node types in all Cypher queries, cause the way Neo4jReactivity subscribes on data. For example if we would like to retrieve Users from Neo4j and update them later, so data will be updated reactively:
MATCH (usr {type: 'User'}) RETURN usr # To update use only `usr` alias for node: MATCH (usr {type: 'User', perms: 'guest'}) SET usr.something = 2
Of course Neo4jReactivity knows about Neo4j labels and use them for subscription too. With labels you may use different node's name aliases, but it's not recommended:
# To retrieve MATCH (a:User) RETURN a # To update: MATCH (b:User {perms: 'guest'}) SET b.something = 2
It will work, but much better if you will use:
# To retrieve MATCH (user:User) RETURN user # To update: MATCH (user:User {perms: 'guest'}) SET user.something = 2
Usage examples:
As collection and publish/subscribe
Create collection [Isomorphic]
1friends = Meteor.neo4j.collection 'friends'
Publish data [Server]
1friends.publish 'allFriends', () -> 2 return "MATCH (user {_id: {userId}})-[:FriendOf]->(friends) RETURN friends"
Subscribe on this data [Client]
1friends.subscribe 'allFriends', {userId: Meteor.userId()}, 'friends'
Template helper [Client]
1Template.friendsNamesList.helpers 2 friends: ()-> 3 friends.find({})
In Template:
1<template name="friendsNamesList"> 2 <ul> 3 {{#each friends}} 4 <li>{{name}}</li> 5 {{/each}} 6 </ul> 7</template>
As methods/call
In Server Methods
1#CoffeeScript 2Meteor.neo4j.methods 3 getUsersFriends: () -> 4 return "MATCH (user {_id: {userId}})-[:FriendOf]->(friends) RETURN friends"
In Helper
1#CoffeeScript 2Template.friendsNamesList.helpers 3 userFriends: () -> 4 Meteor.neo4j.call 'getUsersFriends', {userId: Meteor.userId()}, (error, data) -> 5 throw new Meteor.error '500', 'Something goes wrong here', error.toString() if error 6 else 7 Session.set 'currenUserFriends', data 8 return Session.get 'currentUserFriens'
In Template:
1<template name="friendsNamesList"> 2 <ul> 3 {{#each userFriends.friends}} 4 <li>{{name}}</li> 5 {{/each}} 6 </ul> 7</template>
About security
By default query execution is allowed only on server, but for development purpose (or any other), you may enable it on client:
1#Write this line in /lib/ directory to execute this code on both client and server side 2Meteor.neo4j.allowClientQuery = true 3#Do not forget about minimum security, deny all write queries 4Meteor.neo4j.set.deny Meteor.neo4j.rules.write
To allow or deny actions use neo4j.set.allow(['array of strings'])
and neo4j.set.deny(['array of strings'])
1#CoffeeScript 2Meteor.neo4j.set.allow ['create', 'Remove'] 3Meteor.neo4j.set.deny ['SKIP', 'LIMIT'] 4 5#OR to allow or deny all 6Meteor.neo4j.set.allow '*' 7Meteor.neo4j.set.deny '*' 8 9#To deny all write operators 10Meteor.neo4j.set.deny Meteor.neo4j.rules.write 11 12#default rules 13Meteor.neo4j.rules = 14 allow: ['RETURN', 'MATCH', 'SKIP', 'LIMIT', 'OPTIONAL', 'ORDER BY', 'WITH', 'AS', 'WHERE', 'CONSTRAINT', 'UNWIND', 'DISTINCT', 'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'CREATE', 'UNIQUE', 'MERGE', 'SET', 'DELETE', 'REMOVE', 'FOREACH', 'ON', 'INDEX', 'USING', 'DROP'] 15 deny: []
Execute query on client side:
1#Write this line in /lib/ directory to execute this code on both client and server side 2Meteor.neo4j.allowClientQuery = true 3 4#Client code 5getAllUsers = -> 6 return Meteor.neo4j.query('MATCH (a:User) RETURN a').get();
For more info see: neo4jdriver and node-neo4j
Code licensed under Apache v. 2.0: node-neo4j License
Testing & Dev usage
Local usage
- Download (or clone) to local dir
- Stop meteor if running
- Run
mrt link-package [*full path to folder with package*]
in a project dir - Then run
meteor add ostrio:neo4jreactivity
- Run
meteor
in a project dir - From now any changes in ostrio:neo4jreactivity package folder will cause rebuilding of project app
Understanding the package
After installing ostrio:neo4jreactivity
package - you will have next variables:
Meteor.Neo4j;
- [Server] GraphDatabase object from node-neo4j npm package. Use to connect to other Neo4j servers.Meteor.N4JDB;
- [Server] GraphDatabase instance connected to Neo4j server. Use to run Cypher queries directly in Neo4j DB, without any reactivityMeteor.neo4j;
- [Isomorphic] Neo4jReactivity Driver object
Meteor.Neo4j;
1/* 2 * Server only 3 * @class 4 * @name Neo4j 5 * @param url {string} - URL to Neo4j database 6 * Note: It’s better to store URL in environment 7 * variable, 'NEO4J_URL' or 'GRAPHENEDB_URL' - 8 * so it will be automatically picked up by our driver 9 * 10 * @description Run it to create connection to database 11 */ 12Meteor.N4JDB = new Meteor.Neo4j(/* URL TO SERVER */);
Newly created object has next functions, you will use:
1/* @name query */ 2Meteor.N4JDB.query('MATCH (n:User) RETURN n', null /* A map of parameters for the Cypher query */, function(err, data){ 3 Session.set('allUsers', data); 4}); 5 6/* @name listen */ 7Meteor.N4JDB.listen(function(query, opts){ 8 console.log('Incoming request to neo4j database detected!'); 9});
Meteor.neo4j;
1/* Both (Client and Server) 2 * @object 3 * @name neo4j 4 * @description Application wide object neo4j 5 */ 6Meteor.neo4j; 7Meteor.neo4j.allowClientQuery = true; /* Allow/deny client query executions */ 8Meteor.neo4j.connectionURL = null; /* Set custom connection URL to Neo4j DB, Note: It’s better to store URL in environment variable, 'NEO4J_URL' or 'GRAPHENEDB_URL' - so it will be automatically picked up by the driver */
neo4j
object has multiple functions, you will use:
1/* @namespace Meteor.neo4j.set 2 * @name allow 3 * @param rules {array} - Array of Cypher operators to be allowed in app 4 */ 5Meteor.neo4j.set.allow(rules /* array of strings */); 6 7/* @namespace Meteor.neo4j.set 8 * @name deny 9 * @param rules {array} - Array of Cypher operators to be forbidden in app 10 */ 11Meteor.neo4j.set.deny(rules /* array of strings */); 12 13 14/* 15 * @function 16 * @namespace neo4j 17 * @name query 18 * @param query {string} - Cypher query 19 * @param opts {object} - A map of parameters for the Cypher query 20 * @param callback {function} - Callback function(error, data){...}. Where is data is [REACTIVE DATA SOURCE] 21 * So to get data for query like: 22 * 'MATCH (a:User) RETURN a', you will need to: 23 * data.a 24 * @param settings {object} - {returnCursor: boolean} if set to true, returns Mongo\Cursor 25 * @description Isomorphic Cypher query call 26 * @returns Mongo\Cursor or ReactiveVar [REACTIVE DATA SOURCE] 27 * 28 * @note Please keep in mind what on client it returns ReactiveVar, but on server it returns just data, see difference in usage at example below 29 * 30 */ 31allUsers = Meteor.neo4j.query('MATCH (users:User) RETURN users'); 32var users = allUsers.get().users; 33 34/* or via callback, on callback there is no need to run `get()` method */ 35var users; 36Meteor.neo4j.query('MATCH (users:User) RETURN users', null, function(error, data){ 37 users = data.users; 38}); 39 40 41/* 42 * Server only 43 * @name methods 44 * @param methods {object} - Object of methods, like: { methodName: function(){ return 'MATCH (a:User {name: {userName}}) RETURN a' } } 45 * @description Create server methods to send query to neo4j database 46 */ 47Meteor.neo4j.methods({ 48 'GetAllUsers': function(){ 49 return 'MATCH (users:User) RETURN users'; 50 } 51}); 52 53 54/* 55 * Client only 56 * @name call 57 * @description Call for server method registered via neo4j.methods() method, 58 * returns error, data via callback. 59 */ 60Meteor.neo4j.call('GetAllUsers', null, function(error, data){ 61 Session.set('AllUsers', data.users); 62});
Meteor.N4JDB;
1/* 2 * Server only 3 * @description Current GraphDatabase connection object, basically created from 'new Neo4j()'' 4 */ 5Meteor.N4JDB; 6 7 8/* You may run queries with no returns on server with it: */ 9Meteor.N4JDB.query('CREATE (a:User {_id: ”123”})'); 10 11 12/* To set listener: */ 13Meteor.N4JDB.listen(function(query, opts){ 14 console.log('Incoming query: ' + query, opts); 15});