From f4166dedec99382fefda3d9f77bc7f8e4d94675c Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 20 Sep 2017 23:03:28 -0300 Subject: [PATCH 01/24] v1.0.0: Begining definitions for the 1.0 release. --- lib/ogmneo-operation.js | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/ogmneo-operation.js diff --git a/lib/ogmneo-operation.js b/lib/ogmneo-operation.js new file mode 100644 index 0000000..10b010b --- /dev/null +++ b/lib/ogmneo-operation.js @@ -0,0 +1,42 @@ +'use strict'; +let _ = require('lodash'); + +class OGMNeoOperation { + + constructor() { + + } + + get cypher() { + return this._cypher; + } + + set cypher(value) { + if (_.isString(value)) { + this._cypher = value; + } + } + + // can be read or write operation + get type() { + return this._type; + } + + set type(value) { + if (_.isString(value)) { + this._type = value; + } + } + + get resultParser() { + return this._resultParser; + } + + set resultParser(value) { + if (_.isFunction(value)) { + this._resultParser = value; + } + } +} + +module.exports = OGMNeoOperation; \ No newline at end of file From b5bd5644804bd4889c07dd0f5fcdefaf0d1f2268 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 24 Sep 2017 20:11:53 -0300 Subject: [PATCH 02/24] v1.0.0: Defining initial structure of OGMNeoOperation --- __test__/operation-tests.js | 0 lib/ogmneo-operation-executer.js | 105 +++++++++++++++++++++++++++++++ lib/ogmneo-operation.js | 54 ++++++++++++++-- 3 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 __test__/operation-tests.js create mode 100644 lib/ogmneo-operation-executer.js diff --git a/__test__/operation-tests.js b/__test__/operation-tests.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js new file mode 100644 index 0000000..6d5c966 --- /dev/null +++ b/lib/ogmneo-operation-executer.js @@ -0,0 +1,105 @@ +'use strict'; + +const OGMNeoOperation = require('./ogmneo-operation'); +const OGMNeo = require('./ogmneo'); + +const _ = require('lodash'); + +class OGMNeoOperationExecuter { + + constructor() {} + + executeReadOperations(operations) { + try { + this._validateOperations(operations); + return null; + } catch (error) { + return Promise.reject(error); + } + } + + executehWriteOperations(operations) { + try { + this._validateOperations(operations); + return null; + } catch (error) { + return Promise.reject(error); + } + } + + execute(operation) { + if (operation instanceof OGMNeoOperation) { + if (operation.isReadType) { + return this._executeRead(operation); + } else { + return this._executeWrite(operation); + } + } else { + return Promise.reject(new Error('The operation must be a instance of ogmneo.Operation')); + } + } + + _executeRead(operation) { + return new Promise((resolve, reject) => { + let session = OGMNeo.session(); + let readTxResultPromise = session.readTransaction((transaction) => { + if (operation.object != null) { + return transaction.run(operation.cypher, operation.object); + } else { + return transaction.run(operation.cypher); + } + }); + this._handleSingleResultPromise(session, operation, readTxResultPromise, resolve, reject); + }); + } + + //Private functions + + _executeWrite(operation) { + return new Promise((resolve, reject) => { + let session = OGMNeo.session(); + let writeTxResultPromise = session.writeTransaction((transaction) => { + if (operation.object != null) { + return transaction.run(operation.cypher, operation.object); + } else { + return transaction.run(operation.cypher); + } + }); + this._handleSingleResultPromise(session, operation, writeTxResultPromise, resolve, reject); + }); + } + + _handleSingleResultPromise(session, operation, promise, resolve, reject) { + promise.then((result) => { + session.close(); + resolve(this._performResultCallForOperation(operation, result)); + }).catch( (error) => { + reject(error); + }); + } + + _parseResultForOperation(operation, driverResult) { + if (operation.resultParser != null ) { + return operation.resultParser(driverResult); + } else { + return driverResult; + } + } + + _validateOperations(operations, type=null) { + if (_.isArray(operations)) { + for (let op in operations) { + if ((op instanceof OGMNeoOperation) == false) { + throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); + }else if (type != null && op.type != type) { + throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); + } + } + } else { + throw new Error('The parameter operations must be an array'); + } + } + +} + +module.exports = OGMNeoOperationExecuter; \ No newline at end of file diff --git a/lib/ogmneo-operation.js b/lib/ogmneo-operation.js index 10b010b..d30bbba 100644 --- a/lib/ogmneo-operation.js +++ b/lib/ogmneo-operation.js @@ -4,7 +4,29 @@ let _ = require('lodash'); class OGMNeoOperation { constructor() { + this.object = {}; + this.type = OGMNeoOperation.READ; + } + + static get READ() { + return 'READ'; + } + + static get WRITE() { + return 'WRITE'; + } + + static create() { + return new OGMNeoOperation(); + } + + + get isReadType() { + return this.type != null && this.type.toUpperCase() == OGMNeoOperation.READ; + } + get isWriteType() { + return this.type != null && this.type.toUpperCase() == OGMNeoOperation.WRITE; } get cypher() { @@ -15,6 +37,7 @@ class OGMNeoOperation { if (_.isString(value)) { this._cypher = value; } + return this; } // can be read or write operation @@ -23,19 +46,40 @@ class OGMNeoOperation { } set type(value) { - if (_.isString(value)) { + if (this._isValidType(value)){ this._type = value; + } else { + throw new Error('The type cannot be null/undefined and must be a string with either value \'READ\' or \'WRITE\''); + } + return this; + } + + _isValidType(type) { + if (type != null && _.isString(type)) { + let uType = type.toUpperCase(); + return uType == OGMNeoOperation.READ || uType == OGMNeoOperation.WRITE; } + return false; } - get resultParser() { - return this._resultParser; + get then() { + return this._then; } - set resultParser(value) { + set then(value) { if (_.isFunction(value)) { - this._resultParser = value; + this._then = value; } + return this; + } + + get object() { + return this._object; + } + + set object(value) { + this._object = value; + return this; } } From 7141336694aed0d88da646989d965e163f0e81c5 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 24 Sep 2017 20:12:25 -0300 Subject: [PATCH 03/24] v1.0.0: Write initial tests for the ogmneo operation. --- __test__/operation-tests.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/__test__/operation-tests.js b/__test__/operation-tests.js index e69de29..9d09164 100644 --- a/__test__/operation-tests.js +++ b/__test__/operation-tests.js @@ -0,0 +1,35 @@ +'use strict'; + +const test = require('tape'); +const OGMNeoOperation = require('../lib/ogmneo-operation'); +const _ = require('lodash'); + +test('Test create operation', (assert) => { + let opertation = OGMNeoOperation.create() + .cypher('CREATE (n:Label {property: {property}}) RETURN n') + .object({property: 'value'}) + .type(OGMNeoOperation.READ) + .then((result)=> { + return { id: 1, property: 'value' } + }); + + assert.equal(opertation.cypher, 'CREATE (n:Label {property: {property}}) RETURN n'); + assert.equal(opertation.type, OGMNeoOperation.READ); + assert.deepEqual(opertation.object, {property: 'value'} ); + assert.assert(_.isFunction(object.then), 'Then is not a function'); + + assert.end(); +}); + +test('Test operation convenience methods', (assert) => { + let opertation = OGMNeoOperation.create() + .cypher('CREATE (n:Label {property: {property}}) RETURN n') + .object({property: 'value'}) + .type(OGMNeoOperation.READ) + .then((result)=> { + return { id: 1, property: 'value' } + }); + assert.assert(opertation.isRead, 'Then is not a read operation'); + assert.false(opertation.isWrite, 'Then is not a write operation'); + assert.end(); +}); \ No newline at end of file From fd41ab68a050c06d5401abb26cb8041c3aec4212 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 24 Sep 2017 20:37:22 -0300 Subject: [PATCH 04/24] v1.0.0: Implementing ogmneo operation builder and fixes on unit tests. --- __test__/operation-tests.js | 24 +++++++++---------- lib/ogmneo-operation.js | 46 ++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/__test__/operation-tests.js b/__test__/operation-tests.js index 9d09164..f1c984d 100644 --- a/__test__/operation-tests.js +++ b/__test__/operation-tests.js @@ -1,35 +1,35 @@ 'use strict'; const test = require('tape'); -const OGMNeoOperation = require('../lib/ogmneo-operation'); +const { OGMNeoOperation, OGMNeoOperationBuilder } = require('../lib/ogmneo-operation'); const _ = require('lodash'); test('Test create operation', (assert) => { - let opertation = OGMNeoOperation.create() + let operation = OGMNeoOperationBuilder.create() .cypher('CREATE (n:Label {property: {property}}) RETURN n') .object({property: 'value'}) .type(OGMNeoOperation.READ) - .then((result)=> { + .then((result) => { return { id: 1, property: 'value' } - }); + }).build(); - assert.equal(opertation.cypher, 'CREATE (n:Label {property: {property}}) RETURN n'); - assert.equal(opertation.type, OGMNeoOperation.READ); - assert.deepEqual(opertation.object, {property: 'value'} ); - assert.assert(_.isFunction(object.then), 'Then is not a function'); + assert.equal(operation.cypher, 'CREATE (n:Label {property: {property}}) RETURN n'); + assert.equal(operation.type, OGMNeoOperation.READ); + assert.deepEqual(operation.object, {property: 'value'} ); + assert.true(_.isFunction(operation.then), 'Then is not a function'); assert.end(); }); test('Test operation convenience methods', (assert) => { - let opertation = OGMNeoOperation.create() + let operation = OGMNeoOperationBuilder.create() .cypher('CREATE (n:Label {property: {property}}) RETURN n') .object({property: 'value'}) .type(OGMNeoOperation.READ) .then((result)=> { return { id: 1, property: 'value' } - }); - assert.assert(opertation.isRead, 'Then is not a read operation'); - assert.false(opertation.isWrite, 'Then is not a write operation'); + }).build(); + assert.true(operation.isReadType, 'Then is not a read operation'); + assert.false(operation.isWriteType, 'Then is not a write operation'); assert.end(); }); \ No newline at end of file diff --git a/lib/ogmneo-operation.js b/lib/ogmneo-operation.js index d30bbba..3e9b91a 100644 --- a/lib/ogmneo-operation.js +++ b/lib/ogmneo-operation.js @@ -3,9 +3,11 @@ let _ = require('lodash'); class OGMNeoOperation { - constructor() { - this.object = {}; - this.type = OGMNeoOperation.READ; + constructor(cypher, type, object, then) { + this.object = object || {}; + this.cypher = cypher || {}; + this.then = then; + this.type = type || OGMNeoOperation.READ; } static get READ() { @@ -37,7 +39,6 @@ class OGMNeoOperation { if (_.isString(value)) { this._cypher = value; } - return this; } // can be read or write operation @@ -51,7 +52,6 @@ class OGMNeoOperation { } else { throw new Error('The type cannot be null/undefined and must be a string with either value \'READ\' or \'WRITE\''); } - return this; } _isValidType(type) { @@ -70,7 +70,6 @@ class OGMNeoOperation { if (_.isFunction(value)) { this._then = value; } - return this; } get object() { @@ -79,8 +78,39 @@ class OGMNeoOperation { set object(value) { this._object = value; - return this; } } -module.exports = OGMNeoOperation; \ No newline at end of file +class OGMNeoOperationBuilder { + constructor() {} + + static create() { + return new OGMNeoOperationBuilder(); + } + + type(value) { + this._type = value; + return this; + } + + cypher(value) { + this._cypher = value; + return this; + } + + object(value) { + this._object = value; + return this; + } + + then(value) { + this._then = value; + return this; + } + + build() { + return new OGMNeoOperation(this._cypher, this._type, this._object, this._then); + } +} + +module.exports = { OGMNeoOperation : OGMNeoOperation, OGMNeoOperationBuilder: OGMNeoOperationBuilder }; \ No newline at end of file From 2e440ed22526c1032ee7417381a28837007d95b5 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 24 Sep 2017 21:06:38 -0300 Subject: [PATCH 05/24] v1.0.0: createOperation on node. --- __test__/operation-executer-tests.js | 14 ++++++++++++++ lib/ogmneo-node.js | 25 +++++++++++++++++++++++++ lib/ogmneo-operation-executer.js | 8 ++++---- lib/ogmneo-operation.js | 4 +++- 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 __test__/operation-executer-tests.js diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js new file mode 100644 index 0000000..29ff6b0 --- /dev/null +++ b/__test__/operation-executer-tests.js @@ -0,0 +1,14 @@ +'use strict'; + +const test = require('tape'); +const { OGMNeoOperation, OGMNeoOperationBuilder } = require('../lib/ogmneo-operation'); +const OGMNeoOperationExecuter = require('../lib/ogmneo-operation-executer'); +const _ = require('lodash'); + +test('Test Invalid Operation', (assert) => { + OGMNeoOperationExecuter.execute({}).catch((error) => { + assert.equal(error.message, 'The operation must be a instance of ogmneo.Operation'); + assert.end(); + }); + +}); \ No newline at end of file diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index dbd9b72..b458285 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -6,6 +6,7 @@ const OGMNeo = require('./ogmneo'); const OGMNeoQuery = require('./ogmneo-query'); const OGMNeoObjectParse = require('./ogmneo-parse'); const Printer = require('./ogmneo-printer'); +const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./ogmneo-operation'); /** * @class OGMNeoNode @@ -39,6 +40,30 @@ class OGMNeoNode { }); } + /** + * Creates a node on neo4j. + * + * @static + * @param {object} node - The literal object with node propeperties. + * @param {string} [label=null] - The label of the node. Default null is a node without label. + * @returns {OGMNeoOperation} Create node operation that can be executed later. + */ + + static createOperation(node, label = null) { + let value = _.omitBy(node, _.isUndefined); + OGMNeoObjectParse.parseProperties(value); + let objectString = OGMNeoObjectParse.objectString(value); + let labelCypher = (!_.isEmpty(label) && _.isString(label)) ? `:${label}` : ''; + let cypher = `CREATE (n${labelCypher} ${objectString}) RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + let record = _.first(result.records); + return OGMNeoObjectParse.parseRecordNode(record, 'n'); + }); + } + /** * Updates a node on neo4j. * diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 6d5c966..9db2792 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -1,6 +1,6 @@ 'use strict'; -const OGMNeoOperation = require('./ogmneo-operation'); +const {OGMNeoOperation }= require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); const _ = require('lodash'); @@ -9,7 +9,7 @@ class OGMNeoOperationExecuter { constructor() {} - executeReadOperations(operations) { + static executeReadOperations(operations) { try { this._validateOperations(operations); return null; @@ -18,7 +18,7 @@ class OGMNeoOperationExecuter { } } - executehWriteOperations(operations) { + static executehWriteOperations(operations) { try { this._validateOperations(operations); return null; @@ -27,7 +27,7 @@ class OGMNeoOperationExecuter { } } - execute(operation) { + static execute(operation) { if (operation instanceof OGMNeoOperation) { if (operation.isReadType) { return this._executeRead(operation); diff --git a/lib/ogmneo-operation.js b/lib/ogmneo-operation.js index 3e9b91a..01ccabf 100644 --- a/lib/ogmneo-operation.js +++ b/lib/ogmneo-operation.js @@ -1,6 +1,8 @@ 'use strict'; let _ = require('lodash'); - +/** + * @class OGMNeoOperation + */ class OGMNeoOperation { constructor(cypher, type, object, then) { From 4bca4718b27e39e5d792b57b1fb1eab70f743995 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 24 Sep 2017 23:47:32 -0300 Subject: [PATCH 06/24] v1.0.0: Adding more tests and fix some implementation. --- __test__/operation-executer-tests.js | 21 +++++++++++++++++++++ lib/ogmneo-operation-executer.js | 18 +++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index 29ff6b0..225d1fc 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -10,5 +10,26 @@ test('Test Invalid Operation', (assert) => { assert.equal(error.message, 'The operation must be a instance of ogmneo.Operation'); assert.end(); }); +}); +test('Test invalid array of operations', (assert) => { + OGMNeoOperationExecuter.executeReadOperations({}).catch((error) => { + assert.equal(error.message, 'The parameter operations must be an array'); + assert.end(); + }); +}); + +test('Test invalid item on operations array', (assert) => { + OGMNeoOperationExecuter.executeReadOperations([{}]).catch((error) => { + assert.equal(error.message, 'The parameter operations must be an array that contains only instances of ogmneo.Operation'); + assert.end(); + }); +}); + +test('Test invalid operation type on operations array', (assert) => { + let operation = OGMNeoOperationBuilder.create().type(OGMNeoOperation.WRITE).build(); + OGMNeoOperationExecuter.executeReadOperations([operation]).catch((error) => { + assert.equal(error.message, 'The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : READ'); + assert.end(); + }); }); \ No newline at end of file diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 9db2792..62f01e2 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -11,7 +11,7 @@ class OGMNeoOperationExecuter { static executeReadOperations(operations) { try { - this._validateOperations(operations); + this._validateOperations(operations, OGMNeoOperation.READ); return null; } catch (error) { return Promise.reject(error); @@ -20,7 +20,7 @@ class OGMNeoOperationExecuter { static executehWriteOperations(operations) { try { - this._validateOperations(operations); + this._validateOperations(operations, OGMNeoOperation.WRITE); return null; } catch (error) { return Promise.reject(error); @@ -39,7 +39,7 @@ class OGMNeoOperationExecuter { } } - _executeRead(operation) { + static _executeRead(operation) { return new Promise((resolve, reject) => { let session = OGMNeo.session(); let readTxResultPromise = session.readTransaction((transaction) => { @@ -55,7 +55,7 @@ class OGMNeoOperationExecuter { //Private functions - _executeWrite(operation) { + static _executeWrite(operation) { return new Promise((resolve, reject) => { let session = OGMNeo.session(); let writeTxResultPromise = session.writeTransaction((transaction) => { @@ -69,7 +69,7 @@ class OGMNeoOperationExecuter { }); } - _handleSingleResultPromise(session, operation, promise, resolve, reject) { + static _handleSingleResultPromise(session, operation, promise, resolve, reject) { promise.then((result) => { session.close(); resolve(this._performResultCallForOperation(operation, result)); @@ -78,7 +78,7 @@ class OGMNeoOperationExecuter { }); } - _parseResultForOperation(operation, driverResult) { + static _parseResultForOperation(operation, driverResult) { if (operation.resultParser != null ) { return operation.resultParser(driverResult); } else { @@ -86,12 +86,12 @@ class OGMNeoOperationExecuter { } } - _validateOperations(operations, type=null) { + static _validateOperations(operations, type=null) { if (_.isArray(operations)) { - for (let op in operations) { + for (let op of operations) { if ((op instanceof OGMNeoOperation) == false) { throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); - }else if (type != null && op.type != type) { + } else if (type != null && op.type != type) { throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); } } From 6fcb7c1185728d07a0393b614f28bb5c98ed489c Mon Sep 17 00:00:00 2001 From: Luciano Date: Mon, 25 Sep 2017 19:49:51 -0300 Subject: [PATCH 07/24] v1.0.0: Updating dependencies versions --- lib/ogmneo-operation-executer.js | 28 +++++++++++++++++++--------- package.json | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 62f01e2..13b2a0f 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -1,6 +1,6 @@ 'use strict'; -const {OGMNeoOperation }= require('./ogmneo-operation'); +const { OGMNeoOperation }= require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); const _ = require('lodash'); @@ -9,14 +9,24 @@ class OGMNeoOperationExecuter { constructor() {} - static executeReadOperations(operations) { - try { - this._validateOperations(operations, OGMNeoOperation.READ); - return null; - } catch (error) { - return Promise.reject(error); - } - } + // static executeReadOperations(operations) { + // try { + // this._validateOperations(operations, OGMNeoOperation.READ); + // if (_.isEmpty(operations)) { + // return Promise.resolve([]); + // } + // let session = OGMNeo.session(); + // let readTxResultPromise = session.readTransaction((transaction) => { + // let results = operations.map((operation)=> { + // let cypher = operation.cypher; + + // }); + // }); + // return null; + // } catch (error) { + // return Promise.reject(error); + // } + // } static executehWriteOperations(operations) { try { diff --git a/package.json b/package.json index 90347f0..9ea4993 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "license": "MIT", "dependencies": { "lodash": "^4.17.4", - "neo4j-driver": "^1.2.0" + "neo4j-driver": "^1.4.1" }, "devDependencies": { "eslint": "^3.19.0", From 80c4db3cd973b63b27f15b31a4e8fbce251e1929 Mon Sep 17 00:00:00 2001 From: Luciano Date: Mon, 25 Sep 2017 19:50:57 -0300 Subject: [PATCH 08/24] v1.0.0: Fixing comented lines --- lib/ogmneo-operation-executer.js | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 13b2a0f..a2556b4 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -9,24 +9,24 @@ class OGMNeoOperationExecuter { constructor() {} - // static executeReadOperations(operations) { - // try { - // this._validateOperations(operations, OGMNeoOperation.READ); - // if (_.isEmpty(operations)) { - // return Promise.resolve([]); - // } - // let session = OGMNeo.session(); - // let readTxResultPromise = session.readTransaction((transaction) => { - // let results = operations.map((operation)=> { - // let cypher = operation.cypher; + static executeReadOperations(operations) { + try { + this._validateOperations(operations, OGMNeoOperation.READ); + if (_.isEmpty(operations)) { + return Promise.resolve([]); + } + // let session = OGMNeo.session(); + // let readTxResultPromise = session.readTransaction((transaction) => { + // let results = operations.map((operation)=> { + // let cypher = operation.cypher; - // }); - // }); - // return null; - // } catch (error) { - // return Promise.reject(error); - // } - // } + // }); + // }); + return null; + } catch (error) { + return Promise.reject(error); + } + } static executehWriteOperations(operations) { try { From 6374af41c949784ae540ee0628d0ae96312e530c Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 27 Sep 2017 22:33:11 -0300 Subject: [PATCH 09/24] v1.0.0: Implementing first version of executeReadOperations and executeWriteOperations on OGMNeoOperationExecuter --- __test__/operation-executer-tests.js | 17 ++++++++++ lib/ogmneo-node.js | 25 +++------------ lib/ogmneo-operation-executer.js | 47 ++++++++++++++++++++-------- 3 files changed, 56 insertions(+), 33 deletions(-) diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index 225d1fc..2ac9f7d 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -3,6 +3,7 @@ const test = require('tape'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('../lib/ogmneo-operation'); const OGMNeoOperationExecuter = require('../lib/ogmneo-operation-executer'); +const OGMNeoNode = require('../lib/ogmneo-node'); const _ = require('lodash'); test('Test Invalid Operation', (assert) => { @@ -32,4 +33,20 @@ test('Test invalid operation type on operations array', (assert) => { assert.equal(error.message, 'The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : READ'); assert.end(); }); +}); + +//Testing OGMNeoOperationExecuter.executeWriteOperations executor +test('Test invalid operation type on operations array', (assert) => { + let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); + let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); + + OGMNeoOperationExecuter.executeWriteOperations([createUser1, createUser2]).then((result) => { + let created1 = result[0]; + let created2 = result[1]; + assert.notEqual(created1.id, undefined); + assert.equal(created1.name, 'Ayrton Senna'); + assert.notEqual(created2.id, undefined); + assert.equal(created2.name, 'Alain Prost'); + assert.end(); + }); }); \ No newline at end of file diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index b458285..e44135e 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -7,7 +7,7 @@ const OGMNeoQuery = require('./ogmneo-query'); const OGMNeoObjectParse = require('./ogmneo-parse'); const Printer = require('./ogmneo-printer'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./ogmneo-operation'); - +const OGMNeoOperationExecuter = require('./ogmneo-operation-executer'); /** * @class OGMNeoNode */ @@ -21,23 +21,8 @@ class OGMNeoNode { * @returns {Promise} Created node literal object if fulfilled, or some neo4j error if rejected. */ static create(node, label = null) { - let value = _.omitBy(node, _.isUndefined); - OGMNeoObjectParse.parseProperties(value); - return new Promise((resolve, reject) => { - let objectString = OGMNeoObjectParse.objectString(value); - let labelCypher = (!_.isEmpty(label) && _.isString(label)) ? `:${label}` : ''; - let cypher = `CREATE (n${labelCypher} ${objectString}) RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher, value)); - writeTxResultPromise.then((result) => { - let record = _.first(result.records); - session.close(); - resolve(OGMNeoObjectParse.parseRecordNode(record, 'n')); - }).catch((error) => { - reject(error); - }); - }); + let operation = this.createOperation(node, label); + return OGMNeoOperationExecuter.execute(operation); } /** @@ -48,7 +33,7 @@ class OGMNeoNode { * @param {string} [label=null] - The label of the node. Default null is a node without label. * @returns {OGMNeoOperation} Create node operation that can be executed later. */ - + static createOperation(node, label = null) { let value = _.omitBy(node, _.isUndefined); OGMNeoObjectParse.parseProperties(value); @@ -61,7 +46,7 @@ class OGMNeoNode { .type(OGMNeoOperation.WRITE).then((result) => { let record = _.first(result.records); return OGMNeoObjectParse.parseRecordNode(record, 'n'); - }); + }).build(); } /** diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index a2556b4..23069bf 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -15,28 +15,49 @@ class OGMNeoOperationExecuter { if (_.isEmpty(operations)) { return Promise.resolve([]); } - // let session = OGMNeo.session(); - // let readTxResultPromise = session.readTransaction((transaction) => { - // let results = operations.map((operation)=> { - // let cypher = operation.cypher; - - // }); - // }); - return null; + + let session = OGMNeo.session(); + let readTxResultPromise = session.readTransaction((transaction) => { + return Promise.all(operations.map(operation => transaction.run(operation.cypher, operation.object))); + }); + return this._handleMultipleOperationsResult(operations, readTxResultPromise); + } catch (error) { return Promise.reject(error); } } - static executehWriteOperations(operations) { + static executeWriteOperations(operations) { try { this._validateOperations(operations, OGMNeoOperation.WRITE); - return null; + if (_.isEmpty(operations)) { + return Promise.resolve([]); + } + let session = OGMNeo.session(); + let writeTxResultPromise = session.writeTransaction((transaction) => { + return Promise.all(operations.map(operation => transaction.run(operation.cypher, operation.object))); + }); + return this._handleMultipleOperationsResult(operations, writeTxResultPromise); } catch (error) { return Promise.reject(error); } } + static _handleMultipleOperationsResult(operations, promise) { + return promise.then((results) => { + let parsedResults = new Array(operations.length); + for (let i = 0; i < operations.length; i++) { + let operation = operations[i]; + if (operation.then != null) { + parsedResults[i] = operation.then(results[i]); + } else { + parsedResults[i] = results[i]; + } + } + return parsedResults; + }); + } + static execute(operation) { if (operation instanceof OGMNeoOperation) { if (operation.isReadType) { @@ -82,15 +103,15 @@ class OGMNeoOperationExecuter { static _handleSingleResultPromise(session, operation, promise, resolve, reject) { promise.then((result) => { session.close(); - resolve(this._performResultCallForOperation(operation, result)); + resolve(this._parseResultForOperation(operation, result)); }).catch( (error) => { reject(error); }); } static _parseResultForOperation(operation, driverResult) { - if (operation.resultParser != null ) { - return operation.resultParser(driverResult); + if (operation.then != null ) { + return operation.then(driverResult); } else { return driverResult; } From 27f539fd59a2076ee4295b6326e7c5702d03a76b Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 27 Sep 2017 22:53:34 -0300 Subject: [PATCH 10/24] v1.0.0: Adding docs to the Operation executer class. --- index.js | 8 +++-- lib/ogmneo-operation-executer.js | 53 +++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 51a8550..128882a 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ const cypher = require('./lib/ogmneo-cypher'); const index = require('./lib/ogmneo-index'); const where = require('./lib/ogmneo-where'); const relationQuery = require('./lib/ogmneo-relation-query'); +const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./lib/ogmneo-operation'); module.exports = { Connection: connection, @@ -16,7 +17,8 @@ module.exports = { OGMNeoIndex: index, OGMNeoWhere: where, OGMNeoRelationQuery: relationQuery, - + OGMNeoOperation: OGMNeoOperation, + OGMNeoOperationBuilder: OGMNeoOperationBuilder, //Simplified names Node: nodes, Query: query, @@ -24,5 +26,7 @@ module.exports = { Cypher: cypher, Index: index, Where: where, - RelationQuery: relationQuery + RelationQuery: relationQuery, + Operation: OGMNeoOperation, + OperationBuilder: OGMNeoOperationBuilder }; diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 23069bf..b594f37 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -4,11 +4,20 @@ const { OGMNeoOperation }= require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); const _ = require('lodash'); - +/** + * @class OGMNeoOperationExecuter + */ class OGMNeoOperationExecuter { constructor() {} + /** + * Executes an array of READ operations in a single transaction and returns the results. + * + * @static + * @param {array} operations - The array of operations that should be executed. + * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. + */ static executeReadOperations(operations) { try { this._validateOperations(operations, OGMNeoOperation.READ); @@ -27,6 +36,13 @@ class OGMNeoOperationExecuter { } } + /** + * Executes an array of WRITE operations in a single transaction and returns the results. + * + * @static + * @param {array} operations - The array of operations that should be executed. + * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. + */ static executeWriteOperations(operations) { try { this._validateOperations(operations, OGMNeoOperation.WRITE); @@ -43,6 +59,27 @@ class OGMNeoOperationExecuter { } } + /** + * Executes an READ of WRITE ogmneo.Operation and returns a result. + * + * @static + * @param {operation} operation - . + * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. + */ + static execute(operation) { + if (operation instanceof OGMNeoOperation) { + if (operation.isReadType) { + return this._executeRead(operation); + } else { + return this._executeWrite(operation); + } + } else { + return Promise.reject(new Error('The operation must be a instance of ogmneo.Operation')); + } + } + + + // Private API static _handleMultipleOperationsResult(operations, promise) { return promise.then((results) => { let parsedResults = new Array(operations.length); @@ -58,18 +95,6 @@ class OGMNeoOperationExecuter { }); } - static execute(operation) { - if (operation instanceof OGMNeoOperation) { - if (operation.isReadType) { - return this._executeRead(operation); - } else { - return this._executeWrite(operation); - } - } else { - return Promise.reject(new Error('The operation must be a instance of ogmneo.Operation')); - } - } - static _executeRead(operation) { return new Promise((resolve, reject) => { let session = OGMNeo.session(); @@ -84,8 +109,6 @@ class OGMNeoOperationExecuter { }); } - //Private functions - static _executeWrite(operation) { return new Promise((resolve, reject) => { let session = OGMNeo.session(); From c28a47bf7a80adbbaa624104fa546313fe0fa33c Mon Sep 17 00:00:00 2001 From: Luciano Date: Fri, 29 Sep 2017 22:32:39 -0300 Subject: [PATCH 11/24] v1.0.0: Remove some methods and changing architecture --- lib/ogmneo-operation-executer.js | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index b594f37..7bbe2b1 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -59,6 +59,10 @@ class OGMNeoOperationExecuter { } } + static executeInReadTransaction(callback) { + + } + /** * Executes an READ of WRITE ogmneo.Operation and returns a result. * diff --git a/package.json b/package.json index 9ea4993..6cfe79c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ogmneo", - "version": "0.4.9", + "version": "1.0.0", "description": "OGM(object-graph mapping) abstraction layer for neo4j", "main": "index.js", "scripts": { From f863143dd9598d7cd6414ee203503d57aa32b968 Mon Sep 17 00:00:00 2001 From: Luciano Date: Fri, 29 Sep 2017 22:33:06 -0300 Subject: [PATCH 12/24] v1.0.0: Some files not added --- __test__/operation-executer-tests.js | 63 ++++++------- lib/ogmneo-operation-executer.js | 130 +++++++++++++-------------- 2 files changed, 95 insertions(+), 98 deletions(-) diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index 2ac9f7d..86b6195 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -4,6 +4,8 @@ const test = require('tape'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('../lib/ogmneo-operation'); const OGMNeoOperationExecuter = require('../lib/ogmneo-operation-executer'); const OGMNeoNode = require('../lib/ogmneo-node'); +const OGMNeo = require('../lib/ogmneo'); + const _ = require('lodash'); test('Test Invalid Operation', (assert) => { @@ -13,40 +15,41 @@ test('Test Invalid Operation', (assert) => { }); }); -test('Test invalid array of operations', (assert) => { - OGMNeoOperationExecuter.executeReadOperations({}).catch((error) => { - assert.equal(error.message, 'The parameter operations must be an array'); +//Testing OGMNeoOperationExecuter.write +test('Test invalid write type on operation', (assert) => { + OGMNeoOperationExecuter.write(null, (transaction) => { + //Nothing to test + }).catch((error) => { + assert.equal(error.message, 'You must provide a session object. See ogmneo.Connection.session()'); assert.end(); - }); + }); }); -test('Test invalid item on operations array', (assert) => { - OGMNeoOperationExecuter.executeReadOperations([{}]).catch((error) => { - assert.equal(error.message, 'The parameter operations must be an array that contains only instances of ogmneo.Operation'); +//Testing OGMNeoOperationExecuter.read +test('Test invalid read type on operation', (assert) => { + OGMNeoOperationExecuter.read(null, (transaction) => { + //Nothing to test + }).catch((error) => { + assert.equal(error.message, 'You must provide a session object. See ogmneo.Connection.session()'); assert.end(); - }); + }); }); -test('Test invalid operation type on operations array', (assert) => { - let operation = OGMNeoOperationBuilder.create().type(OGMNeoOperation.WRITE).build(); - OGMNeoOperationExecuter.executeReadOperations([operation]).catch((error) => { - assert.equal(error.message, 'The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : READ'); - assert.end(); - }); -}); -//Testing OGMNeoOperationExecuter.executeWriteOperations executor -test('Test invalid operation type on operations array', (assert) => { - let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); - let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); - - OGMNeoOperationExecuter.executeWriteOperations([createUser1, createUser2]).then((result) => { - let created1 = result[0]; - let created2 = result[1]; - assert.notEqual(created1.id, undefined); - assert.equal(created1.name, 'Ayrton Senna'); - assert.notEqual(created2.id, undefined); - assert.equal(created2.name, 'Alain Prost'); - assert.end(); - }); -}); \ No newline at end of file + // let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); + // let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); + // OGMNeo.session().readTransaction((transaction) => { + // let r = transaction.run('MATCH (n) RETURN n').then((result)=> { + // console.log('result => ', result); + // }); + // console.log(r); + // }); + // OGMNeoOperationExecuter.executeWriteOperations([createUser1, createUser2]).then((result) => { + // let created1 = result[0]; + // let created2 = result[1]; + // assert.notEqual(created1.id, undefined); + // assert.equal(created1.name, 'Ayrton Senna'); + // assert.notEqual(created2.id, undefined); + // assert.equal(created2.name, 'Alain Prost'); + // assert.end(); + // }); \ No newline at end of file diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index 7bbe2b1..d6d457b 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -1,6 +1,6 @@ 'use strict'; -const { OGMNeoOperation }= require('./ogmneo-operation'); +const { OGMNeoOperation } = require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); const _ = require('lodash'); @@ -9,73 +9,55 @@ const _ = require('lodash'); */ class OGMNeoOperationExecuter { - constructor() {} + constructor() { } /** - * Executes an array of READ operations in a single transaction and returns the results. + * Executes an READ or WRITE ogmneo.Operation and returns a result. * * @static - * @param {array} operations - The array of operations that should be executed. + * @param {operation} operation - . * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ - static executeReadOperations(operations) { - try { - this._validateOperations(operations, OGMNeoOperation.READ); - if (_.isEmpty(operations)) { - return Promise.resolve([]); - } - - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction((transaction) => { - return Promise.all(operations.map(operation => transaction.run(operation.cypher, operation.object))); + static read(session, transactional) { + if (session != null && session.readTransaction != null ) { + return session.readTransaction((transaction) => { + return transactional(session, transaction); }); - return this._handleMultipleOperationsResult(operations, readTxResultPromise); - - } catch (error) { - return Promise.reject(error); + } else { + return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); } } /** - * Executes an array of WRITE operations in a single transaction and returns the results. + * Executes an READ or WRITE ogmneo.Operation and returns a result. * * @static - * @param {array} operations - The array of operations that should be executed. + * @param {operation} operation - . * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ - static executeWriteOperations(operations) { - try { - this._validateOperations(operations, OGMNeoOperation.WRITE); - if (_.isEmpty(operations)) { - return Promise.resolve([]); - } - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction((transaction) => { - return Promise.all(operations.map(operation => transaction.run(operation.cypher, operation.object))); + static write(session, transactional) { + if (session != null && session.readTransaction != null ) { + return session.readTransaction((transaction) => { + return transactional(session, transaction); }); - return this._handleMultipleOperationsResult(operations, writeTxResultPromise); - } catch (error) { - return Promise.reject(error); + } else { + return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); } } - static executeInReadTransaction(callback) { - - } - /** - * Executes an READ of WRITE ogmneo.Operation and returns a result. + * Executes an READ or WRITE ogmneo.Operation and returns a result. * * @static * @param {operation} operation - . * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ - static execute(operation) { + static execute(operation, transaction = null) { if (operation instanceof OGMNeoOperation) { if (operation.isReadType) { - return this._executeRead(operation); + return this._executeRead(operation, transaction); } else { - return this._executeWrite(operation); + return this._executeWrite(operation, transaction); } } else { return Promise.reject(new Error('The operation must be a instance of ogmneo.Operation')); @@ -96,61 +78,73 @@ class OGMNeoOperationExecuter { } } return parsedResults; - }); + }); } - static _executeRead(operation) { + static _executeRead(operation, transaction) { return new Promise((resolve, reject) => { - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction((transaction) => { - if (operation.object != null) { - return transaction.run(operation.cypher, operation.object); - } else { - return transaction.run(operation.cypher); - } - }); - this._handleSingleResultPromise(session, operation, readTxResultPromise, resolve, reject); + if (transaction != null) { + let promise = this.runInTransaction(operation, transaction); + this._handleSingleResultPromise(null, operation, promise, resolve, reject); + } else { + let session = OGMNeo.session(); + session.readTransaction((transaction) => { + let promise = this.runInTransaction(operation, transaction); + this._handleSingleResultPromise(session, operation, promise, resolve, reject); + }); + } }); } - static _executeWrite(operation) { + static _executeWrite(operation, transaction) { return new Promise((resolve, reject) => { - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction((transaction) => { - if (operation.object != null) { - return transaction.run(operation.cypher, operation.object); - } else { - return transaction.run(operation.cypher); - } - }); - this._handleSingleResultPromise(session, operation, writeTxResultPromise, resolve, reject); + if (transaction != null) { + let promise = this.runInTransaction(operation, transaction); + this._handleSingleResultPromise(null, operation, promise, resolve, reject); + } else { + let session = OGMNeo.session(); + session.writeTransaction((transaction) => { + let promise = this.runInTransaction(operation, transaction); + this._handleSingleResultPromise(session, operation, promise, resolve, reject); + }); + } }); } + static runInTransaction(operation, transaction) { + if (operation.object != null) { + return transaction.run(operation.cypher, operation.object); + } else { + return transaction.run(operation.cypher); + } + } + static _handleSingleResultPromise(session, operation, promise, resolve, reject) { promise.then((result) => { - session.close(); + if (session != null) { + session.close(); + } resolve(this._parseResultForOperation(operation, result)); - }).catch( (error) => { + }).catch((error) => { reject(error); }); } - + static _parseResultForOperation(operation, driverResult) { - if (operation.then != null ) { + if (operation.then != null) { return operation.then(driverResult); } else { return driverResult; } } - static _validateOperations(operations, type=null) { + static _validateOperations(operations, type = null) { if (_.isArray(operations)) { for (let op of operations) { if ((op instanceof OGMNeoOperation) == false) { - throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); + throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); } else if (type != null && op.type != type) { - throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); + throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); } } } else { From 72f395b0a7fccb452b834caa9ed0ebd6febd66b8 Mon Sep 17 00:00:00 2001 From: Luciano Date: Fri, 29 Sep 2017 23:42:27 -0300 Subject: [PATCH 13/24] v1.0.0: Fix on read an write on transactional API Operation Executer. --- __test__/node-tests.js | 13 +++- __test__/operation-executer-tests.js | 21 +++++ lib/ogmneo-node.js | 111 ++++++++++++++++----------- lib/ogmneo-operation-executer.js | 39 ++-------- 4 files changed, 102 insertions(+), 82 deletions(-) diff --git a/__test__/node-tests.js b/__test__/node-tests.js index 2aade7f..2ec300e 100644 --- a/__test__/node-tests.js +++ b/__test__/node-tests.js @@ -57,11 +57,18 @@ test('Test FAIL for query param updateMany node', (assert) => { }); }); +test('Test FAIL for query param updateMany not object newProperties', (assert) => { + OGMNeoNode.updateMany('', '').catch((error) => { + assert.equal(error.message, 'The new properties must be an object'); + assert.end(); + }); +}); + test('Test empty new properties updateMany node', (assert) => { let query = OGMQueryBuilder.create('test', new OGMNeoWhere('name', {$eq: 'name1'})); - OGMNeoNode.updateMany(query, {}).then((updateNodes) => { - assert.equal(updateNodes.length, 0); - assert.end(); + OGMNeoNode.updateMany(query, {}).catch((error) => { + assert.equal(error.message, 'You must provide at least one property with NO undefined values to update'); + assert.end(); }); }); diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index 86b6195..fdf97cc 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -35,6 +35,27 @@ test('Test invalid read type on operation', (assert) => { }); }); +//Testing OGMNeoOperationExecuter.read +test('Test write type on operation', (assert) => { + let create = OGMNeoNode.createOperation({name: 'Ayrton Senna', carNumber: 12 }, 'Person'); + let session = OGMNeo.session(); + OGMNeoOperationExecuter.write(session, (transaction) => { + return OGMNeoOperationExecuter.execute(create, transaction) + .then((created) => { + assert.equal(created.name, 'Ayrton Senna'); + assert.equal(created.carNumber, 12); + let id = created.id; + created.carNumber = 1; + let update = OGMNeoNode.updateOperation(created); + return OGMNeoOperationExecuter.execute(update, transaction); + }); + }).then((result) => { + session.close(); + assert.equal(result.name, 'Ayrton Senna'); + assert.equal(result.carNumber, 1); + assert.end(); + }); +}); // let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); // let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index e44135e..676d9ec 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -57,30 +57,41 @@ class OGMNeoNode { * @returns {Promise.} Updated node literal object if fulfilled, or error if node.id is invalid or some neo4j error if rejected. */ static update(node) { + try { + let operation = this.updateOperation(node); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Update node operation on neo4j. + * + * @static + * @param {object} node - The literal object with node propeperties and required node.id. + * @returns {OGMNeoOperation} Create node operation that can be executed later. + */ + static updateOperation(node) { let value = _.omitBy(node, _.isUndefined); OGMNeoObjectParse.parseProperties(value); - return new Promise((resolve, reject) => { - if (value && value.id != undefined && _.isInteger(value.id)) { - let objectString = OGMNeoObjectParse.objectString(value); - let cypher = `MATCH (n) WHERE ID(n)=${node.id} SET n+=${objectString} RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher, value)); - writeTxResultPromise.then((result) => { - let record = _.first(result.records); - session.close(); - resolve(OGMNeoObjectParse.parseRecordNode(record, 'n')); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('Node must have an integer id to be updated')); - } - }); + if (value && value.id != undefined && _.isInteger(value.id)) { + let objectString = OGMNeoObjectParse.objectString(value); + let cypher = `MATCH (n) WHERE ID(n)=${node.id} SET n+=${objectString} RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + let record = _.first(result.records); + return OGMNeoObjectParse.parseRecordNode(record, 'n'); + }).build(); + } else { + throw new Error('Node must have an integer id to be updated'); + } } /** - * Update new properties on every node that matches the. + * Update new properties on every node that matches the query. * * @static * @param {OGMNeoQuery} query - The query to filter the nodes. @@ -88,36 +99,46 @@ class OGMNeoNode { * @returns {Promise.} Updated nodes if fulfilled or some neo4j error if rejected. */ static updateMany(query, newProperties) { - return new Promise((resolve, reject) => { - if (_.isObject(newProperties)) { - let value = _.omitBy(newProperties, _.isUndefined); - if (!_.isEmpty(value)) { - OGMNeoObjectParse.parseProperties(value); - if (query instanceof OGMNeoQuery) { - let objectString = OGMNeoObjectParse.objectString(value); - let cypher = `${query.matchCypher()} SET n+=${objectString} RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher, value)); - writeTxResultPromise.then((result) => { - session.close(); - let nodes = result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); - resolve(nodes); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object must be an instance of OGMNeoQuery')); - } + try { + let operation = this.updateManyOperation(query, newProperties); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Update new properties on every node that matches the . + * + * @static + * @param {OGMNeoQuery} query - The query to filter the nodes. + * @param {object} newProperties - NEW properties. + * @returns {Promise.} Updated nodes if fulfilled or some neo4j error if rejected. + */ + static updateManyOperation(query, newProperties) { + if (_.isObject(newProperties)) { + let value = _.omitBy(newProperties, _.isUndefined); + if (!_.isEmpty(value)) { + OGMNeoObjectParse.parseProperties(value); + if (query instanceof OGMNeoQuery) { + let objectString = OGMNeoObjectParse.objectString(value); + let cypher = `${query.matchCypher()} SET n+=${objectString} RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); + }).build(); } else { - resolve([]); + throw new Error('The query object must be an instance of OGMNeoQuery'); } } else { - resolve([]); + throw new Error('You must provide at least one property with NO undefined values to update'); } - }); - } - + } else { + throw new Error('The new properties must be an object'); + } + } /** * Deletes a node on neo4j. diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index d6d457b..e1a7dba 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -2,8 +2,8 @@ const { OGMNeoOperation } = require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); +const Printer = require('./ogmneo-printer'); -const _ = require('lodash'); /** * @class OGMNeoOperationExecuter */ @@ -21,7 +21,7 @@ class OGMNeoOperationExecuter { static read(session, transactional) { if (session != null && session.readTransaction != null ) { return session.readTransaction((transaction) => { - return transactional(session, transaction); + return transactional(transaction); }); } else { return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); @@ -37,8 +37,8 @@ class OGMNeoOperationExecuter { */ static write(session, transactional) { if (session != null && session.readTransaction != null ) { - return session.readTransaction((transaction) => { - return transactional(session, transaction); + return session.writeTransaction((transaction) => { + return transactional(transaction); }); } else { return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); @@ -66,21 +66,6 @@ class OGMNeoOperationExecuter { // Private API - static _handleMultipleOperationsResult(operations, promise) { - return promise.then((results) => { - let parsedResults = new Array(operations.length); - for (let i = 0; i < operations.length; i++) { - let operation = operations[i]; - if (operation.then != null) { - parsedResults[i] = operation.then(results[i]); - } else { - parsedResults[i] = results[i]; - } - } - return parsedResults; - }); - } - static _executeRead(operation, transaction) { return new Promise((resolve, reject) => { if (transaction != null) { @@ -112,6 +97,7 @@ class OGMNeoOperationExecuter { } static runInTransaction(operation, transaction) { + Printer.printCypherIfEnabled(operation.cypher); if (operation.object != null) { return transaction.run(operation.cypher, operation.object); } else { @@ -137,21 +123,6 @@ class OGMNeoOperationExecuter { return driverResult; } } - - static _validateOperations(operations, type = null) { - if (_.isArray(operations)) { - for (let op of operations) { - if ((op instanceof OGMNeoOperation) == false) { - throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); - } else if (type != null && op.type != type) { - throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); - } - } - } else { - throw new Error('The parameter operations must be an array'); - } - } - } module.exports = OGMNeoOperationExecuter; \ No newline at end of file From e792a27b4ccde1169478c23f3fc5ec2b2fcd29b1 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sat, 30 Sep 2017 00:11:24 -0300 Subject: [PATCH 14/24] v1.0.0: Enough of js for today let go back to the beautiful and typed language called swift. --- lib/ogmneo-node.js | 53 +++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index 676d9ec..2a151b0 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -70,7 +70,8 @@ class OGMNeoNode { * * @static * @param {object} node - The literal object with node propeperties and required node.id. - * @returns {OGMNeoOperation} Create node operation that can be executed later. + * @returns {OGMNeoOperation} Update node operation that can be executed later. + * @throws {Error} Will throw an error if the node.id was not integer or not exists. */ static updateOperation(node) { let value = _.omitBy(node, _.isUndefined); @@ -108,12 +109,15 @@ class OGMNeoNode { } /** - * Update new properties on every node that matches the . + * Returns an operation that updates new properties on every node that matches the query. * * @static * @param {OGMNeoQuery} query - The query to filter the nodes. * @param {object} newProperties - NEW properties. - * @returns {Promise.} Updated nodes if fulfilled or some neo4j error if rejected. + * @returns {OGMNeoOperation} Operation that updates new properties on every node that matches the query. + * @throws {Error} Will throw an error if the query is not an instance of ogmneo.Query. + * @throws {Error} Will throw an error if newProperties is not an object. + * @throws {Error} Will throw an error if newProperties don't have at least one property with NO undefined values to update. */ static updateManyOperation(query, newProperties) { if (_.isObject(newProperties)) { @@ -148,22 +152,33 @@ class OGMNeoNode { * @returns {Promise.} True if fulfilled and found and delete node, false if not found object to delete, or error if node.id is invalid or some neo4j error if rejected. */ static delete(node) { - return new Promise((resolve, reject) => { - if (node && node.id != undefined && _.isInteger(node.id)) { - let cypher = `MATCH (n) WHERE ID(n)=${node.id} DELETE n RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - resolve(!_.isEmpty(result.records)); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('Node must to have an non-nil property id to be deleted')); - } - }); + try { + let operation = this.deleteOperation(node); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Deletes a node on neo4j. + * + * @static + * @param {object} node - The literal object with node propeperties and required node.id. + * @returns {OGMNeoOperation} Operation that updates deletes a node. + * @throws {Error} Will throw an error if the node.id was not integer or not exists. + */ + static deleteOperation(node) { + if (node && node.id != undefined && _.isInteger(node.id)) { + let cypher = `MATCH (n) WHERE ID(n)=${node.id} DELETE n RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE).then((result) => { + return !_.isEmpty(result.records); + }).build(); + } else { + throw new Error('Node must to have an non-nil property id to be deleted'); + } } /** From 11cd428822b53082a9fe79471cf5d8cfc3ff131c Mon Sep 17 00:00:00 2001 From: Luciano Date: Mon, 2 Oct 2017 22:37:19 -0300 Subject: [PATCH 15/24] v1.0.0: Implementing some method that return operations on node. --- lib/ogmneo-node.js | 361 +++++++++++++++++++++++++++------------------ 1 file changed, 216 insertions(+), 145 deletions(-) diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index 2a151b0..939f219 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -41,12 +41,12 @@ class OGMNeoNode { let labelCypher = (!_.isEmpty(label) && _.isString(label)) ? `:${label}` : ''; let cypher = `CREATE (n${labelCypher} ${objectString}) RETURN n`; return OGMNeoOperationBuilder.create() - .cypher(cypher) - .object(value) - .type(OGMNeoOperation.WRITE).then((result) => { - let record = _.first(result.records); - return OGMNeoObjectParse.parseRecordNode(record, 'n'); - }).build(); + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + let record = _.first(result.records); + return OGMNeoObjectParse.parseRecordNode(record, 'n'); + }).build(); } /** @@ -80,12 +80,12 @@ class OGMNeoNode { let objectString = OGMNeoObjectParse.objectString(value); let cypher = `MATCH (n) WHERE ID(n)=${node.id} SET n+=${objectString} RETURN n`; return OGMNeoOperationBuilder.create() - .cypher(cypher) - .object(value) - .type(OGMNeoOperation.WRITE).then((result) => { - let record = _.first(result.records); - return OGMNeoObjectParse.parseRecordNode(record, 'n'); - }).build(); + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + let record = _.first(result.records); + return OGMNeoObjectParse.parseRecordNode(record, 'n'); + }).build(); } else { throw new Error('Node must have an integer id to be updated'); } @@ -108,17 +108,17 @@ class OGMNeoNode { } } - /** - * Returns an operation that updates new properties on every node that matches the query. - * - * @static - * @param {OGMNeoQuery} query - The query to filter the nodes. - * @param {object} newProperties - NEW properties. - * @returns {OGMNeoOperation} Operation that updates new properties on every node that matches the query. - * @throws {Error} Will throw an error if the query is not an instance of ogmneo.Query. - * @throws {Error} Will throw an error if newProperties is not an object. - * @throws {Error} Will throw an error if newProperties don't have at least one property with NO undefined values to update. - */ + /** + * Returns an operation that updates new properties on every node that matches the query. + * + * @static + * @param {OGMNeoQuery} query - The query to filter the nodes. + * @param {object} newProperties - NEW properties. + * @returns {OGMNeoOperation} Operation that updates new properties on every node that matches the query. + * @throws {Error} Will throw an error if the query is not an instance of ogmneo.Query. + * @throws {Error} Will throw an error if newProperties is not an object. + * @throws {Error} Will throw an error if newProperties don't have at least one property with NO undefined values to update. + */ static updateManyOperation(query, newProperties) { if (_.isObject(newProperties)) { let value = _.omitBy(newProperties, _.isUndefined); @@ -128,11 +128,11 @@ class OGMNeoNode { let objectString = OGMNeoObjectParse.objectString(value); let cypher = `${query.matchCypher()} SET n+=${objectString} RETURN n`; return OGMNeoOperationBuilder.create() - .cypher(cypher) - .object(value) - .type(OGMNeoOperation.WRITE).then((result) => { - return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); - }).build(); + .cypher(cypher) + .object(value) + .type(OGMNeoOperation.WRITE).then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); + }).build(); } else { throw new Error('The query object must be an instance of OGMNeoQuery'); } @@ -142,7 +142,7 @@ class OGMNeoNode { } else { throw new Error('The new properties must be an object'); } - } + } /** * Deletes a node on neo4j. @@ -165,17 +165,17 @@ class OGMNeoNode { * * @static * @param {object} node - The literal object with node propeperties and required node.id. - * @returns {OGMNeoOperation} Operation that updates deletes a node. + * @returns {OGMNeoOperation} Operation that deletes a node. * @throws {Error} Will throw an error if the node.id was not integer or not exists. */ static deleteOperation(node) { if (node && node.id != undefined && _.isInteger(node.id)) { let cypher = `MATCH (n) WHERE ID(n)=${node.id} DELETE n RETURN n`; return OGMNeoOperationBuilder.create() - .cypher(cypher) - .type(OGMNeoOperation.WRITE).then((result) => { - return !_.isEmpty(result.records); - }).build(); + .cypher(cypher) + .type(OGMNeoOperation.WRITE).then((result) => { + return !_.isEmpty(result.records); + }).build(); } else { throw new Error('Node must to have an non-nil property id to be deleted'); } @@ -189,23 +189,36 @@ class OGMNeoNode { * @returns {Promise.} Number of nodes deleted if fulfilled or some neo4j error if rejected. */ static deleteMany(query) { - return new Promise((resolve, reject) => { - if (query instanceof OGMNeoQuery) { - let cypher = `${query.matchCypher()} DELETE n RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); - writeTxResultPromise.then((result) => { - session.close(); - resolve(result.records.length); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object must be an instance of OGMNeoQuery')); - } - }); + try { + let operation = this.deleteManyOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } } + + /** + * Creates an operation that deletes every node that matches the query. + * + * @static + * @param {OGMNeoQuery} query - The query to filter the nodes. + * @returns {OGMNeoOperation} Operation that deletes a node. + * @throws {Error} Will throw an error if the query was not a instance of ogmneo.Query. + */ + static deleteManyOperation(query) { + if (query instanceof OGMNeoQuery) { + let cypher = `${query.matchCypher()} DELETE n RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .then((result) => { + return result.records.length; + }).build(); + } else { + throw new Error('The query object must be an instance of OGMNeoQuery'); + } + } + /** * Retrive node with id. * @@ -215,23 +228,35 @@ class OGMNeoNode { or error if id is invalid or some neo4j error if rejected. */ static nodeWithId(id) { - return new Promise((resolve, reject) => { - if (_.isInteger(id)) { - let cypher = `MATCH (n) WHERE ID(n)=${id} RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); - writeTxResultPromise.then((result) => { + try { + let operation = this.nodeWithIdOperation(id); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Create a READ operation that retrives a node with id. + * + * @static + * @param {integer} id - The id of node that's wanted. + * @returns {OGMNeoOperation} Operation retrives a node. + * @throws {Error} Will throw an error if id was not an integer value. + */ + static nodeWithIdOperation(id) { + if (_.isInteger(id)) { + let cypher = `MATCH (n) WHERE ID(n)=${id} RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { let record = _.first(result.records); - session.close(); - resolve(OGMNeoObjectParse.parseRecordNode(record, 'n')); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('You must provide an non-null integer id property to find the node')); - } - }); + return OGMNeoObjectParse.parseRecordNode(record, 'n'); + }).build(); + } else { + throw new Error('You must provide an non-null integer id property to find the node'); + } } /** @@ -243,29 +268,43 @@ class OGMNeoNode { or error if id is invalid or some neo4j error if rejected. */ static manyWithIds(ids) { + try { + let operation = this.manyWithIdsOperation(ids); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation to retrive nodes with ids. + * + * @static + * @param {array} ids - The ids of nodes that are wanted. + * @returns {OGMNeoOperation} Operation that query the nodes with the ids. + * @throws {Error} Will throw an error if you don't provide any valid id to retrive. + * @throws {Error} Will throw an error if the ids are not an array. + + */ + static manyWithIdsOperation(ids) { if (_.isArray(ids)) { - return new Promise((resolve, reject) => { - let validIds = ids.filter(id => _.isInteger(id)); - if (_.isEmpty(validIds)) { - resolve([]); - } else { - let idsQuery = validIds.reduce((result, current) => { - return result + ((result === '') ? '' : ',') + ` ${current}`; - }, ''); - let cypher = `MATCH (n) WHERE ID(n) IN [${idsQuery}] RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - resolve(result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n'))); - }).catch((error) => { - reject(error); - }); - } - }); - }else { - return Promise.reject(new Error('The parameter must be an array of ids')); + let validIds = ids.filter(id => _.isInteger(id)); + if (_.isEmpty(validIds)) { + throw new Error('You must provide at least one valid(integer) id to query'); + } else { + let idsQuery = validIds.reduce((result, current) => { + return result + ((result === '') ? '' : ',') + ` ${current}`; + }, ''); + let cypher = `MATCH (n) WHERE ID(n) IN [${idsQuery}] RETURN n`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')) + }).build(); + } + } else { + throw new Error('The parameter must be an array of ids'); } } @@ -288,27 +327,35 @@ class OGMNeoNode { * @returns {Promise.} Count of nodes if fulfilled, some neo4j error if rejected. */ static count(query) { - return new Promise((resolve, reject) => { - if (query && query instanceof OGMNeoQuery) { - let cypher = query.countCypher(); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - session.run(cypher).subscribe({ - onNext: (record) => { - resolve(record.get('count').low); - }, - onCompleted: () => { - session.close(); - }, - onError: (error) => { - reject(error); - } - }); - } else { - reject(new Error('A OGMNeoQuery object must to be provided')); - } - }); + try { + let operation = this.countOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + /** + * Creates a READ operation count of nodes with the query. + * + * @static + * @param {OGMNeoQuery} query - The query to filter nodes that have to be counted. + * @returns {OGMNeoOperation} Operation that query the count of the nodes with query. + * @throws {Error} Will throw an error if the query was not a instance of ogmneo.Query. + */ + static countOperation(query) { + if (query && query instanceof OGMNeoQuery) { + let cypher = query.countCypher(); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + let record = _.first(result.records); + return (record != null) ? record.get('count').low : 0; + }).build(); + } else { + throw new Error('A OGMNeoQuery object must to be provided'); + } } /** @@ -319,28 +366,35 @@ class OGMNeoNode { * @returns {Promise.} Nodes if fulfilled, some neo4j error if rejected. */ static find(query) { - return new Promise((resolve, reject) => { - if (query && query instanceof OGMNeoQuery) { - let cypher = query.queryCypher(); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction((transaction) => { - return transaction.run(cypher); - }); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve([]); - } else { - resolve(result.records.map((record) => OGMNeoObjectParse.parseRecordNode(record, 'n'))); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('A OGMNeoQuery object must to be provided')); - } - }); + try { + let operation = this.findOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + + /** + * Operation for find nodes filtered by the query parameter. + * + * @static + * @param {OGMNeoQuery} query - The query to filter nodes that have to be returned. + * @returns {OGMNeoOperation} Operation that returns the nodes with query. + * @throws {Error} Will throw an error if the query was not a instance of ogmneo.Query. + */ + static findOperation(query) { + if (query && query instanceof OGMNeoQuery) { + let cypher = query.queryCypher(); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + return result.records.map((record) => OGMNeoObjectParse.parseRecordNode(record, 'n')); + }).build(); + } else { + throw new Error('A OGMNeoQuery object must to be provided'); + } } /** @@ -351,18 +405,35 @@ class OGMNeoNode { * @returns {Promise.} Node found if fulfilled, some neo4j error if rejected. */ static findOne(query) { - return new Promise((resolve, reject) => { - if (query && query instanceof OGMNeoQuery) { - query.limit(1); - this.find(query).then((nodes) => { - resolve(_.first(nodes)); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('A OGMNeoQuery object must to be provided')); - } - }); + try { + let operation = this.findOneOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Find one node filtered by query parameter. + * + * @static + * @param {OGMNeoQuery} query - The query to filter nodes that have to be returned. + * @returns {OGMNeoOperation} Operation that returns the node with query. + * @throws {Error} Will throw an error if the query was not a instance of ogmneo.Query. + */ + static findOneOperation(query) { + if (query && query instanceof OGMNeoQuery) { + query.limit(1); + return OGMNeoOperationBuilder.create() + .cypher(query.queryCypher()) + .type(OGMNeoOperation.READ) + .then((result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.parseRecordNode(record, 'n') : null; + }).build(); + } else { + throw new Error('A OGMNeoQuery object must to be provided'); + } } /** * Adding label to a node. @@ -407,7 +478,7 @@ class OGMNeoNode { return Promise.reject(new Error('The nodeId must be an integer value')); } } - + /** * Adding label to nodes. * @@ -423,7 +494,7 @@ class OGMNeoNode { if (params) { let cypher = `MATCH (n) WHERE ID(n) IN ${params} SET n:${label} RETURN n`; Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); + let session = OGMNeo.session(); let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); writeTxResultPromise.then((result) => { session.close(); @@ -458,7 +529,7 @@ class OGMNeoNode { if (params) { let cypher = `MATCH (n:${label}) WHERE ID(n) IN ${params} REMOVE n:${label} RETURN n`; Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); + let session = OGMNeo.session(); let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); writeTxResultPromise.then((result) => { session.close(); From 3e793e117506f32ce6f476026beda9f83191c33e Mon Sep 17 00:00:00 2001 From: Luciano Date: Mon, 2 Oct 2017 23:39:52 -0300 Subject: [PATCH 16/24] v1.0.0: Adding operations suport on all the ogmneo-node methods. --- __test__/node-tests.js | 8 +- lib/ogmneo-node.js | 202 ++++++++++++++++++++++++++--------------- 2 files changed, 134 insertions(+), 76 deletions(-) diff --git a/__test__/node-tests.js b/__test__/node-tests.js index 2ec300e..1a70899 100644 --- a/__test__/node-tests.js +++ b/__test__/node-tests.js @@ -243,8 +243,8 @@ test('Test FAIL add label ids not array', (assert) => { }); test('Test add label empty nodesIds', (assert) => { - OGMNeoNode.addLabelToNodes('label', []).then((nodes) => { - assert.equal(nodes.length, 0); + OGMNeoNode.addLabelToNodes('label', []).catch((error) => { + assert.equal(error.message, 'You must provide at least one valid id to this operation'); assert.end(); }); }); @@ -280,8 +280,8 @@ test('Test FAIL remove label ids not array', (assert) => { }); test('Test remove label empty nodesIds', (assert) => { - OGMNeoNode.removeLabelFromNodes('label', []).then((nodes) => { - assert.equal(nodes.length, 0); + OGMNeoNode.removeLabelFromNodes('label', []).catch((error) => { + assert.equal(error.message, 'You must provide at least one valid id to this operation'); assert.end(); }); }); diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index 939f219..3db3b96 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -444,16 +444,34 @@ class OGMNeoNode { * @returns {Promise.} Node(if node exists) or null(if not exists) if fulfilled, some error if rejected. */ static addLabelToNode(label, nodeId) { + try { + let operation = this.addLabelToNodeOperation(label, nodeId); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation to adding a label to a node. + * + * @static + * @param {string} label - The label to be added to the node. + * @param {integer} nodeId - The id of the node to add the label. + * @returns {OGMNeoOperation} Operation that adds a label. + * @throws {Error} Will throw an error if the nodeId was not an integer value. + * @throws {Error} Will throw an error if the label was anything diferent than an non-empty string. + */ + static addLabelToNodeOperation(label, nodeId) { if (_.isInteger(nodeId)) { - return new Promise((resolve, reject) => { - this.addLabelToNodes(label, [nodeId]).then((results) => { - resolve(_.first(results)); - }).catch((error) => { - reject(error); - }); - }); + let operation = this.addLabelToNodesOperation(label, [nodeId]); + operation.then = (result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.parseRecordNode(record, 'n') : null; + }; + return operation; } else { - return Promise.reject(new Error('The nodeId must be an integer value')); + throw new Error('The nodeId must be an integer value'); } } @@ -466,16 +484,34 @@ class OGMNeoNode { * @returns {Promise.} Node(if node exists) or null(if not exists) if fulfilled, some error if rejected. */ static removeLabelFromNode(label, nodeId) { + try { + let operation = this.removeLabelFromNodeOperation(label, nodeId); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation to remove a label from a node. + * + * @static + * @param {string} label - The label to be removed from the node. + * @param {integer} nodeId - The id of the node to remove the label from. + * @returns {OGMNeoOperation} Operation that removes a label. + * @throws {Error} Will throw an error if the nodeId was not an integer value. + * @throws {Error} Will throw an error if the label was anything diferent than an non-empty string. + */ + static removeLabelFromNodeOperation(label, nodeId) { if (_.isInteger(nodeId)) { - return new Promise((resolve, reject) => { - this.removeLabelFromNodes(label, [nodeId]).then((results) => { - resolve(_.first(results)); - }).catch((error) => { - reject(error); - }); - }); + let operation = this.removeLabelFromNodesOperation(label, [nodeId]); + operation.then = (result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.parseRecordNode(record, 'n') : null; + }; + return operation; } else { - return Promise.reject(new Error('The nodeId must be an integer value')); + throw new Error('The nodeId must be an integer value'); } } @@ -484,33 +520,45 @@ class OGMNeoNode { * * @static * @param {string} label - The label to be added to the node. - * @param {integer} nodesIds - The ids of the nodes to add the label. + * @param {array} nodesIds - The ids of the nodes to add the label. * @returns {Promise} Nodes(if nodes exists) or null(if not exists) if fulfilled, some error if rejected. */ static addLabelToNodes(label, nodesIds) { + try { + let operation = this.addLabelToNodesOperation(label, nodesIds); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation that adds a label to nodes. + * + * @static + * @param {string} label - The label to be added to the node. + * @param {array} nodesIds - The ids of the nodes to add the label. + * @returns {OGMNeoOperation} Operation that removes a label. + * @throws {Error} Will throw an error if you don't provide at least one valid id to this operation. + * @throws {Error} Will throw an error if the label was anything diferent than an non-empty string. + */ + static addLabelToNodesOperation(label, nodesIds) { if (_.isString(label) && !_.isEmpty(label)) { - return new Promise((resolve, reject) => { - this._validateAndBuildParams(nodesIds).then((params) => { - if (params) { - let cypher = `MATCH (n) WHERE ID(n) IN ${params} SET n:${label} RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); - writeTxResultPromise.then((result) => { - session.close(); - resolve(result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n'))); - }).catch((error) => { - reject(error); - }); - } else { - resolve([]); - } - }).catch((error) => { - reject(error); - }); - }); + let params = this._validateAndBuildParams(nodesIds); + if (params != null) { + let cypher = `MATCH (n) WHERE ID(n) IN ${params} SET n:${label} RETURN n`; + return OGMNeoOperationBuilder + .create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); + }).build(); + } else { + throw new Error('You must provide at least one valid id to this operation'); + } } else { - return Promise.reject(new Error('label must be a non empty string')); + throw new Error('label must be a non empty string'); } } @@ -519,51 +567,61 @@ class OGMNeoNode { * * @static * @param {string} label - The label to be removed from the nodes. - * @param {integer} nodeId - The ids of the nodes to remove the label from. + * @param {array} nodeIds - The ids of the nodes to remove the label from. * @returns {Promise.} Nodes(if nodes exists) or null(if not exists) if fulfilled, some error if rejected. */ static removeLabelFromNodes(label, nodesIds) { + try { + let operation = this.removeLabelFromNodesOperation(label, nodesIds); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation that removes a label from nodes. + * + * @static + * @param {string} label - The label to be removed from the nodes. + * @param {array} nodeIds - The ids of the nodes to remove the label from. + * @returns {OGMNeoOperation} Operation that removes a label from many nodes. + * @throws {Error} Will throw an error if you don't provide at least one valid id to this operation. + * @throws {Error} Will throw an error if the label was anything diferent than an non-empty string. + */ + static removeLabelFromNodesOperation(label, nodesIds) { if (_.isString(label) && !_.isEmpty(label)) { - return new Promise((resolve, reject) => { - this._validateAndBuildParams(nodesIds).then((params) => { - if (params) { - let cypher = `MATCH (n:${label}) WHERE ID(n) IN ${params} REMOVE n:${label} RETURN n`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let writeTxResultPromise = session.writeTransaction(transaction => transaction.run(cypher)); - writeTxResultPromise.then((result) => { - session.close(); - resolve(result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n'))); - }).catch((error) => { - reject(error); - }); - } else { - resolve([]); - } - }).catch((error) => { - reject(error); - }); - }); + let params = this._validateAndBuildParams(nodesIds); + if (params) { + let cypher = `MATCH (n:${label}) WHERE ID(n) IN ${params} REMOVE n:${label} RETURN n`; + return OGMNeoOperationBuilder + .create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRecordNode(record, 'n')); + }).build(); + } else { + throw new Error('You must provide at least one valid id to this operation'); + } } else { - return Promise.reject(new Error('label must be a non empty string')); + throw new Error('label must be a non empty string'); } } static _validateAndBuildParams(nodesIds) { if (_.isArray(nodesIds)) { - return new Promise((resolve) => { - let validIds = nodesIds.filter(id => _.isInteger(id)); - if (_.isEmpty(validIds)) { - resolve(null); - } else { - let parameters = validIds.reduce((result, current) => { - return result + `${(result === '') ? '' : ','} ${current}`; - }, ''); - resolve(`[${parameters}]`); - } - }); + let validIds = nodesIds.filter(id => _.isInteger(id)); + if (_.isEmpty(validIds)) { + return null; + } else { + let parameters = validIds.reduce((result, current) => { + return result + `${(result === '') ? '' : ','} ${current}`; + }, ''); + return `[${parameters}]`; + } } else { - return Promise.reject(new Error('nodesIds must be an array')); + throw new Error('nodesIds must be an array'); } } } From 142a4b77876912a81f51f47b99a8190df09ebd17 Mon Sep 17 00:00:00 2001 From: Luciano Date: Tue, 3 Oct 2017 20:07:17 -0300 Subject: [PATCH 17/24] v1.0.0: Implement batching read and write operations. --- __test__/operation-executer-tests.js | 72 ++++++++++------------- lib/ogmneo-node.js | 4 +- lib/ogmneo-operation-executer.js | 87 ++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 53 deletions(-) diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index fdf97cc..48450de 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -5,6 +5,8 @@ const { OGMNeoOperation, OGMNeoOperationBuilder } = require('../lib/ogmneo-opera const OGMNeoOperationExecuter = require('../lib/ogmneo-operation-executer'); const OGMNeoNode = require('../lib/ogmneo-node'); const OGMNeo = require('../lib/ogmneo'); +const OGMNeoQuery = require('../lib/ogmneo-query'); +const OGMNeoWhere = require('../lib/ogmneo-where'); const _ = require('lodash'); @@ -15,31 +17,10 @@ test('Test Invalid Operation', (assert) => { }); }); -//Testing OGMNeoOperationExecuter.write -test('Test invalid write type on operation', (assert) => { - OGMNeoOperationExecuter.write(null, (transaction) => { - //Nothing to test - }).catch((error) => { - assert.equal(error.message, 'You must provide a session object. See ogmneo.Connection.session()'); - assert.end(); - }); -}); - -//Testing OGMNeoOperationExecuter.read -test('Test invalid read type on operation', (assert) => { - OGMNeoOperationExecuter.read(null, (transaction) => { - //Nothing to test - }).catch((error) => { - assert.equal(error.message, 'You must provide a session object. See ogmneo.Connection.session()'); - assert.end(); - }); -}); - //Testing OGMNeoOperationExecuter.read test('Test write type on operation', (assert) => { let create = OGMNeoNode.createOperation({name: 'Ayrton Senna', carNumber: 12 }, 'Person'); - let session = OGMNeo.session(); - OGMNeoOperationExecuter.write(session, (transaction) => { + OGMNeoOperationExecuter.write((transaction) => { return OGMNeoOperationExecuter.execute(create, transaction) .then((created) => { assert.equal(created.name, 'Ayrton Senna'); @@ -50,27 +31,38 @@ test('Test write type on operation', (assert) => { return OGMNeoOperationExecuter.execute(update, transaction); }); }).then((result) => { - session.close(); assert.equal(result.name, 'Ayrton Senna'); assert.equal(result.carNumber, 1); assert.end(); }); }); - // let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); - // let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); - // OGMNeo.session().readTransaction((transaction) => { - // let r = transaction.run('MATCH (n) RETURN n').then((result)=> { - // console.log('result => ', result); - // }); - // console.log(r); - // }); - // OGMNeoOperationExecuter.executeWriteOperations([createUser1, createUser2]).then((result) => { - // let created1 = result[0]; - // let created2 = result[1]; - // assert.notEqual(created1.id, undefined); - // assert.equal(created1.name, 'Ayrton Senna'); - // assert.notEqual(created2.id, undefined); - // assert.equal(created2.name, 'Alain Prost'); - // assert.end(); - // }); \ No newline at end of file +test('Test batch write type operations', (assert) => { + let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); + let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); + + OGMNeoOperationExecuter.batchWriteOperations([createUser1, createUser2]).then((result) => { + let created1 = result[0]; + let created2 = result[1]; + assert.notEqual(created1.id, undefined); + assert.equal(created1.name, 'Ayrton Senna'); + assert.notEqual(created2.id, undefined); + assert.equal(created2.name, 'Alain Prost'); + assert.end(); + }); +}); + +test('Test batch read type operations', (assert) => { + let query1 = OGMNeoNode.findOneOperation(OGMNeoQuery.create('Person').where(OGMNeoWhere.create('name', { $eq: 'Ayrton Senna' }))); + let query2 = OGMNeoNode.findOneOperation(OGMNeoQuery.create('Person').where(OGMNeoWhere.create('name', { $eq: 'Alain Prost' }))); + + OGMNeoOperationExecuter.batchReadOperations([query1, query2]).then((result) => { + let found1 = result[0]; + let found2 = result[1]; + assert.notEqual(found1.id, undefined); + assert.equal(found1.name, 'Ayrton Senna'); + assert.notEqual(found2.id, undefined); + assert.equal(found2.name, 'Alain Prost'); + assert.end(); + }); +}); \ No newline at end of file diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index 3db3b96..a1a01fa 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -2,10 +2,8 @@ const _ = require('lodash'); -const OGMNeo = require('./ogmneo'); const OGMNeoQuery = require('./ogmneo-query'); const OGMNeoObjectParse = require('./ogmneo-parse'); -const Printer = require('./ogmneo-printer'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./ogmneo-operation'); const OGMNeoOperationExecuter = require('./ogmneo-operation-executer'); /** @@ -66,7 +64,7 @@ class OGMNeoNode { } /** - * Update node operation on neo4j. + * Update node operation. * * @static * @param {object} node - The literal object with node propeperties and required node.id. diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index e1a7dba..ce17bf6 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -3,6 +3,7 @@ const { OGMNeoOperation } = require('./ogmneo-operation'); const OGMNeo = require('./ogmneo'); const Printer = require('./ogmneo-printer'); +const _ = require('lodash'); /** * @class OGMNeoOperationExecuter @@ -11,6 +12,49 @@ class OGMNeoOperationExecuter { constructor() { } + /** + * Batches an array of READ operations in a single transaction and returns the results. + * + * @static + * @param {array} operations - The array of operations that should be executed. + * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. + */ + static batchReadOperations(operations) { + try { + this._validateOperations(operations, OGMNeoOperation.READ); + if (_.isEmpty(operations)) { + return Promise.resolve([]); + } + return this.read((transaction) => { + return Promise.all(operations.map(operation => this.execute(operation, transaction))); + }); + + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Batches an array of WRITE operations in a single transaction and returns the results. + * + * @static + * @param {array} operations - The array of operations that should be executed. + * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. + */ + static batchWriteOperations(operations) { + try { + this._validateOperations(operations, OGMNeoOperation.WRITE); + if (_.isEmpty(operations)) { + return Promise.resolve([]); + } + return this.write((transaction) => { + return Promise.all(operations.map(operation => this.execute(operation, transaction))); + }); + } catch (error) { + return Promise.reject(error); + } + } + /** * Executes an READ or WRITE ogmneo.Operation and returns a result. * @@ -18,14 +62,19 @@ class OGMNeoOperationExecuter { * @param {operation} operation - . * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ - static read(session, transactional) { - if (session != null && session.readTransaction != null ) { + static read(transactional) { + return new Promise((resolve, reject) => { + let session = OGMNeo.session(); return session.readTransaction((transaction) => { return transactional(transaction); + }).then((result) => { + session.close(); + resolve(result); + }).catch((error)=> { + reject(error); }); - } else { - return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); - } + }); + } /** @@ -35,14 +84,18 @@ class OGMNeoOperationExecuter { * @param {operation} operation - . * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ - static write(session, transactional) { - if (session != null && session.readTransaction != null ) { + static write(transactional) { + return new Promise((resolve, reject) => { + let session = OGMNeo.session(); return session.writeTransaction((transaction) => { return transactional(transaction); + }).then((result) => { + session.close(); + resolve(result); + }).catch((error)=> { + reject(error); }); - } else { - return Promise.reject(new Error('You must provide a session object. See ogmneo.Connection.session()')); - } + }); } /** @@ -123,6 +176,20 @@ class OGMNeoOperationExecuter { return driverResult; } } + + static _validateOperations(operations, type=null) { + if (_.isArray(operations)) { + for (let op of operations) { + if ((op instanceof OGMNeoOperation) == false) { + throw new Error('The parameter operations must be an array that contains only instances of ogmneo.Operation'); + } else if (type != null && op.type != type) { + throw new Error(`The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : ${type}`); + } + } + } else { + throw new Error('The parameter operations must be an array'); + } + } } module.exports = OGMNeoOperationExecuter; \ No newline at end of file From e2d6401c8d33cd714dcfb6c5fdaa3f4a08be45d9 Mon Sep 17 00:00:00 2001 From: Luciano Date: Tue, 3 Oct 2017 20:20:20 -0300 Subject: [PATCH 18/24] v1.0.0: Batching operations validation tests. --- __test__/operation-executer-tests.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/__test__/operation-executer-tests.js b/__test__/operation-executer-tests.js index 48450de..d9fcbe8 100644 --- a/__test__/operation-executer-tests.js +++ b/__test__/operation-executer-tests.js @@ -17,7 +17,7 @@ test('Test Invalid Operation', (assert) => { }); }); -//Testing OGMNeoOperationExecuter.read +//Testing OGMNeoOperationExecuter.write test('Test write type on operation', (assert) => { let create = OGMNeoNode.createOperation({name: 'Ayrton Senna', carNumber: 12 }, 'Person'); OGMNeoOperationExecuter.write((transaction) => { @@ -65,4 +65,21 @@ test('Test batch read type operations', (assert) => { assert.equal(found2.name, 'Alain Prost'); assert.end(); }); -}); \ No newline at end of file +}); + +test('Test validate batch operations', (assert) => { + let query1 = OGMNeoNode.findOneOperation(OGMNeoQuery.create('Person').where(OGMNeoWhere.create('name', { $eq: 'Ayrton Senna' }))); + + assert.throws(() => { + OGMNeoOperationExecuter._validateOperations('',OGMNeoOperation.READ); + }, /The parameter operations must be an array/); + + assert.throws(() => { + OGMNeoOperationExecuter._validateOperations([''],OGMNeoOperation.READ); + }, /The parameter operations must be an array that contains only instances of ogmneo.Operation/); + + assert.throws(() => { + OGMNeoOperationExecuter._validateOperations([query1], OGMNeoOperation.WRITE); + }, /The parameter operations must be an array that contains only instances of ogmneo.Operation that have type : WRITE/); + assert.end(); +}); From f5dd3d429abeef6b2db20451741d1a1e1844443b Mon Sep 17 00:00:00 2001 From: Luciano Date: Tue, 3 Oct 2017 21:26:26 -0300 Subject: [PATCH 19/24] v1.0.0: Creating operations on the relation class. --- __test__/relation-tests.js | 4 +- lib/ogmneo-node.js | 1 + lib/ogmneo-relation.js | 190 +++++++++++++++++++++++-------------- 3 files changed, 122 insertions(+), 73 deletions(-) diff --git a/__test__/relation-tests.js b/__test__/relation-tests.js index afd4449..72bf40a 100644 --- a/__test__/relation-tests.js +++ b/__test__/relation-tests.js @@ -106,8 +106,8 @@ test('Test empty newProperties UPDATE MANY', (assert) => { let node2 = nodes[1]; let query = OGMNeoRelationQuery.create('relatedto').relationWhere(OGMNeoWhere.create('property', { $eq: 'c' })); OGMNeoRelation.updateMany({}, query) - .then((updatedRelations) => { - assert.equal(updatedRelations.length, 0); + .catch((error) => { + assert.equal(error.message, 'newProperties must be an object with at least one valid property to update'); assert.end(); }); }); diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index a1a01fa..f582df6 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -6,6 +6,7 @@ const OGMNeoQuery = require('./ogmneo-query'); const OGMNeoObjectParse = require('./ogmneo-parse'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./ogmneo-operation'); const OGMNeoOperationExecuter = require('./ogmneo-operation-executer'); + /** * @class OGMNeoNode */ diff --git a/lib/ogmneo-relation.js b/lib/ogmneo-relation.js index fc00d76..8be8d0d 100644 --- a/lib/ogmneo-relation.js +++ b/lib/ogmneo-relation.js @@ -4,6 +4,8 @@ const OGMNeo = require('./ogmneo'); const OGMNeoObjectParse = require('./ogmneo-parse'); const OGMNeoRelationQuery = require('./ogmneo-relation-query'); const Printer = require('./ogmneo-printer'); +const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./ogmneo-operation'); +const OGMNeoOperationExecuter = require('./ogmneo-operation-executer'); const _ = require('lodash'); @@ -23,33 +25,53 @@ class OGMRelation { * @returns {Promise.} Created relation literal object if fulfilled, or some neo4j error if rejected. */ static relate(nodeId, type, otherNodeId, properties = {}, unique = false) { + try { + let operation = this.relateOperation(nodeId, type, otherNodeId, properties, unique); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation that creates a relation between two nodes if they both exists. + * + * @static + * @param {integer} nodeId - First in relation node id. + * @param {integer} otherNodeId - Second in relation node id. + * @param {string} type - Case sensitive relation type name. + * @param {object} [properties={}] - Relation properties. + * @param {bool} [unique=false] - If include unique clause on create statement. + * @returns {OGMNeoOperation} Operation that creates the relation between nodes. + * @throws {Error} Will throw an error if the ids from node was not integers. + * @throws {Error} Will throw an error if a relatioship type was not an non-empty string. + */ + + static relateOperation(nodeId, type, otherNodeId, properties = {}, unique = false) { OGMNeoObjectParse.parseProperties(properties); let value = _.omitBy(properties, _.isUndefined); - return new Promise((resolve, reject) => { - if (_.isInteger(nodeId) && _.isInteger(otherNodeId)) { - if (_.isString(type) && !_.isEmpty(type)) { - let uniqueQuery = (unique) ? 'UNIQUE' : ''; - let cypher = `MATCH (n1) WHERE ID(n1)=${nodeId} ` + - `MATCH (n2) WHERE ID(n2)=${otherNodeId} ` + - `CREATE ${uniqueQuery} (n1)-[r:${type} ${OGMNeoObjectParse.objectString(value)}]->(n2) ` + - 'RETURN r'; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher, value)); - readTxResultPromise.then((result) => { + if (_.isInteger(nodeId) && _.isInteger(otherNodeId)) { + if (_.isString(type) && !_.isEmpty(type)) { + let uniqueQuery = (unique) ? 'UNIQUE' : ''; + let cypher = `MATCH (n1) WHERE ID(n1)=${nodeId} ` + + `MATCH (n2) WHERE ID(n2)=${otherNodeId} ` + + `CREATE ${uniqueQuery} (n1)-[r:${type} ${OGMNeoObjectParse.objectString(value)}]->(n2) ` + + 'RETURN r'; + + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .object(value) + .then((result) => { let record = _.first(result.records); - session.close(); - resolve(OGMNeoObjectParse.recordToRelation(record)); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('A relatioship type must be specified')); - } + return OGMNeoObjectParse.recordToRelation(record); + }).build(); } else { - reject(new Error('Ids from node must to be integers')); + throw new Error('A relatioship type must be specified'); } - }); + } else { + throw new Error('Ids from node must to be integers'); + } } /** @@ -61,31 +83,41 @@ class OGMRelation { * @returns {Promise.} Updated relation literal object if fulfilled, or some neo4j error if rejected. */ static update(relationId, newProperties) { + try { + let operation = this.updateOperation(relationId, newProperties); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation that update a relation propeties if it exists. + * + * @static + * @param {integer} relationId - Relation node id. + * @param {object} newProperties - Relation NEW properties. + * @returns {OGMNeoOperation} Operation that creates the relation between nodes. + * @throws {Error} Will throw an error if the id from relation node was not integer. + */ + static updateOperation(relationId, newProperties) { OGMNeoObjectParse.parseProperties(newProperties); let value = _.omitBy(newProperties, _.isUndefined); - return new Promise((resolve, reject) => { - if (_.isInteger(relationId)) { - let propertiesString = OGMNeoObjectParse.objectString(value); - let cypher = 'MATCH p=(n1)-[r]->(n2) ' + - `WHERE ID(r)=${relationId} SET r+=${propertiesString} RETURN r`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher, newProperties)); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve(null); - } else { - let record = _.first(result.records); - resolve(OGMNeoObjectParse.recordToRelation(record)); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('Relation id must to be integer')); - } - }); + if (_.isInteger(relationId)) { + let propertiesString = OGMNeoObjectParse.objectString(value); + let cypher = 'MATCH p=(n1)-[r]->(n2) ' + + `WHERE ID(r)=${relationId} SET r+=${propertiesString} RETURN r`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .object(value) + .then((result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.recordToRelation(record) : null; + }).build(); + } else { + throw new Error('Relation id must to be integer'); + } } /** @@ -97,34 +129,50 @@ class OGMRelation { * @returns {Promise.} Updated nodes if fulfilled, or some neo4j error if rejected. */ static updateMany(newProperties, query) { - return new Promise((resolve, reject) => { - if (_.isObject(newProperties)) { - let value = _.omitBy(newProperties, _.isUndefined); - if (!_.isEmpty(value)) { - OGMNeoObjectParse.parseProperties(value); - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypherMatch = query.matchCypher(); - let propertiesString = OGMNeoObjectParse.objectString(value); - let cypher = `${cypherMatch} SET r+=${propertiesString} RETURN r`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher, value)); - readTxResultPromise.then((result) => { - session.close(); - resolve(result.records.map(record => OGMNeoObjectParse.recordToRelation(record))); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } + try { + let operation = this.updateManyOperation(newProperties, query); + return OGMNeoOperationExecuter.execute(operation); + } catch(error) { + return Promise.reject(error); + } + } + + /** + * Operation that set or update newPropeties on all relation nodes that matches parameters query. + * + * @static + * @param {object} newProperties - New properties ot be set or updated. + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that updates properties on the relation nodes. + * @throws {Error} Will throw an error if query was not an instance of ogmneo.RelationQuery. + * @throws {Error} Will throw an error if newProperties was not an object. + * @throws {Error} Will throw an error if newProperties was was empty. + */ + static updateManyOperation(newProperties, query) { + if (_.isObject(newProperties)) { + let value = _.omitBy(newProperties, _.isUndefined); + if (!_.isEmpty(value)) { + OGMNeoObjectParse.parseProperties(value); + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypherMatch = query.matchCypher(); + let propertiesString = OGMNeoObjectParse.objectString(value); + let cypher = `${cypherMatch} SET r+=${propertiesString} RETURN r`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .object(value) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.recordToRelation(record)); + }).build(); } else { - resolve([]); + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); } } else { - resolve([]); + throw new Error('newProperties must be an object with at least one valid property to update'); } - }); + } else { + throw new Error('newProperties must be an object'); + } } /** @@ -168,7 +216,7 @@ class OGMRelation { return new Promise((resolve, reject) => { if (query != null && query instanceof OGMNeoRelationQuery) { query.limit(1); - this.find(query).then((relations)=> { + this.find(query).then((relations) => { resolve(_.first(relations)); }).catch((error) => { reject(error); @@ -220,7 +268,7 @@ class OGMRelation { return new Promise((resolve, reject) => { if (query != null && query instanceof OGMNeoRelationQuery) { query.limit(1); - this.findPopulated(query).then((relations)=> { + this.findPopulated(query).then((relations) => { resolve(_.first(relations)); }).catch((error) => { reject(error); From f8cbf1d893e3bba1cfab70f72b212fec83ae8a59 Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 4 Oct 2017 22:08:13 -0300 Subject: [PATCH 20/24] v1.0.0: Finishing adding the operation in all the methods of relation nodes. --- __test__/relation-tests.js | 2 +- lib/ogmneo-relation.js | 375 +++++++++++++++++++++++-------------- 2 files changed, 233 insertions(+), 144 deletions(-) diff --git a/__test__/relation-tests.js b/__test__/relation-tests.js index 72bf40a..9706855 100644 --- a/__test__/relation-tests.js +++ b/__test__/relation-tests.js @@ -163,7 +163,7 @@ test('Test FIND ONE relation', (assert) => { Promise.all([find1, find2, find3]).then((finds) => { assert.equal(finds[0].__type, 'relatedto'); - assert.equal(finds[1], undefined); + assert.equal(finds[1], null); let rel = finds[2]; assert.equal(rel.__type, 'relatedto'); assert.equal(rel.property, 'c'); diff --git a/lib/ogmneo-relation.js b/lib/ogmneo-relation.js index 8be8d0d..ee6d029 100644 --- a/lib/ogmneo-relation.js +++ b/lib/ogmneo-relation.js @@ -132,7 +132,7 @@ class OGMRelation { try { let operation = this.updateManyOperation(newProperties, query); return OGMNeoOperationExecuter.execute(operation); - } catch(error) { + } catch (error) { return Promise.reject(error); } } @@ -183,28 +183,37 @@ class OGMRelation { * @returns {Promise.} Found relation if fulfilled, or some neo4j error if rejected. */ static find(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypher = query.queryCypher(); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve([]); - } else { - resolve(result.records.map(record => OGMNeoObjectParse.parseRelation(record))); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.findOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * An operation that finds relation nodes that matches queries. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that find the relation nodes. + * @throws {Error} Will throw an error if the query object was null or not an instance of OGMNeoRelationQuery. + */ + static findOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypher = query.queryCypher(); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.parseRelation(record)); + }).build(); + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } } + /** * Find one relation node. * @@ -213,18 +222,34 @@ class OGMRelation { * @returns {Promise.} Found relation if fulfilled, or some neo4j error if rejected. */ static findOne(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - query.limit(1); - this.find(query).then((relations) => { - resolve(_.first(relations)); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.findOneOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation to find one relation node that matches query. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that find one relation node. + * @throws {Error} Will throw an error if the query object was null or not an instance of OGMNeoRelationQuery. + */ + static findOneOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + query.limit(1); + let operation = this.findOperation(query); + operation.then = (result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.parseRelation(record) : null; + }; + return operation; + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } } /** @@ -235,28 +260,37 @@ class OGMRelation { * @returns {Promise.} Found populated relation if fulfilled, or some neo4j error if rejected. */ static findPopulated(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypher = query.queryPopulatedCypher(); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve([]); - } else { - resolve(result.records.map(record => OGMNeoObjectParse.recordToRelationPopulated(record))); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.findPopulatedOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } } + + /** + * Operation that find relation nodes with start and end nodes populated. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that find relations nodes. + * @throws {Error} Will throw an error if the query object was null or not an instance of ogmneo.RelationQuery. + */ + static findPopulatedOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypher = query.queryPopulatedCypher(); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.recordToRelationPopulated(record)); + }).build(); + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } + } + /** * Find one Populated relation node. * @@ -265,18 +299,34 @@ class OGMRelation { * @returns {Promise.} Populated found relation if fulfilled, or some neo4j error if rejected. */ static findOnePopulated(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - query.limit(1); - this.findPopulated(query).then((relations) => { - resolve(_.first(relations)); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.findOnePopulatedOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation to find one populated relation node. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that find one populated relation node. + * @throws {Error} Will throw an error if the query object was null or not an instance of ogmneo.RelationQuery. + */ + static findOnePopulatedOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + query.limit(1); + let operation = this.findPopulatedOperation(query); + operation.then = (result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.recordToRelationPopulated(record) : null; + }; + return operation; + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } } /** @@ -289,26 +339,37 @@ class OGMRelation { * @returns {Promise.} Found populated relation if fulfilled, or some neo4j error if rejected. */ static findNodes(query, nodes = 'both', distinct = false) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypher = query.queryNodesCypher(nodes, distinct); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve([]); - } else { - resolve(result.records.map(record => OGMNeoObjectParse.recordToRelationStartEndNodes(record, nodes))); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.findNodesOperation(query, nodes, distinct); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.resolve(error); + } + } + + + /** + * Operation to find start and end nodes for the relation. Do not return relation properties to find relation properties use find or find populated. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @param {string} [nodes='both'] - The return nodes. 'both'/null = return start and end nodes, 'start' = return start nodes, 'end' = return end nodes + * @param {boolean} [distinct = false] - Add distinct clause to cypher return. + * @returns {OGMNeoOperation} Operation that find start and end nodes with query. + * @throws {Error} Will throw an error if the query object was null or not an instance of ogmneo.RelationQuery. + */ + static findNodesOperation(query, nodes = 'both', distinct = false) { + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypher = query.queryNodesCypher(nodes, distinct); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.recordToRelationStartEndNodes(record, nodes)); + }).build(); + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } } /** @@ -319,26 +380,35 @@ class OGMRelation { * @returns {Promise.} Count of relations if fulfilled, or some neo4j error if rejected. */ static count(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypher = query.countCypher(); - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - session.run(cypher).subscribe({ - onNext: (record) => { - resolve(record.get('count').low); - }, - onCompleted: () => { - session.close(); - }, - onError: (error) => { - reject(error); - } - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.countOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + /** + * Operation that counts relation nodes. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that count nodes with query. + * @throws {Error} Will throw an error if the query object was null or not an instance of ogmneo.RelationQuery. + */ + static countOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypher = query.countCypher(); + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.READ) + .then((result) => { + let record = _.first(result.records); + return (record != null) ? record.get('count').low : 0; + }).build(); + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } } /** @@ -367,27 +437,35 @@ class OGMRelation { * @returns {Promise.} Deleted relation node if fulfilled, or some neo4j error if rejected. */ static deleteRelation(relationId) { - return new Promise((resolve, reject) => { - if (_.isInteger(relationId)) { - let cypher = `MATCH p=(n1)-[r]->(n2) WHERE ID(r)=${relationId} DELETE r RETURN r`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - if (_.isEmpty(result.records)) { - resolve(null); - } else { - let record = _.first(result.records); - resolve(OGMNeoObjectParse.recordToRelation(record)); - } - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('Relation id must to be an integer number')); - } - }); + try { + let operation = this.deleteRelationOperation(relationId); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } + } + + + /** + * Operation that deletes a relation by id. + * + * @static + * @param {integer} relationId - relation node id. + * @returns {OGMNeoOperation} Operation that deletes a node with id. + * @throws {Error} Will throw an error if the relation id was not an integer value. + */ + static deleteRelationOperation(relationId) { + if (_.isInteger(relationId)) { + let cypher = `MATCH p=(n1)-[r]->(n2) WHERE ID(r)=${relationId} DELETE r RETURN r`; + return OGMNeoOperationBuilder.create().cypher(cypher) + .type(OGMNeoOperation.WRITE) + .then((result) => { + let record = _.first(result.records); + return (record != null) ? OGMNeoObjectParse.recordToRelation(record) : null; + }).build(); + } else { + throw new Error('Relation id must to be an integer number'); + } } /** @@ -398,25 +476,36 @@ class OGMRelation { * @returns {Promise.} Deleted nodes if fulfilled, or some neo4j error if rejected. */ static deleteMany(query) { - return new Promise((resolve, reject) => { - if (query != null && query instanceof OGMNeoRelationQuery) { - let cypherMatch = query.matchCypher(); - let cypher = `${cypherMatch} DELETE r RETURN r`; - Printer.printCypherIfEnabled(cypher); - let session = OGMNeo.session(); - let readTxResultPromise = session.readTransaction(transaction => transaction.run(cypher)); - readTxResultPromise.then((result) => { - session.close(); - resolve(result.records.map(record => OGMNeoObjectParse.recordToRelation(record))); - }).catch((error) => { - reject(error); - }); - } else { - reject(new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery')); - } - }); + try { + let operation = this.deleteManyOperation(query); + return OGMNeoOperationExecuter.execute(operation); + } catch (error) { + return Promise.reject(error); + } } + /** + * Operation that deletes all relation nodes that matches parameters query. + * + * @static + * @param {OGMNeoRelationQuery} query - Query filter. + * @returns {OGMNeoOperation} Operation that deletes nodes with query. + * @throws {Error} Will throw an error if the query object was null or not an instance of ogmneo.RelationQuery. + */ + static deleteManyOperation(query) { + if (query != null && query instanceof OGMNeoRelationQuery) { + let cypherMatch = query.matchCypher(); + let cypher = `${cypherMatch} DELETE r RETURN r`; + return OGMNeoOperationBuilder.create() + .cypher(cypher) + .type(OGMNeoOperation.WRITE) + .then((result) => { + return result.records.map(record => OGMNeoObjectParse.recordToRelation(record)); + }).build(); + } else { + throw new Error('The query object can\'t be null and must be an instance of OGMNeoRelationQuery'); + } + } } module.exports = OGMRelation; From f0dd3d7f4eeb0667adb704e679658850ffbcb679 Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 4 Oct 2017 22:21:07 -0300 Subject: [PATCH 21/24] v1.0.0: Adding classes to index :) --- index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 128882a..a304f16 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ const index = require('./lib/ogmneo-index'); const where = require('./lib/ogmneo-where'); const relationQuery = require('./lib/ogmneo-relation-query'); const { OGMNeoOperation, OGMNeoOperationBuilder } = require('./lib/ogmneo-operation'); - +const OGMNeoOperationExecuter = require('./lib/ogmneo-operation-executer'); module.exports = { Connection: connection, OGMNeoNode: nodes, @@ -19,6 +19,7 @@ module.exports = { OGMNeoRelationQuery: relationQuery, OGMNeoOperation: OGMNeoOperation, OGMNeoOperationBuilder: OGMNeoOperationBuilder, + OGMNeoOperationExecuter: OGMNeoOperationExecuter, //Simplified names Node: nodes, Query: query, @@ -28,5 +29,6 @@ module.exports = { Where: where, RelationQuery: relationQuery, Operation: OGMNeoOperation, - OperationBuilder: OGMNeoOperationBuilder + OperationBuilder: OGMNeoOperationBuilder, + OperationExecuter: OGMNeoOperationExecuter }; From ceb0196e35fb597c66214f021b7df4b793bcc99a Mon Sep 17 00:00:00 2001 From: Luciano Date: Wed, 4 Oct 2017 22:53:17 -0300 Subject: [PATCH 22/24] v1.0.0: Improving docs. --- lib/ogmneo-node.js | 4 ++-- lib/ogmneo-operation-executer.js | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/ogmneo-node.js b/lib/ogmneo-node.js index f582df6..5671f71 100644 --- a/lib/ogmneo-node.js +++ b/lib/ogmneo-node.js @@ -25,7 +25,7 @@ class OGMNeoNode { } /** - * Creates a node on neo4j. + * Operation that creates a node on neo4j. * * @static * @param {object} node - The literal object with node propeperties. @@ -65,7 +65,7 @@ class OGMNeoNode { } /** - * Update node operation. + * Operation that updates a node. * * @static * @param {object} node - The literal object with node propeperties and required node.id. diff --git a/lib/ogmneo-operation-executer.js b/lib/ogmneo-operation-executer.js index ce17bf6..54d64b0 100644 --- a/lib/ogmneo-operation-executer.js +++ b/lib/ogmneo-operation-executer.js @@ -56,10 +56,10 @@ class OGMNeoOperationExecuter { } /** - * Executes an READ or WRITE ogmneo.Operation and returns a result. + * Opens a read transaction of neo4j drive and returns a result. * * @static - * @param {operation} operation - . + * @param {function} transactional - A function with the transaction parameter that you must return a promise of your operations on this transaction. * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ static read(transactional) { @@ -77,11 +77,11 @@ class OGMNeoOperationExecuter { } - /** - * Executes an READ or WRITE ogmneo.Operation and returns a result. + /** + * Opens a write transaction of neo4j drive and returns a result. * * @static - * @param {operation} operation - . + * @param {function} transactional - A function with the transaction parameter that you must return a promise of your operations on this transaction. * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ static write(transactional) { @@ -102,7 +102,8 @@ class OGMNeoOperationExecuter { * Executes an READ or WRITE ogmneo.Operation and returns a result. * * @static - * @param {operation} operation - . + * @param {OGMNeoOperation} operation - ogmneo.Operation object to be executed. + * @param {object} transaction - Transaction created on read or write methods or transaction created on neo4j driver. * @returns {Promise.} Result(Parsed or not) of the executed opertion or some error if rejected. */ static execute(operation, transaction = null) { From 9b28ccf4c1a9640d8f69e4000b4480caad0463a6 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 4 Oct 2017 23:28:52 -0300 Subject: [PATCH 23/24] Update README.md --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/README.md b/README.md index eeaf067..d6e53c4 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,62 @@ You can create and drop indexes in properties. }); ``` +## Operation API + +Almost every method on ogmneo.Node and ogmneo.Relation have now the Operation API, that instead of executing the function on database returning a promise, it creates an ogmneo.Operation object that can be executed after by the ogmneo.OperationExecuter. Exemple: +```js + const ogmneo = require('ogmneo'); + + let operation = ogmneo.Node.createOperation({ name: 'name', tes: 3 }, 'test'); + ogmneo.OperationExecuter.execute(operation) + .then((node) => { + //Created returned object => {id: 1, name: 'name', tes: 3} + }).catch((error) => { + //Handle error + }); +``` + +## Transactional API +With the Operation API we can now execute as many READ or WRITE operations on the same transaction. +For exemple you want to create to nodes and then relate those two. But if the relation fails you want to rollback all the operations. +```js + const ogmneo = require('ogmneo'); + + let createDriver = ogmneo.Node.createOperation({name: 'Ayrton Senna', carNumber: 12 }, 'Driver'); + ogmneo.OperationExecuter.write((transaction) => { + return ogmneo.OperationExecuter.execute(createDriver, transaction) + .then((driver) => { + let createCar = ogmneo.Node.createOperation({name: 'MP4/4'}, 'Car'); + return ogmneo.OperationExecuter.execute(createCar, transaction).then((car) => { +                                       let relate = ogmneo.Relation.relateOperation(driver.id, 'DRIVES', car.id, {year: 1988}); + return ogmneo.OperationExecuter.execute(relate, transaction); +                                    }); + }); + }).then((result) => { + //Result here + }); +``` +All of those operations will be executed on the same transaction and you can rollback anytime you want. The transaction is the [neo4j driver](https://github.com/neo4j/neo4j-javascript-driver) transaction object and you can see more about it on their docs [here](http://neo4j.com/docs/api/javascript-driver/current/class/src/v1/transaction.js~Transaction.html). + +### Batching operation in a single transaction + +You can also batch many operation READ or WRITE operations in a single transaction. + +```js + const ogmneo = require('ogmneo'); + + let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); + let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); + + OGMNeoOperationExecuter.batchWriteOperations([createUser1, createUser2]).then((result) => { + let created1 = result[0]; + let created2 = result[1]; + console.log(created1.name); // 'Ayrton Senna' + console.log(created2.name); // 'Alain Prost' + }); +``` +If one of those fail all operations on the transaction will be rolledback. + ## Documentation See the full **API** documentation at [docs](http://ogmneo-docs.getforge.io/). All docs was generated by [JSDoc](https://github.com/jsdoc3/jsdoc). From 83cb753f88b4fbd296c9c2f85234c1f8ab6365a4 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 4 Oct 2017 23:41:18 -0300 Subject: [PATCH 24/24] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d6e53c4..b343bdf 100644 --- a/README.md +++ b/README.md @@ -184,9 +184,9 @@ All of those operations will be executed on the same transaction and you can rol You can also batch many operation READ or WRITE operations in a single transaction. ```js - const ogmneo = require('ogmneo'); + const ogmneo = require('ogmneo'); - let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); + let createUser1 = OGMNeoNode.createOperation({name: 'Ayrton Senna'}, 'Person'); let createUser2 = OGMNeoNode.createOperation({name: 'Alain Prost'}, 'Person'); OGMNeoOperationExecuter.batchWriteOperations([createUser1, createUser2]).then((result) => { @@ -196,7 +196,7 @@ You can also batch many operation READ or WRITE operations in a single transacti console.log(created2.name); // 'Alain Prost' }); ``` -If one of those fail all operations on the transaction will be rolledback. +If one of those fails, all other operations on the transaction will be rolledback automatically. ## Documentation