Skip to content

Commit 147cd7b

Browse files
committed
shareable server configuration added
1 parent 665aca6 commit 147cd7b

File tree

7 files changed

+187
-101
lines changed

7 files changed

+187
-101
lines changed

README.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
# node-red-contrib-nulli-neo4j
2-
A <a href="http://nodered.org" target="_new">Node-RED</a> node that lets you run generic cypher queries on a Neo4j graph database.
1+
# Neo4j Bolt with shareable driver
2+
3+
A <a href="http://nodered.org" target="_new">Node-RED</a> node with the shareable server configuration that lets you run generic cypher queries on a Neo4j graph database.
34

45
## Install
56

67
Run the following command in the root directory of your Node-RED install or home directory (usually ~/.node-red) and will also install needed libraries.
78

89
```
9-
npm install node-red-contrib-nulli-neo4j
10+
npm install node-red-contrib-neo4j-bolt
1011
```
1112

1213
## Usage
1314

14-
You can define the Neo4j bolt URL and the basic authentication username and password in the node's configuration.
15+
You specify a cypher query in the configuration. The parameters for the query (if needed) are read from `msg.params`. The cypher query can also be passed to the node as `msg.query`.
1516

16-
You can also specify a cypher query in the configuration. The parameters for the query (if needed) are read from `msg.params`. The cypher query can also be passed to the node as `msg.query`. Below are some examples:
17+
You define the Neo4j bolt URL and the basic authentication username and password in the configuration node which you can share across multiple neo4j-bolt nodes.
1718

