flowquery 1.0.36 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flowquery.min.js +1 -1
- package/dist/parsing/functions/coalesce.d.ts +0 -1
- package/dist/parsing/functions/coalesce.d.ts.map +1 -1
- package/dist/parsing/functions/coalesce.js +0 -1
- package/dist/parsing/functions/coalesce.js.map +1 -1
- package/dist/parsing/functions/date.d.ts +0 -2
- package/dist/parsing/functions/date.d.ts.map +1 -1
- package/dist/parsing/functions/date.js +0 -2
- package/dist/parsing/functions/date.js.map +1 -1
- package/dist/parsing/functions/datetime.d.ts +0 -2
- package/dist/parsing/functions/datetime.d.ts.map +1 -1
- package/dist/parsing/functions/datetime.js +0 -2
- package/dist/parsing/functions/datetime.js.map +1 -1
- package/dist/parsing/functions/localdatetime.d.ts +0 -2
- package/dist/parsing/functions/localdatetime.d.ts.map +1 -1
- package/dist/parsing/functions/localdatetime.js +0 -2
- package/dist/parsing/functions/localdatetime.js.map +1 -1
- package/dist/parsing/functions/localtime.d.ts +0 -2
- package/dist/parsing/functions/localtime.d.ts.map +1 -1
- package/dist/parsing/functions/localtime.js +0 -2
- package/dist/parsing/functions/localtime.js.map +1 -1
- package/dist/parsing/functions/temporal_utils.js +1 -1
- package/dist/parsing/functions/time.d.ts +0 -2
- package/dist/parsing/functions/time.d.ts.map +1 -1
- package/dist/parsing/functions/time.js +0 -2
- package/dist/parsing/functions/time.js.map +1 -1
- package/dist/parsing/functions/timestamp.d.ts +0 -2
- package/dist/parsing/functions/timestamp.d.ts.map +1 -1
- package/dist/parsing/functions/timestamp.js +1 -4
- package/dist/parsing/functions/timestamp.js.map +1 -1
- package/dist/parsing/operations/group_by.d.ts.map +1 -1
- package/dist/parsing/operations/group_by.js +3 -2
- package/dist/parsing/operations/group_by.js.map +1 -1
- package/dist/parsing/operations/limit.d.ts +2 -0
- package/dist/parsing/operations/limit.d.ts.map +1 -1
- package/dist/parsing/operations/limit.js +6 -0
- package/dist/parsing/operations/limit.js.map +1 -1
- package/dist/parsing/operations/return.d.ts +3 -0
- package/dist/parsing/operations/return.d.ts.map +1 -1
- package/dist/parsing/operations/return.js +10 -0
- package/dist/parsing/operations/return.js.map +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +13 -10
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/pyproject.toml +1 -1
- package/flowquery-py/src/parsing/functions/coalesce.py +1 -2
- package/flowquery-py/src/parsing/functions/date_.py +0 -2
- package/flowquery-py/src/parsing/functions/datetime_.py +0 -2
- package/flowquery-py/src/parsing/functions/localdatetime.py +0 -2
- package/flowquery-py/src/parsing/functions/localtime.py +0 -2
- package/flowquery-py/src/parsing/functions/temporal_utils.py +1 -1
- package/flowquery-py/src/parsing/functions/time_.py +0 -2
- package/flowquery-py/src/parsing/functions/timestamp.py +1 -3
- package/flowquery-py/src/parsing/operations/limit.py +7 -0
- package/flowquery-py/src/parsing/operations/return_op.py +14 -0
- package/flowquery-py/src/parsing/parser.py +13 -9
- package/flowquery-py/tests/compute/test_runner.py +81 -2
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/parsing/functions/coalesce.ts +0 -1
- package/src/parsing/functions/date.ts +0 -2
- package/src/parsing/functions/datetime.ts +0 -2
- package/src/parsing/functions/localdatetime.ts +0 -2
- package/src/parsing/functions/localtime.ts +0 -2
- package/src/parsing/functions/temporal_utils.ts +1 -1
- package/src/parsing/functions/time.ts +0 -2
- package/src/parsing/functions/timestamp.ts +1 -5
- package/src/parsing/operations/group_by.ts +4 -2
- package/src/parsing/operations/limit.ts +7 -1
- package/src/parsing/operations/return.ts +11 -0
- package/src/parsing/parser.ts +12 -10
- package/tests/compute/runner.test.ts +69 -2
package/package.json
CHANGED
|
@@ -8,8 +8,6 @@ import { buildDateObject, parseTemporalArg } from "./temporal_utils";
|
|
|
8
8
|
* When called with a string argument, parses it as an ISO 8601 date.
|
|
9
9
|
* When called with a map argument, constructs a date from components.
|
|
10
10
|
*
|
|
11
|
-
* Equivalent to Neo4j's date() function.
|
|
12
|
-
*
|
|
13
11
|
* @example
|
|
14
12
|
* ```
|
|
15
13
|
* RETURN date() AS today
|
|
@@ -8,8 +8,6 @@ import { buildDatetimeObject, parseTemporalArg } from "./temporal_utils";
|
|
|
8
8
|
* When called with a string argument, parses it as an ISO 8601 datetime.
|
|
9
9
|
* When called with a map argument, constructs a datetime from components.
|
|
10
10
|
*
|
|
11
|
-
* Equivalent to Neo4j's datetime() function.
|
|
12
|
-
*
|
|
13
11
|
* @example
|
|
14
12
|
* ```
|
|
15
13
|
* RETURN datetime() AS now
|
|
@@ -8,8 +8,6 @@ import { buildDatetimeObject, parseTemporalArg } from "./temporal_utils";
|
|
|
8
8
|
* When called with a string argument, parses it as an ISO 8601 datetime.
|
|
9
9
|
* When called with a map argument, constructs a datetime from components.
|
|
10
10
|
*
|
|
11
|
-
* Equivalent to Neo4j's localdatetime() function.
|
|
12
|
-
*
|
|
13
11
|
* @example
|
|
14
12
|
* ```
|
|
15
13
|
* RETURN localdatetime() AS now
|
|
@@ -7,8 +7,6 @@ import { buildTimeObject, parseTemporalArg } from "./temporal_utils";
|
|
|
7
7
|
* When called with no arguments, returns the current local time.
|
|
8
8
|
* When called with a string argument, parses it.
|
|
9
9
|
*
|
|
10
|
-
* Equivalent to Neo4j's localtime() function.
|
|
11
|
-
*
|
|
12
10
|
* @example
|
|
13
11
|
* ```
|
|
14
12
|
* RETURN localtime() AS now
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Computes the ISO day of the week (1 = Monday, 7 = Sunday)
|
|
9
|
+
* Computes the ISO day of the week (1 = Monday, 7 = Sunday).
|
|
10
10
|
*/
|
|
11
11
|
function isoDayOfWeek(d: Date): number {
|
|
12
12
|
const jsDay = d.getDay(); // 0 = Sunday, 6 = Saturday
|
|
@@ -7,8 +7,6 @@ import { buildTimeObject, parseTemporalArg } from "./temporal_utils";
|
|
|
7
7
|
* When called with no arguments, returns the current UTC time.
|
|
8
8
|
* When called with a string argument, parses it as an ISO 8601 time.
|
|
9
9
|
*
|
|
10
|
-
* Equivalent to Neo4j's time() function.
|
|
11
|
-
*
|
|
12
10
|
* @example
|
|
13
11
|
* ```
|
|
14
12
|
* RETURN time() AS now
|
|
@@ -4,17 +4,13 @@ import { FunctionDef } from "./function_metadata";
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns the number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
|
|
6
6
|
*
|
|
7
|
-
* Equivalent to Neo4j's timestamp() function.
|
|
8
|
-
*
|
|
9
7
|
* @example
|
|
10
8
|
* ```
|
|
11
9
|
* RETURN timestamp() AS ts
|
|
12
10
|
* ```
|
|
13
11
|
*/
|
|
14
12
|
@FunctionDef({
|
|
15
|
-
description:
|
|
16
|
-
"Returns the number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z). " +
|
|
17
|
-
"Equivalent to Neo4j's timestamp() function.",
|
|
13
|
+
description: "Returns the number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z).",
|
|
18
14
|
category: "scalar",
|
|
19
15
|
parameters: [],
|
|
20
16
|
output: {
|
|
@@ -52,10 +52,12 @@ class GroupBy extends Projection {
|
|
|
52
52
|
let node: Node = this.current;
|
|
53
53
|
for (const mapper of this.mappers) {
|
|
54
54
|
const value: any = mapper.value();
|
|
55
|
-
|
|
55
|
+
const key: string =
|
|
56
|
+
typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
57
|
+
let child: Node | undefined = node.children.get(key);
|
|
56
58
|
if (child === undefined) {
|
|
57
59
|
child = new Node(value);
|
|
58
|
-
node.children.set(
|
|
60
|
+
node.children.set(key, child);
|
|
59
61
|
}
|
|
60
62
|
node = child;
|
|
61
63
|
}
|
|
@@ -7,6 +7,12 @@ class Limit extends Operation {
|
|
|
7
7
|
super();
|
|
8
8
|
this.limit = limit;
|
|
9
9
|
}
|
|
10
|
+
public get isLimitReached(): boolean {
|
|
11
|
+
return this.count >= this.limit;
|
|
12
|
+
}
|
|
13
|
+
public increment(): void {
|
|
14
|
+
this.count++;
|
|
15
|
+
}
|
|
10
16
|
public async run(): Promise<void> {
|
|
11
17
|
if (this.count >= this.limit) {
|
|
12
18
|
return;
|
|
@@ -19,4 +25,4 @@ class Limit extends Operation {
|
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
export default Limit;
|
|
28
|
+
export default Limit;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Limit from "./limit";
|
|
1
2
|
import Projection from "./projection";
|
|
2
3
|
import Where from "./where";
|
|
3
4
|
|
|
@@ -15,6 +16,7 @@ import Where from "./where";
|
|
|
15
16
|
class Return extends Projection {
|
|
16
17
|
protected _where: Where | null = null;
|
|
17
18
|
protected _results: Record<string, any>[] = [];
|
|
19
|
+
private _limit: Limit | null = null;
|
|
18
20
|
public set where(where: Where) {
|
|
19
21
|
this._where = where;
|
|
20
22
|
}
|
|
@@ -24,10 +26,16 @@ class Return extends Projection {
|
|
|
24
26
|
}
|
|
25
27
|
return this._where.value();
|
|
26
28
|
}
|
|
29
|
+
public set limit(limit: Limit) {
|
|
30
|
+
this._limit = limit;
|
|
31
|
+
}
|
|
27
32
|
public async run(): Promise<void> {
|
|
28
33
|
if (!this.where) {
|
|
29
34
|
return;
|
|
30
35
|
}
|
|
36
|
+
if (this._limit !== null && this._limit.isLimitReached) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
31
39
|
const record: Map<string, any> = new Map();
|
|
32
40
|
for (const [expression, alias] of this.expressions()) {
|
|
33
41
|
const raw = expression.value();
|
|
@@ -35,6 +43,9 @@ class Return extends Projection {
|
|
|
35
43
|
record.set(alias, value);
|
|
36
44
|
}
|
|
37
45
|
this._results.push(Object.fromEntries(record));
|
|
46
|
+
if (this._limit !== null) {
|
|
47
|
+
this._limit.increment();
|
|
48
|
+
}
|
|
38
49
|
}
|
|
39
50
|
public async initialize(): Promise<void> {
|
|
40
51
|
this._results = [];
|
package/src/parsing/parser.ts
CHANGED
|
@@ -112,6 +112,9 @@ class Parser extends BaseParser {
|
|
|
112
112
|
if (this.token.isUnion()) {
|
|
113
113
|
break;
|
|
114
114
|
}
|
|
115
|
+
if (this.token.isEOF()) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
115
118
|
operation = this.parseOperation();
|
|
116
119
|
if (operation === null && !isSubQuery) {
|
|
117
120
|
throw new Error("Expected one of WITH, UNWIND, RETURN, LOAD, OR CALL");
|
|
@@ -142,8 +145,12 @@ class Parser extends BaseParser {
|
|
|
142
145
|
}
|
|
143
146
|
const limit = this.parseLimit();
|
|
144
147
|
if (limit !== null) {
|
|
145
|
-
operation
|
|
146
|
-
|
|
148
|
+
if (operation instanceof Return) {
|
|
149
|
+
(operation as Return).limit = limit;
|
|
150
|
+
} else {
|
|
151
|
+
operation!.addSibling(limit);
|
|
152
|
+
operation = limit;
|
|
153
|
+
}
|
|
147
154
|
}
|
|
148
155
|
previous = operation;
|
|
149
156
|
}
|
|
@@ -494,16 +501,11 @@ class Parser extends BaseParser {
|
|
|
494
501
|
node.label = label!;
|
|
495
502
|
if (identifier !== null && this._state.variables.has(identifier)) {
|
|
496
503
|
let reference = this._state.variables.get(identifier);
|
|
497
|
-
// Resolve through Expression -> Reference -> Node (e.g., after WITH)
|
|
498
|
-
if (reference instanceof Expression && reference.firstChild() instanceof Reference) {
|
|
499
|
-
const inner = (reference.firstChild() as Reference).referred;
|
|
500
|
-
if (inner instanceof Node) {
|
|
501
|
-
reference = inner;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
504
|
if (
|
|
505
505
|
reference === undefined ||
|
|
506
|
-
(!(reference instanceof Node) &&
|
|
506
|
+
(!(reference instanceof Node) &&
|
|
507
|
+
!(reference instanceof Unwind) &&
|
|
508
|
+
!(reference instanceof Expression))
|
|
507
509
|
) {
|
|
508
510
|
throw new Error(`Undefined node reference: ${identifier}`);
|
|
509
511
|
}
|
|
@@ -842,6 +842,19 @@ test("Test limit", async () => {
|
|
|
842
842
|
expect(results.length).toBe(50);
|
|
843
843
|
});
|
|
844
844
|
|
|
845
|
+
test("Test limit as last operation", async () => {
|
|
846
|
+
const runner = new Runner(
|
|
847
|
+
`
|
|
848
|
+
unwind range(1, 10) as i
|
|
849
|
+
return i
|
|
850
|
+
limit 5
|
|
851
|
+
`
|
|
852
|
+
);
|
|
853
|
+
await runner.run();
|
|
854
|
+
const results = runner.results;
|
|
855
|
+
expect(results.length).toBe(5);
|
|
856
|
+
});
|
|
857
|
+
|
|
845
858
|
test("Test range lookup", async () => {
|
|
846
859
|
const runner = new Runner(
|
|
847
860
|
`
|
|
@@ -1327,6 +1340,60 @@ test("Test match with referenced to previous variable", async () => {
|
|
|
1327
1340
|
expect(results[1]).toEqual({ name1: "Person 2", name2: "Person 3", name3: "Person 4" });
|
|
1328
1341
|
});
|
|
1329
1342
|
|
|
1343
|
+
test("Test match with aggregated with and subsequent match", async () => {
|
|
1344
|
+
await new Runner(`
|
|
1345
|
+
CREATE VIRTUAL (:User) AS {
|
|
1346
|
+
unwind [
|
|
1347
|
+
{id: 1, name: 'Alice'},
|
|
1348
|
+
{id: 2, name: 'Bob'},
|
|
1349
|
+
{id: 3, name: 'Carol'}
|
|
1350
|
+
] as record
|
|
1351
|
+
RETURN record.id as id, record.name as name
|
|
1352
|
+
}
|
|
1353
|
+
`).run();
|
|
1354
|
+
await new Runner(`
|
|
1355
|
+
CREATE VIRTUAL (:User)-[:KNOWS]-(:User) AS {
|
|
1356
|
+
unwind [
|
|
1357
|
+
{left_id: 1, right_id: 2},
|
|
1358
|
+
{left_id: 1, right_id: 3}
|
|
1359
|
+
] as record
|
|
1360
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
1361
|
+
}
|
|
1362
|
+
`).run();
|
|
1363
|
+
await new Runner(`
|
|
1364
|
+
CREATE VIRTUAL (:Project) AS {
|
|
1365
|
+
unwind [
|
|
1366
|
+
{id: 1, name: 'Project A'},
|
|
1367
|
+
{id: 2, name: 'Project B'}
|
|
1368
|
+
] as record
|
|
1369
|
+
RETURN record.id as id, record.name as name
|
|
1370
|
+
}
|
|
1371
|
+
`).run();
|
|
1372
|
+
await new Runner(`
|
|
1373
|
+
CREATE VIRTUAL (:User)-[:WORKS_ON]-(:Project) AS {
|
|
1374
|
+
unwind [
|
|
1375
|
+
{left_id: 1, right_id: 1},
|
|
1376
|
+
{left_id: 1, right_id: 2}
|
|
1377
|
+
] as record
|
|
1378
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
1379
|
+
}
|
|
1380
|
+
`).run();
|
|
1381
|
+
const match = new Runner(`
|
|
1382
|
+
MATCH (u:User)-[:KNOWS]->(s:User)
|
|
1383
|
+
WITH u, count(s) as acquaintances
|
|
1384
|
+
MATCH (u)-[:WORKS_ON]->(p:Project)
|
|
1385
|
+
RETURN u.name as name, acquaintances, collect(p.name) as projects
|
|
1386
|
+
`);
|
|
1387
|
+
await match.run();
|
|
1388
|
+
const results = match.results;
|
|
1389
|
+
expect(results.length).toBe(1);
|
|
1390
|
+
expect(results[0]).toEqual({
|
|
1391
|
+
name: "Alice",
|
|
1392
|
+
acquaintances: 2,
|
|
1393
|
+
projects: ["Project A", "Project B"],
|
|
1394
|
+
});
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1330
1397
|
test("Test match and return full node", async () => {
|
|
1331
1398
|
await new Runner(`
|
|
1332
1399
|
CREATE VIRTUAL (:Person) AS {
|
|
@@ -2039,7 +2106,7 @@ test("Test optional match property access on null node returns null", async () =
|
|
|
2039
2106
|
RETURN record.left_id as left_id, record.right_id as right_id
|
|
2040
2107
|
}
|
|
2041
2108
|
`).run();
|
|
2042
|
-
// When accessing b.name and b is null (no match), should return null
|
|
2109
|
+
// When accessing b.name and b is null (no match), should return null
|
|
2043
2110
|
const match = new Runner(`
|
|
2044
2111
|
MATCH (a:Person)
|
|
2045
2112
|
OPTIONAL MATCH (a)-[:KNOWS]->(b:Person)
|
|
@@ -3246,7 +3313,7 @@ test("Test coalesce with property access", async () => {
|
|
|
3246
3313
|
});
|
|
3247
3314
|
|
|
3248
3315
|
// ============================================================
|
|
3249
|
-
// Temporal / Time Functions
|
|
3316
|
+
// Temporal / Time Functions
|
|
3250
3317
|
// ============================================================
|
|
3251
3318
|
|
|
3252
3319
|
test("Test datetime() returns current datetime object", async () => {
|