Skip to content

Commit 44c6519

Browse files
authored
Optional match testing (#48)
1 parent 6c21d21 commit 44c6519

File tree

2 files changed

+127
-74
lines changed

2 files changed

+127
-74
lines changed

src/resultSet.js

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const ResultSetColumnTypes = {
1010
COLUMN_UNKNOWN: 0,
1111
COLUMN_SCALAR: 1,
1212
COLUMN_NODE: 2,
13-
COLUMN_RELATION: 3
13+
COLUMN_RELATION: 3,
1414
};
1515

1616
const ResultSetValueTypes = {
@@ -23,29 +23,29 @@ const ResultSetValueTypes = {
2323
VALUE_ARRAY: 6,
2424
VALUE_EDGE: 7,
2525
VALUE_NODE: 8,
26-
VALUE_PATH: 9
26+
VALUE_PATH: 9,
2727
};
2828

2929
/**
3030
* Hold a query result
3131
*/
3232
class ResultSet {
33-
/**
34-
* Builds an empty ResultSet object.
35-
* @constructor
36-
* @param {Graph} graph
37-
*/
33+
/**
34+
* Builds an empty ResultSet object.
35+
* @constructor
36+
* @param {Graph} graph
37+
*/
3838
constructor(graph) {
39-
this._graph = graph; //_graph is graph api
40-
this._position = 0; //allowing iterator like behevior
39+
this._graph = graph; //_graph is graph api
40+
this._position = 0; //allowing iterator like behevior
4141
this._resultsCount = 0; //total number of records in this result set
42-
this._header = []; //reponse schema columns labels
43-
this._results = []; //result records
42+
this._header = []; //reponse schema columns labels
43+
this._results = []; //result records
4444
}
4545

4646
/**
4747
* Parse raw response data to ResultSet object.
48-
* @async
48+
* @async
4949
* @param {object[]} resp - raw response representation - the raw representation of response is at most 3 lists of objects.
5050
* The last list is the statistics list.
5151
*/
@@ -66,11 +66,11 @@ class ResultSet {
6666
return this;
6767
}
6868

69-
/**
70-
* Parse a raw response body into header an records.
71-
* @async
72-
* @param {object[]} resp raw response
73-
*/
69+
/**
70+
* Parse a raw response body into header an records.
71+
* @async
72+
* @param {object[]} resp raw response
73+
*/
7474
async parseResults(resp) {
7575
this.parseHeader(resp[0]);
7676
await this.parseRecords(resp);
@@ -95,7 +95,7 @@ class ResultSet {
9595
/**
9696
* The raw representation of response is at most 3 lists of objects. rawResultSet[1] contains the data records.
9797
* Each entry in the record can be either a node, an edge or a scalar
98-
* @async
98+
* @async
9999
* @param {object[]} rawResultSet raw result set representation
100100
*/
101101
async parseRecords(rawResultSet) {
@@ -127,12 +127,12 @@ class ResultSet {
127127
}
128128
}
129129

130-
/**
131-
* Parse raw entity properties representation into a Map
132-
* @async
133-
* @param {object[]} props raw properties representation
134-
* @returns {Map} Map with the parsed properties.
135-
*/
130+
/**
131+
* Parse raw entity properties representation into a Map
132+
* @async
133+
* @param {object[]} props raw properties representation
134+
* @returns {Map} Map with the parsed properties.
135+
*/
136136
async parseEntityProperties(props) {
137137
// [[name, value, value type] X N]
138138
let properties = {};
@@ -158,12 +158,12 @@ class ResultSet {
158158
return properties;
159159
}
160160

161-
/**
162-
* Parse raw node representation into a Node object.
163-
* @async
164-
* @param {object[]} cell raw node representation.
165-
* @returns {Node} Node object.
166-
*/
161+
/**
162+
* Parse raw node representation into a Node object.
163+
* @async
164+
* @param {object[]} cell raw node representation.
165+
* @returns {Node} Node object.
166+
*/
167167
async parseNode(cell) {
168168
// Node ID (integer),
169169
// [label string offset (integer)],
@@ -188,12 +188,12 @@ class ResultSet {
188188
return node;
189189
}
190190

191-
/**
192-
* Parse a raw edge representation into an Edge object.
193-
* @async
194-
* @param {object[]} cell raw edge representation
195-
* @returns {Edge} Edge object.
196-
*/
191+
/**
192+
* Parse a raw edge representation into an Edge object.
193+
* @async
194+
* @param {object[]} cell raw edge representation
195+
* @returns {Edge} Edge object.
196+
*/
197197
async parseEdge(cell) {
198198
// Edge ID (integer),
199199
// reltype string offset (integer),
@@ -223,37 +223,37 @@ class ResultSet {
223223
return edge;
224224
}
225225

226-
/**
227-
* Parse and in-place replace raw array into an array of values or objects.
228-
* @async
229-
* @param {object[]} rawArray raw array representation
230-
* @returns {object[]} Parsed array.
231-
*/
226+
/**
227+
* Parse and in-place replace raw array into an array of values or objects.
228+
* @async
229+
* @param {object[]} rawArray raw array representation
230+
* @returns {object[]} Parsed array.
231+
*/
232232
async parseArray(rawArray) {
233233
for (var i = 0; i < rawArray.length; i++) {
234234
rawArray[i] = await this.parseScalar(rawArray[i]);
235235
}
236236
return rawArray;
237237
}
238238

239-
/**
240-
* Parse a raw path representation into Path object.
241-
* @async
242-
* @param {object[]} rawPath raw path representation
243-
* @returns {Path} Path object.
244-
*/
239+
/**
240+
* Parse a raw path representation into Path object.
241+
* @async
242+
* @param {object[]} rawPath raw path representation
243+
* @returns {Path} Path object.
244+
*/
245245
async parsePath(rawPath) {
246246
let nodes = await this.parseScalar(rawPath[0]);
247247
let edges = await this.parseScalar(rawPath[1]);
248248
return new Path(nodes, edges);
249249
}
250250

251-
/**
252-
* Parse a raw value into its actual value.
253-
* @async
254-
* @param {object[]} cell raw value representation
255-
* @returns {object} Actual value - scalar, array, Node, Edge, Path
256-
*/
251+
/**
252+
* Parse a raw value into its actual value.
253+
* @async
254+
* @param {object[]} cell raw value representation
255+
* @returns {object} Actual value - scalar, array, Node, Edge, Path
256+
*/
257257
async parseScalar(cell) {
258258
let scalar_type = cell[0];
259259
let value = cell[1];
@@ -298,40 +298,40 @@ class ResultSet {
298298
return scalar;
299299
}
300300

301-
/**
302-
* @returns {string[] }ResultSet's header.
303-
*/
301+
/**
302+
* @returns {string[] }ResultSet's header.
303+
*/
304304
getHeader() {
305305
return this._typelessHeader;
306306
}
307307

308-
/**
309-
* @returns {boolean} If the ResultSet object can return additional records.
310-
*/
308+
/**
309+
* @returns {boolean} If the ResultSet object can return additional records.
310+
*/
311311
hasNext() {
312312
return this._position < this._resultsCount;
313313
}
314314

315-
/**
316-
* @returns {Record} The current record.
317-
*/
315+
/**
316+
* @returns {Record} The current record.
317+
*/
318318
next() {
319319
return this._results[this._position++];
320320
}
321321

322-
/**
323-
* @returns {Statistics} ResultsSet's statistics.
324-
*/
322+
/**
323+
* @returns {Statistics} ResultsSet's statistics.
324+
*/
325325
getStatistics() {
326326
return this._statistics;
327-
}
328-
329-
/**
330-
* @returns {int} Result set size.
331-
*/
332-
size() {
333-
return this._resultsCount;
334-
}
327+
}
328+
329+
/**
330+
* @returns {int} Result set size.
331+
*/
332+
size() {
333+
return this._resultsCount;
334+
}
335335
}
336336

337337
module.exports = ResultSet;

test/redisGraphAPITest.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,4 +455,57 @@ describe("RedisGraphAPI Test", () => {
455455
);
456456
}
457457
});
458+
459+
it("testOptionalMatch", async () => {
460+
await api.query("CREATE (:L {val:1})-[:E {val:2}]->(:L2 {val: 3})");
461+
let resultSet = await api.query(
462+
"OPTIONAL MATCH (a:NONEXISTENT)-[e]->(b) RETURN a.val, e.val, b.val"
463+
);
464+
assert.equal(resultSet.size(), 1);
465+
assert.ok(resultSet.hasNext());
466+
let record = resultSet.next();
467+
assert.ok(!resultSet.hasNext());
468+
assert.deepEqual(record.values(), [null, null, null]);
469+
470+
// Test a query that produces 2 records, with 2 null values in the second.
471+
resultSet = await api.query(
472+
"MATCH (a) OPTIONAL MATCH (a)-[e]->(b) RETURN a.val, e.val, b.val ORDER BY ID(a)"
473+
);
474+
assert.equal(resultSet.size(), 2);
475+
record = resultSet.next();
476+
assert.equal(record.size(), 3);
477+
assert.equal(record.get(0), 1);
478+
assert.equal(record.get(1), 2);
479+
assert.equal(record.get(2), 3);
480+
record = resultSet.next();
481+
assert.equal(record.size(), 3);
482+
assert.equal(record.get(0), 3);
483+
assert.equal(record.get(1), null);
484+
assert.equal(record.get(2), null);
485+
486+
// Test a query that produces 2 records, the first containing a path and the second containing a null value
487+
let node0 = new Node("L", {val:1});
488+
node0.setId(0);
489+
let node1 = new Node("L2", {val:3});
490+
node1.setId(1);
491+
let edge01 = new Edge(0, "E", 1, {val:2});
492+
edge01.setId(0);
493+
let expectedPath = new PathBuilder()
494+
.append(node0)
495+
.append(edge01)
496+
.append(node1)
497+
.build();
498+
499+
resultSet = await api.query(
500+
"MATCH (a) OPTIONAL MATCH p = (a)-[e]->(b) RETURN p"
501+
);
502+
503+
assert.equal(resultSet.size(), 2);
504+
record = resultSet.next();
505+
assert.equal(record.size(), 1);
506+
assert.deepEqual(record.get(0), expectedPath);
507+
record = resultSet.next();
508+
assert.equal(record.size(), 1);
509+
assert.equal(record.get(0), null);
510+
});
458511
});

0 commit comments

Comments
 (0)