From c6970e8b13e152cc1ef20772835b9e3ccd13a9fa Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Thu, 31 Oct 2019 15:32:10 +0200 Subject: [PATCH 1/4] added path support --- package.json | 1 + src/path.js | 45 +++++++++++++++++++++++++++++++++++++++ src/resultSet.js | 13 ++++++++++- test/pathBuilder.js | 35 ++++++++++++++++++++++++++++++ test/redisGraphAPITest.js | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/path.js create mode 100644 test/pathBuilder.js diff --git a/package.json b/package.json index c06f53815d..f987a0ba4a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "url": "git://github.com/redislabs/redisgraph.js.git" }, "dependencies": { + "deep-equal": "^1.1.0", "redis": "^2.8.0" }, "devDependencies": { diff --git a/src/path.js b/src/path.js new file mode 100644 index 0000000000..a8617393b6 --- /dev/null +++ b/src/path.js @@ -0,0 +1,45 @@ +class Path { + constructor(nodes, edges){ + this.nodes = nodes; + this.edges = edges; + } + + getNodes(){ + return this.nodes; + } + + getEdges(){ + return this.edges; + } + + getNode(index){ + return this.nodes[index]; + } + + getEdge(index){ + return this.edges[index]; + } + + firstNode(){ + return this.nodes[0]; + } + + lastNode(){ + return this.nodes[this.nodes.length -1]; + } + + nodeCount(){ + return this.nodes.length; + } + + edgeCount(){ + return this.edges.length; + } + + toString() { + return JSON.stringify(this); + } + +} + +module.exports = Path; diff --git a/src/resultSet.js b/src/resultSet.js index b954f9c07b..0ef65d596e 100644 --- a/src/resultSet.js +++ b/src/resultSet.js @@ -2,6 +2,7 @@ const Statistics = require("./statistics"), Record = require("./record"); Node = require("./node"); Edge = require("./edge"); + Path = require("./path"); ReplyError = require("redis").ReplyError const ResultSetColumnTypes = { @@ -20,7 +21,8 @@ const ResultSetValueTypes = { VALUE_DOUBLE: 5, VALUE_ARRAY: 6, VALUE_EDGE: 7, - VALUE_NODE: 8 + VALUE_NODE: 8, + VALUE_PATH: 9 } /** @@ -190,6 +192,12 @@ class ResultSet { return rawArray; } + async parsePath(rawPath) { + let nodes = await this.parseScalar(rawPath[0]); + let edges = await this.parseScalar(rawPath[1]); + return new Path(nodes, edges); + } + async parseScalar(cell) { let scalar_type = cell[0]; let value = cell[1]; @@ -224,6 +232,9 @@ class ResultSet { case ResultSetValueTypes.VALUE_EDGE: scalar = await this.parseEdge(value); break; + case ResultSetValueTypes.VALUE_PATH: + scalar = await this.parsePath(value); + break; case ResultSetValueTypes.VALUE_UNKNOWN: console.log("Unknown scalar type\n"); break; diff --git a/test/pathBuilder.js b/test/pathBuilder.js new file mode 100644 index 0000000000..170d262421 --- /dev/null +++ b/test/pathBuilder.js @@ -0,0 +1,35 @@ +Node = require("../src/node"); +Edge = require("../src/edge"); +Path = require("../src/path"); + +class PathBuilder { + constructor(nodeCount){ + this.nodes = new Array(); + this.edges = new Array(); + this.currentAppendClass = Node; + } + + append(obj){ + if(! obj instanceof this.currentAppendClass) throw "Error in path build insertion order and types." + if(obj instanceof Node) return this._appendNode(obj); + else return this._appendEdge(obj); + } + + build(){ + return new Path(this.nodes, this.edges); + } + + _appendNode(node){ + this.nodes.push(node); + this.currentAppendClass = Edge; + return this; + } + + _appendEdge(edge){ + this.edges.push(edge); + this.currentAppendClass = Node; + return this; + } +} + +module.exports = PathBuilder; diff --git a/test/redisGraphAPITest.js b/test/redisGraphAPITest.js index f5ee8f4562..9e782c60cf 100644 --- a/test/redisGraphAPITest.js +++ b/test/redisGraphAPITest.js @@ -2,6 +2,8 @@ const assert = require("assert"), redis = require("redis"), Label = require("../src/label"), RedisGraph = require("../src/graph"); + PathBuilder = require("./pathBuilder"); + deepEqual = require('deep-equal'); describe('RedisGraphAPI Test', function () { const api = new RedisGraph("social"); @@ -288,4 +290,44 @@ describe('RedisGraphAPI Test', function () { }) }) + it('testPath', (done)=>{ + api.query("CREATE (:L1)-[:R1]->(:L1)-[:R1]->(:L1)").then(response => { + api.query("MATCH p = (:L1)-[:R1*]->(:L1) RETURN p").then(response =>{ + let node0 = new Node("L1", {}); + node0.setId(0); + let node1 = new Node("L1", {}); + node1.setId(1); + let node2 = new Node("L1", {}); + node2.setId(2); + let edge01 = new Edge(0, "R1", 1, {}); + edge01.setId(0); + let edge12 = new Edge(1, "R1", 2, {}); + edge12.setId(1); + + let path01 = new PathBuilder().append(node0).append(edge01).append(node1).build(); + let path12 = new PathBuilder().append(node1).append(edge12).append(node2).build(); + let path02 = new PathBuilder().append(node0).append(edge01).append(node1).append(edge12).append(node2).build(); + + let paths = new Set([path01, path12, path02]); + while(response.hasNext()){ + let p = response.next().get("p"); + let pInPaths = false; + let path = null; + for( pathsIterator = paths.values(); path = pathsIterator.next().value;){ + if(deepEqual(p ,path)){ + pInPaths = true; + break; + } + } + assert(pInPaths); + paths.delete(path); + } + assert.equal(0, paths.size); + done(); + }) + }).catch(error => { + console.log(error); + }) + }) + }); From 273347cde5076aed86ad95aa95847d49615bb322 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Fri, 1 Nov 2019 00:20:30 +0200 Subject: [PATCH 2/4] wip --- examples/redisGraphExample.js | 11 +++++++++-- src/path.js | 12 ++++++------ test/pathBuilder.js | 6 +++--- test/redisGraphAPITest.js | 27 +++++++++++++++++++++++++-- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/examples/redisGraphExample.js b/examples/redisGraphExample.js index 7097923bba..81cfe1bfb6 100644 --- a/examples/redisGraphExample.js +++ b/examples/redisGraphExample.js @@ -20,8 +20,15 @@ graph let record = res.next(); console.log(record.getString("a.name")); } - console.log(res.getStatistics().queryExecutionTime()); - }) + console.log(res.getStatistics().queryExecutionTime()); + return graph.query("MATCH p = (a:person)-[:knows]->(:person) RETURN p"); + }).then(res => { + while (res.hasNext()) { + let record = res.next(); + // Check path.js for more path API + console.log(record.get(p).nodesCount); + } + }) .catch(err => { console.log(err); }); diff --git a/src/path.js b/src/path.js index a8617393b6..a4292d159e 100644 --- a/src/path.js +++ b/src/path.js @@ -4,11 +4,11 @@ class Path { this.edges = edges; } - getNodes(){ + get Nodes(){ return this.nodes; } - getEdges(){ + get Edges(){ return this.edges; } @@ -20,19 +20,19 @@ class Path { return this.edges[index]; } - firstNode(){ + get firstNode(){ return this.nodes[0]; } - lastNode(){ + get lastNode(){ return this.nodes[this.nodes.length -1]; } - nodeCount(){ + get nodeCount(){ return this.nodes.length; } - edgeCount(){ + get edgeCount(){ return this.edges.length; } diff --git a/test/pathBuilder.js b/test/pathBuilder.js index 170d262421..2f6670b606 100644 --- a/test/pathBuilder.js +++ b/test/pathBuilder.js @@ -1,6 +1,6 @@ -Node = require("../src/node"); -Edge = require("../src/edge"); -Path = require("../src/path"); +const Node = require("../src/node"), + Edge = require("../src/edge"), + Path = require("../src/path"); class PathBuilder { constructor(nodeCount){ diff --git a/test/redisGraphAPITest.js b/test/redisGraphAPITest.js index 9e782c60cf..47bc560f89 100644 --- a/test/redisGraphAPITest.js +++ b/test/redisGraphAPITest.js @@ -1,8 +1,8 @@ const assert = require("assert"), redis = require("redis"), Label = require("../src/label"), - RedisGraph = require("../src/graph"); - PathBuilder = require("./pathBuilder"); + RedisGraph = require("../src/graph"), + PathBuilder = require("./pathBuilder"), deepEqual = require('deep-equal'); describe('RedisGraphAPI Test', function () { @@ -290,6 +290,29 @@ describe('RedisGraphAPI Test', function () { }) }) + it('unitTestPath', ()=>{ + let node0 = new Node("L1", {}); + node0.setId(0); + let node1 = new Node("L1", {}); + node1.setId(1); + + let edge01 = new Edge(0, "R1", 1, {}); + edge01.setId(0); + + let path01 = new PathBuilder().append(node0).append(edge01).append(node1).build(); + + assert.equal(1, path01.edgeCount); + assert.equal(2, path01.nodeCount); + assert.deepEqual(node0, path01.firstNode); + assert.deepEqual(node0, path01.getNode(0)); + assert.deepEqual(node1, path01.lastNode); + assert.deepEqual(node1, path01.getNode(1)); + assert.deepEqual(edge01, path01.getEdge(0)); + assert.deepEqual([node0, node1], path01.nodes); + assert.deepEqual([edge01], path01.edges); + + }) + it('testPath', (done)=>{ api.query("CREATE (:L1)-[:R1]->(:L1)-[:R1]->(:L1)").then(response => { api.query("MATCH p = (:L1)-[:R1*]->(:L1) RETURN p").then(response =>{ From 859bc87b8bb678d8048859109c362b35ea2c44d1 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Fri, 1 Nov 2019 00:56:47 +0200 Subject: [PATCH 3/4] added example --- README.md | 51 ++++++++++++++++++++--------------- examples/redisGraphExample.js | 49 +++++++++++++++------------------ 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index cd5807e731..2ca803e505 100644 --- a/README.md +++ b/README.md @@ -30,27 +30,36 @@ npm install redisgraph.js # Example: Using the JavaScript Client ```javascript -const RedisGraph = require("redisgraph.js").RedisGraph; - -let graph = new RedisGraph('social'); -graph -.query("CREATE (:person{name:'roi',age:32})") -.then( () => { - return graph.query("CREATE (:person{name:'amit',age:30})"); -}) -.then( () => { - return graph.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)") -}) -.then( () => { - return graph.query("MATCH (a:person)-[:knows]->(:person) RETURN a") -}) -.then( (res) => { - while(res.hasNext()){ - let record = res.next(); - console.log(record.getString('a.name')); - } - console.log(res.getStatistics().queryExecutionTime()); -}); +const RedisGraph = require("redisgraph.js").Graph; + +let graph = new RedisGraph("social"); + +graph.query("CREATE (:person{name:'roi',age:32})").then(() => { + graph.query("CREATE (:person{name:'amit',age:30})").then(()=>{ + graph.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)").then(()=>{ + graph.query("MATCH (a:person)-[:knows]->(:person) RETURN a.name").then(res=>{ + while (res.hasNext()) { + let record = res.next(); + console.log(record.get("a.name")); + } + console.log(res.getStatistics().queryExecutionTime()); + graph.query("MATCH p = (a:person)-[:knows]->(:person) RETURN p").then(res=>{ + while (res.hasNext()) { + let record = res.next(); + // See path.js for more path API. + console.log(record.get("p").nodeCount); + graph.deleteGraph(); + process.exit(); + } + }) + }) + }) + }) + }) + .catch(err => { + console.log(err); + }); + ``` diff --git a/examples/redisGraphExample.js b/examples/redisGraphExample.js index 81cfe1bfb6..00c92d8042 100644 --- a/examples/redisGraphExample.js +++ b/examples/redisGraphExample.js @@ -1,34 +1,29 @@ -const RedisGraph = require("redisgraph.js").RedisGraph; +const RedisGraph = require("redisgraph.js").Graph; let graph = new RedisGraph("social"); -graph - .query("CREATE (:person{name:'roi',age:32})") - .then(() => { - return graph.query("CREATE (:person{name:'amit',age:30})"); +graph.query("CREATE (:person{name:'roi',age:32})").then(() => { + graph.query("CREATE (:person{name:'amit',age:30})").then(()=>{ + graph.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(b)").then(()=>{ + graph.query("MATCH (a:person)-[:knows]->(:person) RETURN a.name").then(res=>{ + while (res.hasNext()) { + let record = res.next(); + console.log(record.get("a.name")); + } + console.log(res.getStatistics().queryExecutionTime()); + graph.query("MATCH p = (a:person)-[:knows]->(:person) RETURN p").then(res=>{ + while (res.hasNext()) { + let record = res.next(); + // See path.js for more path API. + console.log(record.get("p").nodeCount); + graph.deleteGraph(); + process.exit(); + } + }) + }) + }) + }) }) - .then(() => { - return graph.query( - "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)" - ); - }) - .then(() => { - return graph.query("MATCH (a:person)-[:knows]->(:person) RETURN a"); - }) - .then(res => { - while (res.hasNext()) { - let record = res.next(); - console.log(record.getString("a.name")); - } - console.log(res.getStatistics().queryExecutionTime()); - return graph.query("MATCH p = (a:person)-[:knows]->(:person) RETURN p"); - }).then(res => { - while (res.hasNext()) { - let record = res.next(); - // Check path.js for more path API - console.log(record.get(p).nodesCount); - } - }) .catch(err => { console.log(err); }); From 0f2f9bafc508ba9d57001417103e23def09da9b4 Mon Sep 17 00:00:00 2001 From: DvirDukhan Date: Sun, 3 Nov 2019 16:03:07 +0200 Subject: [PATCH 4/4] added use strict --- src/edge.js | 1 + src/graph.js | 1 + src/label.js | 1 + src/node.js | 1 + src/path.js | 1 + src/record.js | 1 + src/resultSet.js | 11 ++++++----- src/statistics.js | 1 + test/pathBuilder.js | 1 + test/redisGraphAPITest.js | 7 ++++++- 10 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/edge.js b/src/edge.js index f907295868..d671e4b35b 100644 --- a/src/edge.js +++ b/src/edge.js @@ -1,3 +1,4 @@ +"use strict"; /** * An edge connecting two nodes. */ diff --git a/src/graph.js b/src/graph.js index befc6c5157..4b75506d34 100644 --- a/src/graph.js +++ b/src/graph.js @@ -1,3 +1,4 @@ +"use strict"; const redis = require("redis"), util = require("util"), ResultSet = require("./resultSet"); diff --git a/src/label.js b/src/label.js index 33e1565dc2..39f7d2de4f 100644 --- a/src/label.js +++ b/src/label.js @@ -1,3 +1,4 @@ +"use strict"; /** * Different Statistics labels */ diff --git a/src/node.js b/src/node.js index 1b242a9b9e..3018abb8ae 100644 --- a/src/node.js +++ b/src/node.js @@ -1,3 +1,4 @@ +"use strict"; /** * A node within the garph. */ diff --git a/src/path.js b/src/path.js index a4292d159e..83f0bab15d 100644 --- a/src/path.js +++ b/src/path.js @@ -1,3 +1,4 @@ +"use strict"; class Path { constructor(nodes, edges){ this.nodes = nodes; diff --git a/src/record.js b/src/record.js index 17826fc308..afd1ede43e 100644 --- a/src/record.js +++ b/src/record.js @@ -1,3 +1,4 @@ +"use strict"; /** * Hold a query record */ diff --git a/src/resultSet.js b/src/resultSet.js index 0ef65d596e..3afea4a353 100644 --- a/src/resultSet.js +++ b/src/resultSet.js @@ -1,9 +1,10 @@ +"use strict"; const Statistics = require("./statistics"), - Record = require("./record"); - Node = require("./node"); - Edge = require("./edge"); - Path = require("./path"); - ReplyError = require("redis").ReplyError + Record = require("./record"), + Node = require("./node"), + Edge = require("./edge"), + Path = require("./path"), + ReplyError = require("redis").ReplyError; const ResultSetColumnTypes = { COLUMN_UNKNOWN: 0, diff --git a/src/statistics.js b/src/statistics.js index 711ee7d36f..0618a0aa9d 100644 --- a/src/statistics.js +++ b/src/statistics.js @@ -1,3 +1,4 @@ +"use strict"; const Label = require("./label"); class Statistics { diff --git a/test/pathBuilder.js b/test/pathBuilder.js index 2f6670b606..74eb46a0d8 100644 --- a/test/pathBuilder.js +++ b/test/pathBuilder.js @@ -1,3 +1,4 @@ +"use strict"; const Node = require("../src/node"), Edge = require("../src/edge"), Path = require("../src/path"); diff --git a/test/redisGraphAPITest.js b/test/redisGraphAPITest.js index 47bc560f89..687cedde76 100644 --- a/test/redisGraphAPITest.js +++ b/test/redisGraphAPITest.js @@ -1,7 +1,11 @@ +"use strict"; const assert = require("assert"), redis = require("redis"), Label = require("../src/label"), RedisGraph = require("../src/graph"), + Node = require("../src/node"), + Edge = require("../src/edge"), + Path = require("../src/path"), PathBuilder = require("./pathBuilder"), deepEqual = require('deep-equal'); @@ -336,7 +340,8 @@ describe('RedisGraphAPI Test', function () { let p = response.next().get("p"); let pInPaths = false; let path = null; - for( pathsIterator = paths.values(); path = pathsIterator.next().value;){ + let pathsIterator = paths.values(); + for( pathsIterator; path = pathsIterator.next().value;){ if(deepEqual(p ,path)){ pInPaths = true; break;