flowquery 1.0.14 → 1.0.15
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/.editorconfig +21 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +22 -0
- package/dist/flowquery.min.js +1 -1
- package/dist/parsing/expressions/expression_map.d.ts +8 -0
- package/dist/parsing/expressions/expression_map.d.ts.map +1 -0
- package/dist/parsing/expressions/expression_map.js +21 -0
- package/dist/parsing/expressions/expression_map.js.map +1 -0
- package/dist/parsing/operations/call.d.ts +17 -0
- package/dist/parsing/operations/call.d.ts.map +1 -0
- package/dist/parsing/operations/call.js +103 -0
- package/dist/parsing/operations/call.js.map +1 -0
- package/dist/parsing/operations/load.d.ts +6 -6
- package/dist/parsing/operations/load.d.ts.map +1 -1
- package/dist/parsing/operations/load.js +8 -6
- package/dist/parsing/operations/load.js.map +1 -1
- package/dist/parsing/operations/operation.d.ts +1 -0
- package/dist/parsing/operations/operation.d.ts.map +1 -1
- package/dist/parsing/operations/operation.js +6 -5
- package/dist/parsing/operations/operation.js.map +1 -1
- package/dist/parsing/operations/projection.d.ts +1 -1
- package/dist/parsing/operations/projection.d.ts.map +1 -1
- package/dist/parsing/operations/projection.js.map +1 -1
- package/dist/parsing/parser.d.ts +1 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +148 -99
- package/dist/parsing/parser.js.map +1 -1
- package/dist/parsing/token_to_node.d.ts +2 -2
- package/dist/parsing/token_to_node.d.ts.map +1 -1
- package/dist/parsing/token_to_node.js +12 -12
- package/dist/parsing/token_to_node.js.map +1 -1
- package/dist/tokenization/token.d.ts +5 -1
- package/dist/tokenization/token.d.ts.map +1 -1
- package/dist/tokenization/token.js +17 -5
- package/dist/tokenization/token.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/package.json +1 -1
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +66 -0
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +4 -3
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +7 -5
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +3 -3
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +4 -3
- package/package.json +12 -2
- package/src/parsing/expressions/expression_map.ts +19 -0
- package/src/parsing/operations/call.ts +67 -0
- package/src/parsing/operations/load.ts +123 -120
- package/src/parsing/operations/operation.ts +14 -13
- package/src/parsing/operations/projection.ts +3 -3
- package/src/parsing/parser.ts +303 -239
- package/src/parsing/token_to_node.ts +67 -50
- package/src/tokenization/token.ts +29 -14
- package/tests/compute/runner.test.ts +277 -165
- package/tests/parsing/parser.test.ts +355 -303
- package/vscode-settings.json.recommended +16 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* RETURN user.name, user.email
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { FunctionDef } from 'flowquery/extensibility';
|
|
9
|
+
import { FunctionDef, AsyncFunction } from 'flowquery/extensibility';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* MockUsers class - generates mock user data for testing.
|
|
@@ -39,7 +39,7 @@ import { FunctionDef } from 'flowquery/extensibility';
|
|
|
39
39
|
"LOAD JSON FROM mockUsers(20) AS user RETURN user WHERE user.active = true"
|
|
40
40
|
]
|
|
41
41
|
})
|
|
42
|
-
export class MockUsers {
|
|
42
|
+
export class MockUsers extends AsyncFunction {
|
|
43
43
|
private readonly firstNames: string[];
|
|
44
44
|
private readonly lastNames: string[];
|
|
45
45
|
private readonly domains: string[];
|
|
@@ -49,6 +49,7 @@ export class MockUsers {
|
|
|
49
49
|
lastNames: string[] = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez'],
|
|
50
50
|
domains: string[] = ['example.com', 'test.org', 'demo.net']
|
|
51
51
|
) {
|
|
52
|
+
super();
|
|
52
53
|
this.firstNames = firstNames;
|
|
53
54
|
this.lastNames = lastNames;
|
|
54
55
|
this.domains = domains;
|
|
@@ -59,7 +60,7 @@ export class MockUsers {
|
|
|
59
60
|
*
|
|
60
61
|
* @param count - Number of mock users to generate
|
|
61
62
|
*/
|
|
62
|
-
async *
|
|
63
|
+
async *generate(count: number = 5): AsyncGenerator<any, void, unknown> {
|
|
63
64
|
for (let i = 0; i < count; i++) {
|
|
64
65
|
const firstName = this.firstNames[Math.floor(Math.random() * this.firstNames.length)];
|
|
65
66
|
const lastName = this.lastNames[Math.floor(Math.random() * this.lastNames.length)];
|
|
@@ -108,7 +109,7 @@ export class MockUsers {
|
|
|
108
109
|
"LOAD JSON FROM mockProducts(50) AS p RETURN p WHERE p.category = 'Electronics'"
|
|
109
110
|
]
|
|
110
111
|
})
|
|
111
|
-
export class MockProducts {
|
|
112
|
+
export class MockProducts extends AsyncFunction {
|
|
112
113
|
private readonly categories: string[];
|
|
113
114
|
private readonly adjectives: string[];
|
|
114
115
|
private readonly nouns: string[];
|
|
@@ -118,6 +119,7 @@ export class MockProducts {
|
|
|
118
119
|
adjectives: string[] = ['Premium', 'Basic', 'Pro', 'Ultra', 'Classic'],
|
|
119
120
|
nouns: string[] = ['Widget', 'Gadget', 'Item', 'Product', 'Thing']
|
|
120
121
|
) {
|
|
122
|
+
super();
|
|
121
123
|
this.categories = categories;
|
|
122
124
|
this.adjectives = adjectives;
|
|
123
125
|
this.nouns = nouns;
|
|
@@ -128,7 +130,7 @@ export class MockProducts {
|
|
|
128
130
|
*
|
|
129
131
|
* @param count - Number of mock products to generate
|
|
130
132
|
*/
|
|
131
|
-
async *
|
|
133
|
+
async *generate(count: number = 5): AsyncGenerator<any, void, unknown> {
|
|
132
134
|
for (let i = 0; i < count; i++) {
|
|
133
135
|
const adj = this.adjectives[Math.floor(Math.random() * this.adjectives.length)];
|
|
134
136
|
const noun = this.nouns[Math.floor(Math.random() * this.nouns.length)];
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Note: Async providers cannot be nested as function arguments.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { FunctionDef } from 'flowquery/extensibility';
|
|
17
|
+
import { FunctionDef, AsyncFunction } from 'flowquery/extensibility';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Interface for Adaptive Card structure
|
|
@@ -90,7 +90,7 @@ interface TableRow {
|
|
|
90
90
|
"LOAD JSON FROM mockProducts(10) AS p WITH collect(p) AS products LOAD JSON FROM table(products, 'Products', ['name', 'price', 'category']) AS card RETURN card"
|
|
91
91
|
]
|
|
92
92
|
})
|
|
93
|
-
export class Table {
|
|
93
|
+
export class Table extends AsyncFunction {
|
|
94
94
|
/**
|
|
95
95
|
* Transforms data into an Adaptive Card with table layout.
|
|
96
96
|
*
|
|
@@ -99,7 +99,7 @@ export class Table {
|
|
|
99
99
|
* @param columns - Optional column names to include
|
|
100
100
|
* @param maxRows - Maximum rows to include
|
|
101
101
|
*/
|
|
102
|
-
async *
|
|
102
|
+
async *generate(
|
|
103
103
|
data: any[] | AsyncIterable<any>,
|
|
104
104
|
title: string = 'Data Table',
|
|
105
105
|
columns?: string[],
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* RETURN forecast
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { FunctionDef } from 'flowquery/extensibility';
|
|
9
|
+
import { FunctionDef, AsyncFunction } from 'flowquery/extensibility';
|
|
10
10
|
|
|
11
11
|
const GEOCODING_API = 'https://geocoding-api.open-meteo.com/v1/search';
|
|
12
12
|
const WEATHER_API = 'https://api.met.no/weatherapi/locationforecast/2.0/compact';
|
|
@@ -48,11 +48,12 @@ const WEATHER_API = 'https://api.met.no/weatherapi/locationforecast/2.0/compact'
|
|
|
48
48
|
"LOAD JSON FROM weather('London') AS forecast RETURN forecast[0]"
|
|
49
49
|
]
|
|
50
50
|
})
|
|
51
|
-
export class Weather {
|
|
51
|
+
export class Weather extends AsyncFunction {
|
|
52
52
|
private readonly geocodingApiUrl: string;
|
|
53
53
|
private readonly weatherApiUrl: string;
|
|
54
54
|
|
|
55
55
|
constructor(geocodingApiUrl: string = GEOCODING_API, weatherApiUrl: string = WEATHER_API) {
|
|
56
|
+
super();
|
|
56
57
|
this.geocodingApiUrl = geocodingApiUrl;
|
|
57
58
|
this.weatherApiUrl = weatherApiUrl;
|
|
58
59
|
}
|
|
@@ -62,7 +63,7 @@ export class Weather {
|
|
|
62
63
|
*
|
|
63
64
|
* @param location - The name of the location to get weather for
|
|
64
65
|
*/
|
|
65
|
-
async *
|
|
66
|
+
async *generate(location: string): AsyncGenerator<any, void, unknown> {
|
|
66
67
|
// Step 1: Geocode the location name to get lat/lon
|
|
67
68
|
const geocodeUrl = `${this.geocodingApiUrl}?name=${encodeURIComponent(location)}`;
|
|
68
69
|
const geocodeResponse = await fetch(geocodeUrl);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowquery",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "A declarative query language for data processing pipelines.",
|
|
5
5
|
"main": "dist/index.node.js",
|
|
6
6
|
"types": "dist/index.node.d.ts",
|
|
@@ -31,16 +31,26 @@
|
|
|
31
31
|
"build:node": "tsc",
|
|
32
32
|
"build:browser": "webpack",
|
|
33
33
|
"main": "tsc & node dist/index.js",
|
|
34
|
-
"docs": "typedoc --out docs/api src/index.node.ts"
|
|
34
|
+
"docs": "typedoc --out docs/api src/index.node.ts",
|
|
35
|
+
"prepare": "husky",
|
|
36
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\""
|
|
37
|
+
},
|
|
38
|
+
"lint-staged": {
|
|
39
|
+
"*.{ts,js}": "prettier --write",
|
|
40
|
+
"*.{json,md}": "prettier --write"
|
|
35
41
|
},
|
|
36
42
|
"keywords": [],
|
|
37
43
|
"author": "",
|
|
38
44
|
"license": "ISC",
|
|
39
45
|
"devDependencies": {
|
|
46
|
+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
40
47
|
"@types/jest": "^29.5.14",
|
|
41
48
|
"@types/node": "^25.0.3",
|
|
42
49
|
"copyfiles": "^2.4.1",
|
|
50
|
+
"husky": "^9.1.7",
|
|
43
51
|
"jest": "^29.7.0",
|
|
52
|
+
"lint-staged": "^15.2.11",
|
|
53
|
+
"prettier": "^3.4.2",
|
|
44
54
|
"ts-jest": "^29.2.5",
|
|
45
55
|
"ts-loader": "^9.5.1",
|
|
46
56
|
"ts-node": "^10.9.2",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Expression from "./expression";
|
|
2
|
+
|
|
3
|
+
class ExpressionMap {
|
|
4
|
+
private _map: Map<string, Expression> = new Map();
|
|
5
|
+
public get(alias: string): Expression | undefined {
|
|
6
|
+
return this._map.get(alias);
|
|
7
|
+
}
|
|
8
|
+
public set map(expressions: Expression[]) {
|
|
9
|
+
this._map.clear();
|
|
10
|
+
for (const expr of expressions) {
|
|
11
|
+
if (expr.alias == undefined) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
this._map.set(expr.alias, expr);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default ExpressionMap;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Expression from "../expressions/expression";
|
|
2
|
+
import ExpressionMap from "../expressions/expression_map";
|
|
3
|
+
import AsyncFunction from "../functions/async_function";
|
|
4
|
+
import Projection from "./projection";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_VARIABLE_NAME: string = "value";
|
|
7
|
+
|
|
8
|
+
class Call extends Projection {
|
|
9
|
+
protected _function: AsyncFunction | null = null;
|
|
10
|
+
private _map: ExpressionMap = new ExpressionMap();
|
|
11
|
+
protected _results: Record<string, any>[] = [];
|
|
12
|
+
constructor() {
|
|
13
|
+
super([]);
|
|
14
|
+
}
|
|
15
|
+
public set function(asyncFunction: AsyncFunction) {
|
|
16
|
+
this._function = asyncFunction;
|
|
17
|
+
}
|
|
18
|
+
public get function(): AsyncFunction | null {
|
|
19
|
+
return this._function;
|
|
20
|
+
}
|
|
21
|
+
public set yielded(expressions: Expression[]) {
|
|
22
|
+
this.children = expressions;
|
|
23
|
+
this._map.map = expressions;
|
|
24
|
+
}
|
|
25
|
+
public get hasYield(): boolean {
|
|
26
|
+
return this.children.length > 0;
|
|
27
|
+
}
|
|
28
|
+
public async run(): Promise<void> {
|
|
29
|
+
if (this._function === null) {
|
|
30
|
+
throw new Error("No function set for Call operation.");
|
|
31
|
+
}
|
|
32
|
+
const args = this._function.getArguments();
|
|
33
|
+
for await (const item of this._function.generate(...args)) {
|
|
34
|
+
if (!this.isLast) {
|
|
35
|
+
if (typeof item == "object" && !Array.isArray(item)) {
|
|
36
|
+
for (const [key, value] of Object.entries(item)) {
|
|
37
|
+
const expression = this._map.get(key);
|
|
38
|
+
if (expression) {
|
|
39
|
+
expression.overridden = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
const expression = this._map.get(DEFAULT_VARIABLE_NAME);
|
|
44
|
+
if (expression) {
|
|
45
|
+
expression.overridden = item;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
await this.next?.run();
|
|
49
|
+
} else {
|
|
50
|
+
const record: Map<string, any> = new Map();
|
|
51
|
+
if (typeof item == "object" && !Array.isArray(item)) {
|
|
52
|
+
for (const [key, value] of Object.entries(item)) {
|
|
53
|
+
record.set(key, value);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
record.set(DEFAULT_VARIABLE_NAME, item);
|
|
57
|
+
}
|
|
58
|
+
this._results.push(Object.fromEntries(record));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
public get results(): Record<string, any>[] {
|
|
63
|
+
return this._results;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default Call;
|
|
@@ -1,143 +1,146 @@
|
|
|
1
|
-
import Operation from "./operation";
|
|
2
1
|
import CSV from "../components/csv";
|
|
3
|
-
import
|
|
4
|
-
import Text from "../components/text";
|
|
5
|
-
import Function from "../functions/function";
|
|
6
|
-
import AsyncFunction from "../functions/async_function";
|
|
7
|
-
import AssociativeArray from "../data_structures/associative_array";
|
|
8
|
-
import Reference from "../expressions/reference";
|
|
9
|
-
import Expression from "../expressions/expression";
|
|
2
|
+
import From from "../components/from";
|
|
10
3
|
import Headers from "../components/headers";
|
|
4
|
+
import { default as _JSON } from "../components/json";
|
|
11
5
|
import Post from "../components/post";
|
|
6
|
+
import Text from "../components/text";
|
|
7
|
+
import AssociativeArray from "../data_structures/associative_array";
|
|
12
8
|
import Lookup from "../data_structures/lookup";
|
|
13
|
-
import
|
|
9
|
+
import Expression from "../expressions/expression";
|
|
10
|
+
import Reference from "../expressions/reference";
|
|
11
|
+
import AsyncFunction from "../functions/async_function";
|
|
12
|
+
import Function from "../functions/function";
|
|
13
|
+
import Operation from "./operation";
|
|
14
14
|
|
|
15
15
|
class Load extends Operation {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
private _value: any = null;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
public get type(): _JSON | CSV | Text {
|
|
22
|
+
return this.children[0] as _JSON | CSV | Text;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Gets the From component which contains either a URL expression or an AsyncFunction.
|
|
27
|
+
*/
|
|
28
|
+
public get fromComponent(): From {
|
|
29
|
+
return this.children[1] as From;
|
|
30
|
+
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Checks if the data source is an async function.
|
|
34
|
+
*/
|
|
35
|
+
public get isAsyncFunction(): boolean {
|
|
36
|
+
return this.fromComponent.firstChild() instanceof AsyncFunction;
|
|
37
|
+
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Gets the async function if the source is a function, otherwise null.
|
|
41
|
+
*/
|
|
42
|
+
public get asyncFunction(): AsyncFunction | null {
|
|
43
|
+
const child = this.fromComponent.firstChild();
|
|
44
|
+
return child instanceof AsyncFunction ? child : null;
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
public get headers(): { [key: string]: string } {
|
|
50
|
-
if(this.childCount() > 2 && this.children[2] instanceof Headers) {
|
|
51
|
-
return this.children[2].value() as { [key: string]: string } || {};
|
|
47
|
+
public get from(): string {
|
|
48
|
+
return this.children[1].value() as string;
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
post = this.children[2] as Post;
|
|
59
|
-
} else if(this.childCount() > 3 && this.children[3] instanceof Post) {
|
|
60
|
-
post = this.children[3] as Post;
|
|
50
|
+
public get headers(): { [key: string]: string } {
|
|
51
|
+
if (this.childCount() > 2 && this.children[2] instanceof Headers) {
|
|
52
|
+
return (this.children[2].value() as { [key: string]: string }) || {};
|
|
53
|
+
}
|
|
54
|
+
return {};
|
|
61
55
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
public get payload(): Function | Reference | Expression | AssociativeArray | Lookup | null {
|
|
57
|
+
let post: Post | null = null;
|
|
58
|
+
if (this.childCount() > 2 && this.children[2] instanceof Post) {
|
|
59
|
+
post = this.children[2] as Post;
|
|
60
|
+
} else if (this.childCount() > 3 && this.children[3] instanceof Post) {
|
|
61
|
+
post = this.children[3] as Post;
|
|
62
|
+
}
|
|
63
|
+
return post !== null
|
|
64
|
+
? (post.firstChild() as Function | Reference | Expression | AssociativeArray | Lookup)
|
|
65
|
+
: null;
|
|
69
66
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
headers["Content-Type"] = "application/json";
|
|
67
|
+
private method(): "GET" | "POST" {
|
|
68
|
+
if (this.payload === null) {
|
|
69
|
+
return "GET";
|
|
70
|
+
} else {
|
|
71
|
+
return "POST";
|
|
72
|
+
}
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const asyncFunc = this.asyncFunction!;
|
|
91
|
-
const args = asyncFunc.getArguments();
|
|
92
|
-
for await (const item of asyncFunc.generate(...args)) {
|
|
93
|
-
this._value = item;
|
|
94
|
-
await this.next?.run();
|
|
74
|
+
private options(): object {
|
|
75
|
+
const headers = this.headers as { [key: string]: string };
|
|
76
|
+
const payload = this.payload;
|
|
77
|
+
const data = payload?.value();
|
|
78
|
+
if (data !== null && typeof data === "object" && !headers.hasOwnProperty("Content-Type")) {
|
|
79
|
+
headers["Content-Type"] = "application/json";
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
method: this.method(),
|
|
83
|
+
headers: headers,
|
|
84
|
+
...(payload !== null ? { body: JSON.stringify(payload.value()) } : {}),
|
|
85
|
+
};
|
|
95
86
|
}
|
|
96
|
-
}
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Loads data from an async function source.
|
|
90
|
+
* Arguments from the query (e.g., myFunc(arg1, arg2)) are passed to generate().
|
|
91
|
+
*/
|
|
92
|
+
private async loadFromFunction(): Promise<void> {
|
|
93
|
+
const asyncFunc = this.asyncFunction!;
|
|
94
|
+
const args = asyncFunc.getArguments();
|
|
95
|
+
for await (const item of asyncFunc.generate(...args)) {
|
|
96
|
+
this._value = item;
|
|
97
|
+
await this.next?.run();
|
|
98
|
+
}
|
|
108
99
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Loads data from a URL source (original behavior).
|
|
103
|
+
*/
|
|
104
|
+
private async loadFromUrl(): Promise<void> {
|
|
105
|
+
const result = await fetch(this.from, this.options());
|
|
106
|
+
let data: any = null;
|
|
107
|
+
if (this.type instanceof _JSON) {
|
|
108
|
+
data = await result.json();
|
|
109
|
+
} else if (this.type instanceof Text) {
|
|
110
|
+
data = await result.text();
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(data)) {
|
|
113
|
+
for (const item of data) {
|
|
114
|
+
this._value = item;
|
|
115
|
+
await this.next?.run();
|
|
116
|
+
}
|
|
117
|
+
} else if (typeof data === "object" && data !== null) {
|
|
118
|
+
this._value = data;
|
|
119
|
+
await this.next?.run();
|
|
120
|
+
} else if (typeof data === "string") {
|
|
121
|
+
this._value = data;
|
|
122
|
+
await this.next?.run();
|
|
123
|
+
}
|
|
120
124
|
}
|
|
121
|
-
}
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
public async load(): Promise<any> {
|
|
127
|
+
if (this.isAsyncFunction) {
|
|
128
|
+
await this.loadFromFunction();
|
|
129
|
+
} else {
|
|
130
|
+
await this.loadFromUrl();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
public async run(): Promise<void> {
|
|
134
|
+
try {
|
|
135
|
+
await this.load();
|
|
136
|
+
} catch (e) {
|
|
137
|
+
const source = this.isAsyncFunction ? this.asyncFunction?.name : this.from;
|
|
138
|
+
throw new Error(`Failed to load data from ${source}. Error: ${e}`);
|
|
139
|
+
}
|
|
128
140
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
await this.load();
|
|
133
|
-
} catch(e) {
|
|
134
|
-
const source = this.isAsyncFunction ? this.asyncFunction?.name : this.from;
|
|
135
|
-
throw new Error(`Failed to load data from ${source}. Error: ${e}`);
|
|
141
|
+
public value(): any {
|
|
142
|
+
return this._value;
|
|
136
143
|
}
|
|
137
|
-
}
|
|
138
|
-
public value(): any {
|
|
139
|
-
return this._value;
|
|
140
|
-
}
|
|
141
144
|
}
|
|
142
145
|
|
|
143
|
-
export default Load;
|
|
146
|
+
export default Load;
|
|
@@ -2,16 +2,16 @@ import ASTNode from "../ast_node";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Base class for all FlowQuery operations.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Operations represent the main statements in FlowQuery (WITH, UNWIND, RETURN, LOAD, WHERE).
|
|
7
7
|
* They form a linked list structure and can be executed sequentially.
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* @abstract
|
|
10
10
|
*/
|
|
11
11
|
abstract class Operation extends ASTNode {
|
|
12
12
|
private _previous: Operation | null = null;
|
|
13
13
|
private _next: Operation | null = null;
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
/**
|
|
16
16
|
* Creates a new Operation instance.
|
|
17
17
|
*/
|
|
@@ -35,31 +35,32 @@ abstract class Operation extends ASTNode {
|
|
|
35
35
|
operation.previous = this;
|
|
36
36
|
this.next = operation;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
public get isLast(): boolean {
|
|
39
|
+
return this._next === null;
|
|
40
|
+
}
|
|
41
|
+
|
|
39
42
|
/**
|
|
40
43
|
* Executes this operation. Must be implemented by subclasses.
|
|
41
|
-
*
|
|
44
|
+
*
|
|
42
45
|
* @returns A promise that resolves when the operation completes
|
|
43
46
|
* @throws {Error} If not implemented by subclass
|
|
44
47
|
*/
|
|
45
48
|
public async run(): Promise<void> {
|
|
46
|
-
throw new Error(
|
|
49
|
+
throw new Error("Not implemented");
|
|
47
50
|
}
|
|
48
|
-
|
|
51
|
+
|
|
49
52
|
/**
|
|
50
53
|
* Finishes execution by calling finish on the next operation in the chain.
|
|
51
|
-
*
|
|
54
|
+
*
|
|
52
55
|
* @returns A promise that resolves when all operations finish
|
|
53
56
|
*/
|
|
54
57
|
public async finish(): Promise<void> {
|
|
55
58
|
await this.next?.finish();
|
|
56
59
|
}
|
|
57
|
-
public reset(): void {
|
|
58
|
-
;
|
|
59
|
-
}
|
|
60
|
+
public reset(): void {}
|
|
60
61
|
public get results(): Record<string, any>[] {
|
|
61
|
-
throw new Error(
|
|
62
|
+
throw new Error("Not implemented");
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
export default Operation;
|
|
66
|
+
export default Operation;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Operation from "./operation";
|
|
2
1
|
import Expression from "../expressions/expression";
|
|
2
|
+
import Operation from "./operation";
|
|
3
3
|
|
|
4
4
|
class Projection extends Operation {
|
|
5
5
|
constructor(expressions: Expression[]) {
|
|
@@ -7,7 +7,7 @@ class Projection extends Operation {
|
|
|
7
7
|
this.children = expressions;
|
|
8
8
|
}
|
|
9
9
|
protected *expressions(): Generator<[Expression, string]> {
|
|
10
|
-
for(let i = 0; i < this.children.length; i++) {
|
|
10
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
11
11
|
const expression: Expression = this.children[i] as Expression;
|
|
12
12
|
const alias = expression.alias || `expr${i}`;
|
|
13
13
|
yield [expression, alias];
|
|
@@ -15,4 +15,4 @@ class Projection extends Operation {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export default Projection;
|
|
18
|
+
export default Projection;
|