diff --git a/src/node.js b/src/node.js index 2541d5ce2b..f7e4957c55 100644 --- a/src/node.js +++ b/src/node.js @@ -7,12 +7,12 @@ class Node { /** * Builds a node object. * - * @param {string} label - node label. + * @param {string|string[]} label - node label(s). * @param {Map} properties - properties map. */ constructor(label, properties) { this.id = undefined; //node's id - set by RedisGraph - this.label = label; //node's label + this.label = label; //node's label(s) this.properties = properties; //node's list of properties (list of Key:Value) } diff --git a/src/resultSet.js b/src/resultSet.js index 4698e97948..de73232854 100644 --- a/src/resultSet.js +++ b/src/resultSet.js @@ -174,31 +174,47 @@ class ResultSet { } /** - * Parse raw node representation into a Node object. + * Parse label index into a label string. * @async - * @param {object[]} cell raw node representation. - * @returns {Promise} Node object. + * @param {number} label_idx index of label. + * @returns {Promise} string representation of label. */ - async parseNode(cell) { - // Node ID (integer), - // [label string offset (integer)], - // [[name, value, value type] X N] - - let node_id = cell[0]; - let label = this._graph.getLabel(cell[1][0]); + async parseNodeLabel(label_idx) { + let label = this._graph.getLabel(label_idx); // will try to get the right label for at most 10 times var tries = 0; while (label == undefined && tries < 10) { - label = await this._graph.fetchAndGetLabel(cell[1][0]); + label = await this._graph.fetchAndGetLabel(label_idx); tries++; } if (label == undefined) { console.warn( - "unable to retrive label value for label index " + cell[1][0] + "unable to retrieve label value for label index " + label_idx ); } + return label; + } + + /** + * Parse raw node representation into a Node object. + * @async + * @param {object[]} cell raw node representation. + * @returns {Promise} Node object. + */ + async parseNode(cell) { + // Node ID (integer), + // [label string offset (integer) X N], + // [[name, value, value type] X N] + + let node_id = cell[0]; + var labels = undefined; + if (cell[1].length == 1) { + labels = await this.parseNodeLabel(cell[1][0]); + } else { + labels = await Promise.all(cell[1].map(x => this.parseNodeLabel(x))); + } let properties = await this.parseEntityProperties(cell[2]); - let node = new Node(label, properties); + let node = new Node(labels, properties); node.setId(node_id); return node; } diff --git a/test/redisGraphAPITest.js b/test/redisGraphAPITest.js index 715d764a59..9df31cb5a8 100644 --- a/test/redisGraphAPITest.js +++ b/test/redisGraphAPITest.js @@ -95,6 +95,54 @@ describe("RedisGraphAPI Test", () => { ); }); + it("test Create Multi-Labeled Node", async () => { + // Create a node with 2 labels + let result = await api.query("CREATE (:human:male {name:'danny', age:12})"); + assert.equal(result.size(), 0); + assert.ok(!result.hasNext()); + assert.equal( + "1", + result.getStatistics().getStringValue(Label.NODES_CREATED) + ); + assert.equal( + "2", + result.getStatistics().getStringValue(Label.PROPERTIES_SET) + ); + assert.ok( + result + .getStatistics() + .getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME) + ); + + // Retrieve the node + let resultSet = await api.query( + "MATCH (a:human:male) RETURN a" + ); + assert.equal(resultSet.size(), 1); + assert.ok(resultSet.hasNext()); + assert.equal(0, resultSet.getStatistics().nodesCreated()); + assert.equal(0, resultSet.getStatistics().nodesDeleted()); + assert.equal(0, resultSet.getStatistics().labelsAdded()); + assert.equal(0, resultSet.getStatistics().propertiesSet()); + assert.equal(0, resultSet.getStatistics().relationshipsCreated()); + assert.equal(0, resultSet.getStatistics().relationshipsDeleted()); + assert.ok( + resultSet + .getStatistics() + .getStringValue(Label.QUERY_INTERNAL_EXECUTION_TIME) + ); + + assert.deepStrictEqual(["a"], resultSet.getHeader()); + + let record = resultSet.next(); + let n = record.get(0); + assert.equal(12, n.properties["age"]); + assert.equal("danny", n.properties["name"]); + assert.deepEqual(["human", "male"], n.label); + assert.equal(0, n.id); + + }); + it("test Connect Nodes", async () => { // Create both source and destination nodes await api.query("CREATE (:person {name:'roi', age:34})"); diff --git a/types/src/edge.d.ts b/types/src/edge.d.ts index 2333d68c99..4df214361c 100644 --- a/types/src/edge.d.ts +++ b/types/src/edge.d.ts @@ -32,4 +32,4 @@ declare class Edge { declare namespace Edge { export { Node }; } -type Node = import("./node"); +type Node = import('./node'); diff --git a/types/src/graph.d.ts b/types/src/graph.d.ts index 63ae5bce8b..a391915e0f 100644 --- a/types/src/graph.d.ts +++ b/types/src/graph.d.ts @@ -1,4 +1,7 @@ export = Graph; +/** + * @typedef {import('ioredis') | redis.RedisClient} RedisClient + */ /** * RedisGraph client */ @@ -8,11 +11,11 @@ declare class Graph { * See: node_redis for more options on createClient * * @param {string} graphId the graph id - * @param {string | redis.RedisClient} [host] Redis host or node_redis client + * @param {string | RedisClient} [host] Redis host or node_redis client or ioredis client * @param {string | number} [port] Redis port (integer) * @param {Object} [options] node_redis options */ - constructor(graphId: string, host?: string | any, port?: string | number, options?: any); + constructor(graphId: string, host?: string | RedisClient, port?: string | number, options?: any); _graphId: string; _labels: any[]; _relationshipTypes: any[]; @@ -53,6 +56,26 @@ declare class Graph { * @returns {Promise} a promise contains a result set */ query(query: string, params?: Map): Promise; + /** + * Execute a Cypher readonly query + * @async + * @param {string} query Cypher query + * @param {Map} [params] Parameters map + * + * @returns {Promise} a promise contains a result set + */ + readonlyQuery(query: string, params?: Map): Promise; + /** + * Execute a Cypher query + * @private + * @async + * @param {'graph.QUERY'|'graph.RO_QUERY'} command + * @param {string} query Cypher query + * @param {Map} [params] Parameters map + * + * @returns {Promise} a promise contains a result set + */ + private _query; /** * Deletes the entire graph * @async @@ -122,4 +145,8 @@ declare class Graph { */ fetchAndGetProperty(id: number): Promise; } +declare namespace Graph { + export { RedisClient }; +} import ResultSet = require("./resultSet"); +type RedisClient = any | any; diff --git a/types/src/node.d.ts b/types/src/node.d.ts index 354d540b68..61f254aa5a 100644 --- a/types/src/node.d.ts +++ b/types/src/node.d.ts @@ -6,12 +6,12 @@ declare class Node { /** * Builds a node object. * - * @param {string} label - node label. + * @param {string|string[]} label - node label(s). * @param {Map} properties - properties map. */ - constructor(label: string, properties: Map); + constructor(label: string | string[], properties: Map); id: number; - label: string; + label: string | string[]; properties: Map; /** * Sets the node id. diff --git a/types/src/path.d.ts b/types/src/path.d.ts index 57178effc0..50da350b44 100644 --- a/types/src/path.d.ts +++ b/types/src/path.d.ts @@ -64,5 +64,5 @@ declare class Path { declare namespace Path { export { Node, Edge }; } -type Node = import("./node"); -type Edge = import("./edge"); +type Node = import('./node'); +type Edge = import('./edge'); diff --git a/types/src/resultSet.d.ts b/types/src/resultSet.d.ts index c61954fd1e..abe2df2e19 100644 --- a/types/src/resultSet.d.ts +++ b/types/src/resultSet.d.ts @@ -50,6 +50,13 @@ declare class ResultSet { * @returns {Promise} Map with the parsed properties. */ parseEntityProperties(props: object[]): Promise; + /** + * Parse label index into a label string + * @async + * @param {number} label_idx index of label + * @returns {Promise} string representation of label. + */ + parseNodeLabel(label_idx: number): Promise; /** * Parse raw node representation into a Node object. * @async @@ -85,6 +92,15 @@ declare class ResultSet { * @returns {Promise} Map object. */ parseMap(rawMap: object[]): Promise; + /** + * Parse a raw Point representation into a lat-lon Map object. + * @param {object[]} rawPoint 2-valued lat-lon array representation + * @returns {{ latitude: number, longitude: number }} Map object with latitude and longitude keys. + */ + parsePoint(rawPoint: object[]): { + latitude: number; + longitude: number; + }; /** * Parse a raw value into its actual value. * @async @@ -121,4 +137,4 @@ import Node = require("./node"); import Edge = require("./edge"); import Path = require("./path"); import Record = require("./record"); -type Graph = import("./graph"); +type Graph = import('./graph');