1819
* Example of hard coded query in the configuration of the node.
1920
```
@@ -45,15 +46,11 @@ msg.params:
4546

4647
The node has two outputs. If the query returns only 1 record, the requested properties of the node are sent to output #1. If the query returns multiple records, an array of requested properties of the nodes are sent to output #2.
4748

48-
You can import the following and use it with the [neo4j example movie dataset](https://neo4j.com/developer/movie-database/)
49-
50-
![Example Flow](./docs/images/example_flow.png)
51-
52-
```
53-
[{"id":"e10395d.a56ef68","type":"inject","z":"488d970f.76d278","name":"Single record query","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":178,"y":170,"wires":[["9eb7f4e.b975a08"]]},{"id":"edc735f1.c9aeb8","type":"debug","z":"488d970f.76d278","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":978,"y":215,"wires":[]},{"id":"9eb7f4e.b975a08","type":"change","z":"488d970f.76d278","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"MATCH (m:Movie {title: $moviename}) RETURN m","tot":"str"},{"t":"set","p":"params","pt":"msg","to":"{\"moviename\": \"Forrest Gump\"}","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":415,"y":169,"wires":[["c9467e8a.62018","ce358d36.ff73c"]]},{"id":"b2bd2c4.0a654d","type":"debug","z":"488d970f.76d278","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":976,"y":349,"wires":[]},{"id":"3e3a33b3.12f48c","type":"inject","z":"488d970f.76d278","name":"Multi record query","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":171,"y":275,"wires":[["9e73c15d.04eb4"]]},{"id":"9e73c15d.04eb4","type":"change","z":"488d970f.76d278","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"MATCH (m:Movie {title: $moviename})<-[:ACTS_IN]-(a:Actor) RETURN a","tot":"str"},{"t":"set","p":"params","pt":"msg","to":"{\"moviename\": \"Forrest Gump\"}","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":411,"y":274,"wires":[["c9467e8a.62018","ce358d36.ff73c"]]},{"id":"94ca7897.d13bd8","type":"change","z":"488d970f.76d278","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"merge (m:Movie {title: $moviename}) set m += $props return m","tot":"str"},{"t":"set","p":"params","pt":"msg","to":"{\t \"moviename\": \"My Blockbuster Movie\",\t \"props\": {\t \"studio\":\"Home Prod\",\t \"runtime\":142,\t \"description\":\"This is my first movie\",\t \"language\":\"en\",\t \"version\":274,\t \"imageUrl\":\"https://c1.staticflickr.com/9/8387/8453530769_9bab22d205_b.jpg\",\t \"genre\":\"Comedy\",\t \"tagline\":\"How not to make a movie...\",\t \"homepage\":\"\"\t } \t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":411,"y":374,"wires":[["c9467e8a.62018","ce358d36.ff73c"]]},{"id":"8f998b68.265728","type":"inject","z":"488d970f.76d278","name":"Create example","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":167,"y":375,"wires":[["94ca7897.d13bd8"]]},{"id":"c9467e8a.62018","type":"debug","z":"488d970f.76d278","name":"query and params","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":752,"y":459,"wires":[]},{"id":"ce358d36.ff73c","type":"node-red-contrib-nulli-neo4j","z":"488d970f.76d278","name":"neo4j","url":"bolt://localhost:7687","username":"neo4j","password":"test1234","query":"","x":667.5,"y":273,"wires":[["edc735f1.c9aeb8"],["b2bd2c4.0a654d"]]}]
54-
```
55-
5649
This node uses the [neo4j-driver](https://www.npmjs.com/package/neo4j-driver) package to communicate with neo4j.
5750

5851
### Runtime information
59-
This node was tested to Node.js v8.10.0 LTS and NPM 5.6.0 on Node-Red v0.18.4
52+
This node was tested to Node.js v7.4.0 and NPM 5.6.0 on Node-Red v0.18.4
53+
54+
### Credits
55+
56+
This node is based on <a href="https://github.com/nullibrew/node-red-contrib-nulli-neo4j" target="_new">node-red-contrib-nulli-neo4j</a> by <a href="http://nulli.com">Nulli</a>.

docs/images/example_flow.png

-63.5 KB
Binary file not shown.

neo4j.html

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<script type="text/javascript">
2-
RED.nodes.registerType('node-red-contrib-nulli-neo4j',{
3-
category: 'Nulli',
4-
color: '#a6bbcf',
2+
RED.nodes.registerType('neo4j-bolt',{
3+
category: 'Neo4j Bolt',
4+
color: '#97ddf4',
55
defaults: {
6+
server: {value:"", type:"neo4j-bolt-server", required:true},
67
name: {value:""},
78
url: {value:""},
89
username: {value:""},
@@ -13,31 +14,26 @@
1314
outputs:2,
1415
icon: "neo4j.png",
1516
label: function() {
16-
return this.name||"node-red-contrib-nulli-neo4j";
17+
return this.name||"neo4j-bolt";
1718
}
1819
});
1920
</script>
2021

21-
<script type="text/x-red" data-template-name="node-red-contrib-nulli-neo4j">
22+
<script type="text/x-red" data-template-name="neo4j-bolt">
23+
<div class="form-row">
24+
<label for="node-input-server"><i class="icon-tag"></i> server</label>
25+
<input type="text" id="node-input-server" placeholder="server">
26+
</div>
2227
<div class="form-row">
2328
<label for="node-input-name"><i class="icon-tag"></i>Name</label>
2429
<input type="text" id="node-input-name" placeholder="Name">
2530
<br/>
26-
<label for="node-input-url"><i class="icon-tag"></i>URL</label>
27-
<input type="text" id="node-input-url" placeholder="bolt://localhost:7687">
28-
<br/>
29-
<label for="node-input-username"><i class="icon-tag"></i>Username</label>
30-
<input type="text" id="node-input-username" placeholder="neo4j">
31-
<br/>
32-
<label for="node-input-password"><i class="icon-tag"></i>Password</label>
33-
<input type="password" id="node-input-password" placeholder="test1234">
34-
<br/>
3531
<label for="node-input-query"><i class="icon-tag"></i>Cypher Query (opt)</label>
36-
<input type="text" id="node-input-query" placeholder="MATCH (o:Object {attrib: 'value'}) RETURN o">
32+
<textarea id="node-input-query" placeholder="MATCH (o:Object {attrib: 'value'}) RETURN o"></textarea>
3733
</div>
3834
</script>
3935

40-
<script type="text/x-red" data-help-name="node-red-contrib-nulli-neo4j">
36+
<script type="text/x-red" data-help-name="neo4j-bolt">
4137
<p>A Neo4j cypher query node</p>
4238

4339
<p>Neo4J cypher node that lets you run generic cypher queries on a Neo4j graph database.</p>
@@ -50,6 +46,6 @@
5046
</p>
5147

5248
<p>
53-
Supported by <a href="http://nulli.com">Nulli</a>
49+
Supported by <a href="https://github.com/webmaxru/">Maxim Salnikov</a>. Based on <a href="https://github.com/nullibrew/node-red-contrib-nulli-neo4j">node-red-contrib-nulli-neo4j</a> by <a href="http://nulli.com">Nulli</a>
5450
</p>
5551
</script>

neo4j.js

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,60 @@
1-
const neo4j = require('neo4j-driver').v1;
1+
const neo4j = require('neo4j-driver').v1
22
module.exports = function (RED) {
3-
function NulliNeo4j(config) {
4-
RED.nodes.createNode(this, config);
5-
var node = this;
6-
//console.log(`url = ${config.url}`);
7-
const driver = neo4j.driver(config.url, neo4j.auth.basic(config.username, config.password));
8-
const session = driver.session();
9-
if (session) {
10-
node.status({
11-
fill: "green",
12-
shape: "dot",
13-
text: "node-red:common.status.connected"
14-
});
15-
node.on('input', function (msg) {
16-
//console.log(`decoding QR data from ${msg.payload}`);
17-
var query = config.query || msg.query;
18-
let params = null;
19-
if (typeof (msg.params) === 'string') {
20-
params = JSON.parse(msg.params);
21-
} else {
22-
params = msg.params;
23-
}
24-
//console.log(`params: ${params}`);
25-
var scalar_result = {
26-
payload: null
27-
};
28-
const resultPromise = session.run(query, params);
3+
function Neo4jBolt (config) {
4+
RED.nodes.createNode(this, config)
5+
var node = this
296

30-
var array_result = {
31-
payload: []
32-
}
33-
resultPromise.then(result => {
34-
session.close();
35-
if (result.records.length > 1) {
36-
result.records.forEach(function (item, index, array) {
37-
array_result.payload.push(item.get(0).properties);
38-
});
39-
//console.log(`array size: ${array_result.payload.length}`)
40-
node.send([null, array_result]);
41-
} else {
42-
scalar_result.payload = result.records[0].get(0).properties;
43-
//msg.payload = result.records;
44-
node.send([scalar_result, null]);
7+
node.server = RED.nodes.getNode(config.server)
458

46-
}
47-
});
48-
});
9+
const driver = node.server.driver
10+
const session = driver.session()
11+
12+
if (session) {
13+
node.status({
14+
fill: 'green',
15+
shape: 'dot',
16+
text: 'node-red:common.status.connected'
17+
})
18+
node.on('input', function (msg) {
19+
var query = config.query || msg.query
20+
let params = null
21+
if (typeof (msg.params) === 'string') {
22+
params = JSON.parse(msg.params)
4923
} else {
50-
node.status({
51-
fill: "red",
52-
shape: "dot",
53-
text: "node-red:common.status.disconnected"
54-
});
24+
params = msg.params
25+
}
26+
var scalar_result = {
27+
payload: null
5528
}
29+
const resultPromise = session.run(query, params)
5630

57-
node.on('close', function () {
58-
// tidy up any state
59-
driver.close();
60-
});
31+
var array_result = {
32+
payload: []
33+
}
34+
resultPromise.then(result => {
35+
session.close()
36+
if (result.records.length > 1) {
37+
result.records.forEach(function (item, index, array) {
38+
array_result.payload.push(item.get(0).properties)
39+
})
40+
node.send([null, array_result])
41+
} else {
42+
scalar_result.payload = result.records[0].get(0).properties
43+
node.send([scalar_result, null])
44+
}
45+
})
46+
})
47+
} else {
48+
node.status({
49+
fill: 'red',
50+
shape: 'dot',
51+
text: 'node-red:common.status.disconnected'
52+
})
6153
}
62-
RED.nodes.registerType("node-red-contrib-nulli-neo4j", NulliNeo4j);
63-
}
54+
55+
node.on('close', function () {
56+
driver.close()
57+
})
58+
}
59+
RED.nodes.registerType('neo4j-bolt', Neo4jBolt)
60+
}

neo4j_server.html

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script type="text/javascript">
2+
RED.nodes.registerType('neo4j-bolt-server', {
3+
category: 'config',
4+
defaults: {
5+
name: { value: '' },
6+
url: { value: 'bolt://', required: true },
7+
user: { value: '' },
8+
pass: { value: '' }
9+
},
10+
label: function () {
11+
return this.url;
12+
},
13+
oneditprepare: function () {
14+
var tabs = RED.tabs.create({
15+
id: "neo4j-bolt-server-tabs",
16+
onchange: function (tab) {
17+
$("#neo4j-bolt-server-tabs-content").children().hide();
18+
$("#" + tab.id).show();
19+
}
20+
});
21+
tabs.addTab({
22+
id: "neo4j-bolt-server-tab-connection",
23+
label: "Connection"
24+
});
25+
tabs.addTab({
26+
id: "neo4j-bolt-server-tab-security",
27+
label: "Security"
28+
});
29+
setTimeout(function () { tabs.resize(); }, 0);
30+
}
31+
});
32+
</script>
33+
34+
<script type="text/x-red" data-template-name="neo4j-bolt-server">
35+
<div class="form-row">
36+
<label for="node-config-input-name">
37+
<i class="fa fa-tag"></i> <span>Name</span>
38+
</label>
39+
<input type="text" id="node-config-input-name" placeholder="Name">
40+
</div>
41+
<div class="form-row">
42+
<ul id="neo4j-bolt-server-tabs"
43+
style="background: #fff; min-width: 600px; margin-bottom: 20px;">
44+
</ul>
45+
</div>
46+
<div id="neo4j-bolt-server-tabs-content" style="min-height: 170px;">
47+
<div id="neo4j-bolt-server-tab-connection" style="display:none">
48+
<div class="form-row">
49+
<label for="node-config-input-url">
50+
<i class="icon-bookmark"></i> Url
51+
</label>
52+
<input type="text" id="node-config-input-url" placeholder="bolt://">
53+
</div>
54+
</div>
55+
<div id="neo4j-bolt-server-tab-security" style="display:none">
56+
<div class="form-row">
57+
<label for="node-config-input-user">
58+
<i class="icon-tag"></i> user
59+
</label>
60+
<input type="text" id="node-config-input-user" placeholder="neo4j">
61+
</div>
62+
<div class="form-row">
63+
<label for="node-config-input-pass">
64+
<i class="icon-tag"></i> pass
65+
</label>
66+
<input type="password" id="node-config-input-pass" placeholder="test1234">
67+
</div>
68+
</div>
69+
</div>
70+
</script>

neo4j_server.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const neo4j = require('neo4j-driver').v1;
2+
3+
module.exports = function(RED) {
4+
function RemoteServerNode(n) {
5+
RED.nodes.createNode(this,n);
6+
var node = this;
7+
8+
let url = n.url
9+
10+
this.driver = neo4j.driver(url, neo4j.auth.basic(n.user, n.pass));
11+
12+
node.on('close', function () {
13+
this.driver.close();
14+
});
15+
}
16+
RED.nodes.registerType('neo4j-bolt-server',RemoteServerNode);
17+
}

package.json

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
11
{
2-
"name": "node-red-contrib-nulli-neo4j",
3-
"version": "1.0.6",
4-
"description": "Node RED node for neo4j",
5-
"main": "neo4j.js",
6-
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
2+
"author": {
3+
84
},
9-
"author": "[email protected]",
10-
"license": "MIT",
5+
"bugs": {
6+
"url": "https://github.com/webmaxru/node-red-contrib-neo4j-bolt/issues"
7+
},
8+
"bundleDependencies": false,
119
"dependencies": {
1210
"neo4j-driver": "latest"
1311
},
12+
"deprecated": false,
13+
"description": "Node RED node for neo4j",
14+
"homepage": "https://github.com/webmaxru/node-red-contrib-neo4j-bolt#readme",
1415
"keywords": [
1516
"node-red",
1617
"nodered",
1718
"neo4j",
1819
"cypher"
1920
],
20-
"repository": {
21-
"type": "git",
22-
"url": "git+https://github.com/nullibrew/node-red-contrib-nulli-neo4j.git"
23-
},
21+
"license": "MIT",
22+
"main": "neo4j.js",
23+
"name": "node-red-contrib-neo4j-bolt",
2424
"node-red": {
2525
"nodes": {
26-
"node-red-contrib-nulli-neo4j": "neo4j.js"
26+
"neo4j-bolt": "neo4j.js",
27+
"neo4j-bolt-server": "neo4j_server.js"
2728
}
28-
}
29+
},
30+
"repository": {
31+
"type": "git",
32+
"url": "git+https://github.com/webmaxru/node-red-contrib-neo4j-bolt.git"
33+
},
34+
"scripts": {
35+
"test": "echo \"Error: no test specified\" && exit 1"
36+
},
37+
"version": "1.0.0"
2938
}

0 commit comments

Comments
 (0)