diff --git a/.gitignore b/.gitignore index 0b80d7ad76..e171686cee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.settings/ /node_modules/ .project -package-lock.json \ No newline at end of file +package-lock.json +yarn.lock +/examples/node_modules diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 5b7ff541c3..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,127 +0,0 @@ -{ - "esversion": 6, - - // -------------------------------------------------------------------- - // JSHint Nodeclipse Configuration v0.18 - // Strict Edition with some relaxations and switch to Node.js, no `use strict` - // by Ory Band, Michael Haschke, Paul Verest - // https://github.com/Nodeclipse/nodeclipse-1/blob/master/org.nodeclipse.ui/templates/common-templates/.jshintrc - // JSHint Documentation is at http://www.jshint.com/docs/options/ - // JSHint Integration v0.9.10 comes with JSHInt 2.5.6 , see https://github.com/eclipsesource/jshint-eclipse - // -------------------------------------------------------------------- - // from https://gist.github.com/haschek/2595796 - // - // This is a options template for [JSHint][1], using [JSHint example][2] - // and [Ory Band's example][3] as basis and setting config values to - // be most strict: - // - // * set all enforcing options to true - // * set all relaxing options to false - // * set all environment options to false, except the node value - // * set all JSLint legacy options to false - // - // [1]: http://www.jshint.com/ - // [2]: https://github.com/jshint/node-jshint/blob/master/example/config.json //404 - // [3]: https://github.com/oryband/dotfiles/blob/master/jshintrc - // [4]: http://www.jshint.com/options/ - // - // @author http://michael.haschke.biz/ - // @license http://unlicense.org/ - - // == Enforcing Options =============================================== - // - // These options tell JSHint to be more strict towards your code. Use - // them if you want to allow only a safe subset of JavaScript, very - // useful when your codebase is shared with a big number of developers - // with different skill levels. Was all true. - - "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). - "curly" : true, // Require {} for every new block or scope. - "eqeqeq" : true, // Require triple equals i.e. `===`. - "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. - "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` - "latedef" : true, // Prohibit variable use before definition. - "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. - "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. - "noempty" : true, // Prohibit use of empty blocks. - "nonew" : true, // Prohibit use of constructors for side-effects. - "plusplus" : false, // Prohibit use of `++` & `--`. //coding style related only - "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. - "undef" : true, // Require all non-global variables be declared before they are used. - "strict" : false, // Require `use strict` pragma in every file. - "trailing" : true, // Prohibit trailing whitespaces. - - // == Relaxing Options ================================================ - // - // These options allow you to suppress certain types of warnings. Use - // them only if you are absolutely positive that you know what you are - // doing. Was all false. - "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). - "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. - "debug" : false, // Allow debugger statements e.g. browser breakpoints. - "eqnull" : false, // Tolerate use of `== null`. - //"es5" : true, // Allow EcmaScript 5 syntax. // es5 is default https://github.com/jshint/jshint/issues/1411 - "esnext" : false, // Allow ES.next (ECMAScript 6) specific features such as `const` and `let`. - "evil" : false, // Tolerate use of `eval`. - "expr" : false, // Tolerate `ExpressionStatement` as Programs. - "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. - "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). - "iterator" : false, // Allow usage of __iterator__ property. - "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. - "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. - "laxcomma" : true, // Suppress warnings about comma-first coding style. - "loopfunc" : false, // Allow functions to be defined within loops. - "maxerr" : 100, // This options allows you to set the maximum amount of warnings JSHint will produce before giving up. Default is 50. - "multistr" : false, // Tolerate multi-line strings. - "onecase" : false, // Tolerate switches with just one case. - "proto" : false, // Tolerate __proto__ property. This property is deprecated. - "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. - "scripturl" : false, // Tolerate script-targeted URLs. - "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. - "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. - "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. - "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. - "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. - - // == Environments ==================================================== - // - // These options pre-define global variables that are exposed by - // popular JavaScript libraries and runtime environments—such as - // browser or node.js. TODO JSHint Documentation has more, but it is not clear since what JSHint version they appeared - "browser" : false, // Standard browser globals e.g. `window`, `document`. - "couch" : false, // Enable globals exposed by CouchDB. - "devel" : false, // Allow development statements e.g. `console.log();`. - "dojo" : false, // Enable globals exposed by Dojo Toolkit. - "jquery" : false, // Enable globals exposed by jQuery JavaScript library. - "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. - "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment. - "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. - "phantom" : false, //?since version? This option defines globals available when your core is running inside of the PhantomJS runtime environment. - "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. - "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. - "worker" : false, //?since version? This option defines globals available when your code is running inside of a Web Worker. - "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. - "yui" : false, //?since version? This option defines globals exposed by the YUI JavaScript framework. - - // == JSLint Legacy =================================================== - // - // These options are legacy from JSLint. Aside from bug fixes they will - // not be improved in any way and might be removed at any point. - "nomen" : false, // Prohibit use of initial or trailing underbars in names. - "onevar" : false, // Allow only one `var` statement per function. - "passfail" : false, // Stop on first error. - "white" : false, // Check against strict whitespace and indentation rules. - - // == Undocumented Options ============================================ - // - // While Michael have found these options in [example1][2] and [example2][3] (already gone 404) - // they are not described in the [JSHint Options documentation][4]. - - "predef" : [ // Extra globals. - //"exampleVar", - //"anotherCoolGlobal", - //"iLoveDouglas" - "Java", "JavaFX", "$ARG" //no effect - ] - //, "indent" : 2 // Specify indentation spacing -} \ No newline at end of file diff --git a/examples/RedisGraphExample.js b/examples/RedisGraphExample.js deleted file mode 100644 index eb737c5155..0000000000 --- a/examples/RedisGraphExample.js +++ /dev/null @@ -1,26 +0,0 @@ -const RedisGraph = require('../src/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]->(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()); -}) -.catch((err) => { - console.log(err); -}); diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 0000000000..0aa40c1593 --- /dev/null +++ b/examples/package.json @@ -0,0 +1,15 @@ +{ + "name": "redisgraph-example", + "version": "1.0.0", + "description": "Example using redisgraph.js", + "author": "RedisLabs", + "license": "BSD 3", + "repository": { + "type": "git", + "url": "git://github.com/redislabs/redisgraph.js.git" + }, + "dependencies": { + "redisgraph.js": "^1.1.0" + }, + "main": "redisGraphExample.js" +} diff --git a/examples/redisGraphExample.js b/examples/redisGraphExample.js new file mode 100644 index 0000000000..7097923bba --- /dev/null +++ b/examples/redisGraphExample.js @@ -0,0 +1,27 @@ +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]->(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()); + }) + .catch(err => { + console.log(err); + }); diff --git a/index.js b/index.js new file mode 100644 index 0000000000..8264408085 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +const Record = require("./src/record"), + RedisGraph = require("./src/redisGraph"), + ResultSet = require("./src/resultSet"), + Statistics = require("./src/statistics"), + Label = require("./label"); + +module.exports = { + Record: Record, + RedisGraph: RedisGraph, + ResultSet: ResultSet, + Statistics: Statistics, + Label: Label +}; diff --git a/package.json b/package.json index 5bd24ef21e..db8aa00456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redisgraph.js", - "version": "1.0.4", + "version": "1.1.0", "description": "Connect to RedisGraph 1.0.0 and up from JavaScript", "author": "RedisLabs", "license": "BSD 3", @@ -16,5 +16,6 @@ }, "scripts": { "test": "mocha --exit" - } + }, + "main": "index.js" } diff --git a/src/label.js b/src/label.js new file mode 100644 index 0000000000..33e1565dc2 --- /dev/null +++ b/src/label.js @@ -0,0 +1,14 @@ +/** + * Different Statistics labels + */ +var Label = Object.freeze({ + LABELS_ADDED: "Labels added", + NODES_CREATED: "Nodes created", + NODES_DELETED: "Nodes deleted", + RELATIONSHIPS_DELETED: "Relationships deleted", + PROPERTIES_SET: "Properties set", + RELATIONSHIPS_CREATED: "Relationships created", + QUERY_INTERNAL_EXECUTION_TIME: "Query internal execution time" +}); + +module.exports = Label; diff --git a/src/record.js b/src/record.js index b0978f5b64..d661e7e708 100644 --- a/src/record.js +++ b/src/record.js @@ -1,16 +1,15 @@ /** * Hold a query record */ -module.exports = class Record { - - constructor(header, values){ - this._header = header; - this._values = values; - } - +class Record { + constructor(header, values) { + this._header = header; + this._values = values; + } + getString(key) { let index = key; - if(typeof key === "string"){ + if (typeof key === "string") { index = this._header.indexOf(key); } return this._values[index]; @@ -30,5 +29,7 @@ module.exports = class Record { size() { return this._header.length; - } -} \ No newline at end of file + } +} + +module.exports = Record; diff --git a/src/redisGraph.js b/src/redisGraph.js index bdb87a591b..4dbab581c4 100644 --- a/src/redisGraph.js +++ b/src/redisGraph.js @@ -1,15 +1,14 @@ -const redis = require('redis'), -util = require('util'), -ResultSet = require('./resultSet'); +const redis = require("redis"), + util = require("util"), + ResultSet = require("./resultSet"); /** * RedisGraph client */ -module.exports = class RedisGraph { - +class RedisGraph { /** * Creates a client to a specific graph running on the specific host/post - * See: node_redis for more options on createClient + * See: node_redis for more options on createClient * * @param graphId the graph id * @param host Redis host or node_redis client @@ -17,34 +16,38 @@ module.exports = class RedisGraph { * @param options node_redis options */ constructor(graphId, host, port, options) { - this._graphId = graphId; - let client = (host instanceof redis.RedisClient) ? host : redis.createClient.apply(redis, [].slice.call(arguments,1)); + this._graphId = graphId; + let client = + host instanceof redis.RedisClient + ? host + : redis.createClient.apply(redis, [].slice.call(arguments, 1)); this._sendCommand = util.promisify(client.send_command).bind(client); } - + /** * Execute a Cypher query - * + * * @param query Cypher query - * @return a result set + * @return a result set */ - query(query) { - return this._sendCommand('graph.QUERY',[this._graphId, query]) - .then((res) => { - return new ResultSet(res); - }); + query(query) { + return this._sendCommand("graph.QUERY", [this._graphId, query]).then( + res => { + return new ResultSet(res); + } + ); } - - /** - * Deletes the entire graph - * - * @return delete running time statistics - */ - deleteGraph() { - return this._sendCommand('graph.DELETE',[this._graphId]) - .then((res) => { + + /** + * Deletes the entire graph + * + * @return delete running time statistics + */ + deleteGraph() { + return this._sendCommand("graph.DELETE", [this._graphId]).then(res => { return new ResultSet(res); }); - } - -}; + } +} + +module.exports = RedisGraph; diff --git a/src/resultSet.js b/src/resultSet.js index f01009b14c..f496a9c871 100644 --- a/src/resultSet.js +++ b/src/resultSet.js @@ -1,36 +1,35 @@ -const Statistics = require('./statistics').Statistics, - Record = require('./record'); +const Statistics = require("./statistics"), + Record = require("./record"); /** * Hold a query result */ -module.exports = class ResultSet { - - constructor(resp) { - this._position = 0; - this._statistics = new Statistics(resp[1]); - - let result = resp[0]; - - // Empty result set - if(result === null || result.length === 0) { - this._header = []; - this._totalResults = 0; - this._results = []; - } else { - this._header = result[0]; - this._totalResults = result.length - 1; - this._results = new Array(this._totalResults); - for(let i = 0 ; i < this._totalResults; ++i){ - this._results[i] = new Record(this._header, result[i+1]); - } - } - } - - getHeader(){ - return this._header; - } - +class ResultSet { + constructor(resp) { + this._position = 0; + this._statistics = new Statistics(resp[1]); + + let result = resp[0]; + + // Empty result set + if (result === null || result.length === 0) { + this._header = []; + this._totalResults = 0; + this._results = []; + } else { + this._header = result[0]; + this._totalResults = result.length - 1; + this._results = new Array(this._totalResults); + for (let i = 0; i < this._totalResults; ++i) { + this._results[i] = new Record(this._header, result[i + 1]); + } + } + } + + getHeader() { + return this._header; + } + hasNext() { return this._position < this._totalResults; } @@ -42,4 +41,6 @@ module.exports = class ResultSet { getStatistics() { return this._statistics; } -}; +} + +module.exports = ResultSet; diff --git a/src/statistics.js b/src/statistics.js index b31d7a4ee6..711ee7d36f 100644 --- a/src/statistics.js +++ b/src/statistics.js @@ -1,83 +1,68 @@ -/** - * Different Statistics labels - */ -const Label = Object.freeze({ - LABELS_ADDED: "Labels added", - NODES_CREATED: "Nodes created", - NODES_DELETED: "Nodes deleted", - RELATIONSHIPS_DELETED: "Relationships deleted", - PROPERTIES_SET: "Properties set", - RELATIONSHIPS_CREATED: "Relationships created", - QUERY_INTERNAL_EXECUTION_TIME: "Query internal execution time" -}); +const Label = require("./label"); -module.exports ={ - Label: Label, - Statistics : class Statistics { +class Statistics { + constructor(raw) { + this._raw = raw; + } + getStringValue(label) { + return this.getStatistics()[label]; + } - constructor(raw){ - this._raw = raw; + /** + * Return the query statistics + * + * @return statistics object + */ + getStatistics() { + if (!this._statistics) { + this._statistics = {}; + for (let row of this._raw) { + let touple = row.split(":"); + this._statistics[touple[0]] = touple[1].trim(); } + } + return this._statistics; + } - getStringValue(label) { - return this.getStatistics()[label]; - } + getIntValue(label) { + let value = this.getStringValue(label); + return value ? parseInt(value) : 0; + } - /** - * Return the query statistics - * - * @return statistics object - */ - getStatistics(){ - if(!this._statistics) { - this._statistics = {}; - for(let row of this._raw) { - let touple = row.split(':'); - this._statistics[touple[0]] = touple[1].trim(); - } - } - return this._statistics; - } - getIntValue(label) { - let value = this.getStringValue(label); - return value ? parseInt(value) : 0; - } - - getFloatValue(label) { - let value = this.getStringValue(label); - return value ? parseFloat(value) : 0; - } + getFloatValue(label) { + let value = this.getStringValue(label); + return value ? parseFloat(value) : 0; + } + nodesCreated() { + return this.getIntValue(Label.NODES_CREATED); + } - nodesCreated() { - return this.getIntValue(Label.NODES_CREATED); - } + nodesDeleted() { + return this.getIntValue(Label.NODES_DELETED); + } - nodesDeleted() { - return this.getIntValue(Label.NODES_DELETED); - } + labelsAdded() { + return this.getIntValue(Label.LABELS_ADDED); + } - labelsAdded() { - return this.getIntValue(Label.LABELS_ADDED); - } + relationshipsDeleted() { + return this.getIntValue(Label.RELATIONSHIPS_DELETED); + } - relationshipsDeleted() { - return this.getIntValue(Label.RELATIONSHIPS_DELETED); - } + relationshipsCreated() { + return this.getIntValue(Label.RELATIONSHIPS_CREATED); + } - relationshipsCreated() { - return this.getIntValue(Label.RELATIONSHIPS_CREATED); - } + propertiesSet() { + return this.getIntValue(Label.PROPERTIES_SET); + } - propertiesSet() { - return this.getIntValue(Label.PROPERTIES_SET); - } - - queryExecutionTime() { - return this.getFloatValue(Label.QUERY_INTERNAL_EXECUTION_TIME); - } + queryExecutionTime() { + return this.getFloatValue(Label.QUERY_INTERNAL_EXECUTION_TIME); + } +} - } -} \ No newline at end of file +module.exports = Statistics; diff --git a/test/redisGraphAPITest.js b/test/redisGraphAPITest.js index 9a19b5dfc4..40c346021e 100644 --- a/test/redisGraphAPITest.js +++ b/test/redisGraphAPITest.js @@ -1,64 +1,88 @@ -const assert = require('assert'), -redis = require('redis'), -Label = require('../src/statistics').Label, -RedisGraphAPI = require('../src/redisGraph'); +const assert = require("assert"), + redis = require("redis"), + Label = require("../src/label"), + RedisGraph = require("../src/redisGraph"); describe('RedisGraphAPI Test', () =>{ - const api = new RedisGraphAPI("social"); + const api = new RedisGraph("social"); beforeEach( () => { return api.deleteGraph().catch(()=>{}); }); - it('test bring your client', () => { - return new RedisGraphAPI( "social", redis.createClient()); + it("test bring your client", () => { + return new RedisGraph("social", redis.createClient()); }); - - it('test Create Node', () => { - // Create a node - return api.query("CREATE ({name:'roi',age:32})") - .then( (result) => { - assert.ok(!result.hasNext()); + + it("test Create Node", () => { + // Create a node + return api.query("CREATE ({name:'roi',age:32})").then(result => { + assert.ok(!result.hasNext()); assert.equal(1, result.getStatistics().nodesCreated()); - assert.ifError(result.getStatistics().getStringValue(Label.NODES_DELETED)); - assert.ifError(result.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED)); - assert.ifError(result.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED)); + assert.ifError( + result.getStatistics().getStringValue(Label.NODES_DELETED) + ); + assert.ifError( + result.getStatistics().getStringValue(Label.RELATIONSHIPS_CREATED) + ); + assert.ifError( + result.getStatistics().getStringValue(Label.RELATIONSHIPS_DELETED) + ); assert.equal(2, result.getStatistics().propertiesSet()); assert.ok( result.getStatistics().queryExecutionTime()); // not 0 assert.ok(result.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); // exsits }); }); - it('test Create Labeled Node', () => { + it("test Create Labeled Node", () => { // Create a node with a label - return api.query("CREATE (:human{name:'danny',age:12})") - .then( (result) => { + return api.query("CREATE (:human{name:'danny',age:12})").then(result => { assert.ok(!result.hasNext()); - assert.equal("1", result.getStatistics().getStringValue(Label.NODES_CREATED)); - assert.equal("2", result.getStatistics().getStringValue(Label.PROPERTIES_SET)); - assert.ok(result.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); + assert.equal( + "1", + result.getStatistics().getStringValue(Label.NODES_CREATED) + ); + assert.equal( + "2", + result.getStatistics().getStringValue(Label.PROPERTIES_SET) + ); + assert.ok( + result + .getStatistics() + .getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME) + ); }); }); - it('test Connect Nodes', () => { + it("test Connect Nodes", () => { // Create both source and destination nodes let createResult1 = api.query("CREATE (:person{name:'roi',age:32})"); let createResult2 = api.query("CREATE (:person{name:'amit',age:30})"); // Connect source and destination nodes. - return api.query("MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)") - .then( (matchResult) => { - assert.ok(!matchResult.hasNext()); - assert.ifError(matchResult.getStatistics().getStringValue(Label.NODES_CREATED)); - assert.ifError(matchResult.getStatistics().getStringValue(Label.PROPERTIES_SET)); - assert.equal(1, matchResult.getStatistics().relationshipsCreated()); - assert.equal(0, matchResult.getStatistics().relationshipsDeleted()); - assert.ok(matchResult.getStatistics().getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME)); - }); + return api + .query( + "MATCH (a:person), (b:person) WHERE (a.name = 'roi' AND b.name='amit') CREATE (a)-[:knows]->(a)" + ) + .then(matchResult => { + assert.ok(!matchResult.hasNext()); + assert.ifError( + matchResult.getStatistics().getStringValue(Label.NODES_CREATED) + ); + assert.ifError( + matchResult.getStatistics().getStringValue(Label.PROPERTIES_SET) + ); + assert.equal(1, matchResult.getStatistics().relationshipsCreated()); + assert.equal(0, matchResult.getStatistics().relationshipsDeleted()); + assert.ok( + matchResult + .getStatistics() + .getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME) + ); + }); }); it('test Query', () => { - // Create both source and destination nodes return api.query("CREATE (:qhuman{name:'roi',age:32})") .then( (create1Result) => {