flowquery 1.0.43 → 1.0.45
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/join.d.ts.map +1 -1
- package/dist/parsing/functions/join.js +6 -3
- package/dist/parsing/functions/join.js.map +1 -1
- package/dist/parsing/functions/keys.d.ts.map +1 -1
- package/dist/parsing/functions/keys.js +3 -5
- package/dist/parsing/functions/keys.js.map +1 -1
- package/dist/parsing/functions/range.d.ts.map +1 -1
- package/dist/parsing/functions/range.js +11 -3
- package/dist/parsing/functions/range.js.map +1 -1
- package/dist/parsing/functions/replace.d.ts.map +1 -1
- package/dist/parsing/functions/replace.js +8 -3
- package/dist/parsing/functions/replace.js.map +1 -1
- package/dist/parsing/functions/round.d.ts.map +1 -1
- package/dist/parsing/functions/round.js +5 -4
- package/dist/parsing/functions/round.js.map +1 -1
- package/dist/parsing/functions/size.d.ts.map +1 -1
- package/dist/parsing/functions/size.js +5 -4
- package/dist/parsing/functions/size.js.map +1 -1
- package/dist/parsing/functions/split.d.ts.map +1 -1
- package/dist/parsing/functions/split.js +12 -4
- package/dist/parsing/functions/split.js.map +1 -1
- package/dist/parsing/functions/string_distance.d.ts.map +1 -1
- package/dist/parsing/functions/string_distance.js +3 -0
- package/dist/parsing/functions/string_distance.js.map +1 -1
- package/dist/parsing/functions/stringify.d.ts.map +1 -1
- package/dist/parsing/functions/stringify.js +7 -6
- package/dist/parsing/functions/stringify.js.map +1 -1
- package/dist/parsing/functions/substring.d.ts.map +1 -1
- package/dist/parsing/functions/substring.js +3 -0
- package/dist/parsing/functions/substring.js.map +1 -1
- package/dist/parsing/functions/to_json.d.ts.map +1 -1
- package/dist/parsing/functions/to_json.js +5 -4
- package/dist/parsing/functions/to_json.js.map +1 -1
- package/dist/parsing/functions/to_lower.d.ts.map +1 -1
- package/dist/parsing/functions/to_lower.js +3 -0
- package/dist/parsing/functions/to_lower.js.map +1 -1
- package/dist/parsing/functions/to_string.js +1 -1
- package/dist/parsing/functions/to_string.js.map +1 -1
- package/dist/parsing/functions/trim.d.ts.map +1 -1
- package/dist/parsing/functions/trim.js +3 -0
- package/dist/parsing/functions/trim.js.map +1 -1
- package/dist/parsing/operations/order_by.d.ts +22 -2
- package/dist/parsing/operations/order_by.d.ts.map +1 -1
- package/dist/parsing/operations/order_by.js +54 -6
- package/dist/parsing/operations/order_by.js.map +1 -1
- package/dist/parsing/operations/return.d.ts.map +1 -1
- package/dist/parsing/operations/return.js +4 -0
- package/dist/parsing/operations/return.js.map +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +4 -5
- 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/join.py +2 -0
- package/flowquery-py/src/parsing/functions/keys.py +1 -1
- package/flowquery-py/src/parsing/functions/range_.py +2 -0
- package/flowquery-py/src/parsing/functions/replace.py +2 -0
- package/flowquery-py/src/parsing/functions/round_.py +2 -0
- package/flowquery-py/src/parsing/functions/size.py +2 -0
- package/flowquery-py/src/parsing/functions/split.py +2 -0
- package/flowquery-py/src/parsing/functions/string_distance.py +5 -1
- package/flowquery-py/src/parsing/functions/stringify.py +2 -0
- package/flowquery-py/src/parsing/functions/substring.py +2 -0
- package/flowquery-py/src/parsing/functions/to_json.py +2 -0
- package/flowquery-py/src/parsing/functions/to_lower.py +2 -0
- package/flowquery-py/src/parsing/functions/to_string.py +1 -1
- package/flowquery-py/src/parsing/functions/trim.py +2 -0
- package/flowquery-py/src/parsing/operations/order_by.py +55 -13
- package/flowquery-py/src/parsing/operations/return_op.py +3 -0
- package/flowquery-py/src/parsing/parser.py +4 -5
- package/flowquery-py/tests/compute/test_runner.py +255 -0
- package/flowquery-py/tests/parsing/test_parser.py +63 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/parsing/functions/join.ts +8 -5
- package/src/parsing/functions/keys.ts +4 -6
- package/src/parsing/functions/range.ts +12 -4
- package/src/parsing/functions/replace.ts +11 -4
- package/src/parsing/functions/round.ts +6 -5
- package/src/parsing/functions/size.ts +6 -5
- package/src/parsing/functions/split.ts +14 -6
- package/src/parsing/functions/string_distance.ts +3 -0
- package/src/parsing/functions/stringify.ts +9 -8
- package/src/parsing/functions/substring.ts +3 -0
- package/src/parsing/functions/to_json.ts +6 -5
- package/src/parsing/functions/to_lower.ts +3 -0
- package/src/parsing/functions/to_string.ts +1 -1
- package/src/parsing/functions/trim.ts +3 -0
- package/src/parsing/operations/order_by.ts +58 -7
- package/src/parsing/operations/return.ts +4 -0
- package/src/parsing/parser.ts +4 -5
- package/tests/compute/runner.test.ts +234 -0
- package/tests/parsing/parser.test.ts +56 -0
|
@@ -758,6 +758,120 @@ test("Test substring function with zero length", async () => {
|
|
|
758
758
|
expect(results[0]).toEqual({ result: "" });
|
|
759
759
|
});
|
|
760
760
|
|
|
761
|
+
// --- Null propagation tests ---
|
|
762
|
+
|
|
763
|
+
test("Test toLower with null returns null", async () => {
|
|
764
|
+
const runner = new Runner("RETURN toLower(null) as result");
|
|
765
|
+
await runner.run();
|
|
766
|
+
const results = runner.results;
|
|
767
|
+
expect(results.length).toBe(1);
|
|
768
|
+
expect(results[0]).toEqual({ result: null });
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
test("Test trim with null returns null", async () => {
|
|
772
|
+
const runner = new Runner("RETURN trim(null) as result");
|
|
773
|
+
await runner.run();
|
|
774
|
+
const results = runner.results;
|
|
775
|
+
expect(results.length).toBe(1);
|
|
776
|
+
expect(results[0]).toEqual({ result: null });
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
test("Test replace with null returns null", async () => {
|
|
780
|
+
const runner = new Runner("RETURN replace(null, 'a', 'b') as result");
|
|
781
|
+
await runner.run();
|
|
782
|
+
const results = runner.results;
|
|
783
|
+
expect(results.length).toBe(1);
|
|
784
|
+
expect(results[0]).toEqual({ result: null });
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
test("Test substring with null returns null", async () => {
|
|
788
|
+
const runner = new Runner("RETURN substring(null, 0, 3) as result");
|
|
789
|
+
await runner.run();
|
|
790
|
+
const results = runner.results;
|
|
791
|
+
expect(results.length).toBe(1);
|
|
792
|
+
expect(results[0]).toEqual({ result: null });
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
test("Test split with null returns null", async () => {
|
|
796
|
+
const runner = new Runner("RETURN split(null, ',') as result");
|
|
797
|
+
await runner.run();
|
|
798
|
+
const results = runner.results;
|
|
799
|
+
expect(results.length).toBe(1);
|
|
800
|
+
expect(results[0]).toEqual({ result: null });
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
test("Test size with null returns null", async () => {
|
|
804
|
+
const runner = new Runner("RETURN size(null) as result");
|
|
805
|
+
await runner.run();
|
|
806
|
+
const results = runner.results;
|
|
807
|
+
expect(results.length).toBe(1);
|
|
808
|
+
expect(results[0]).toEqual({ result: null });
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
test("Test round with null returns null", async () => {
|
|
812
|
+
const runner = new Runner("RETURN round(null) as result");
|
|
813
|
+
await runner.run();
|
|
814
|
+
const results = runner.results;
|
|
815
|
+
expect(results.length).toBe(1);
|
|
816
|
+
expect(results[0]).toEqual({ result: null });
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
test("Test join with null returns null", async () => {
|
|
820
|
+
const runner = new Runner("RETURN join(null, ',') as result");
|
|
821
|
+
await runner.run();
|
|
822
|
+
const results = runner.results;
|
|
823
|
+
expect(results.length).toBe(1);
|
|
824
|
+
expect(results[0]).toEqual({ result: null });
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
test("Test string_distance with null returns null", async () => {
|
|
828
|
+
const runner = new Runner("RETURN string_distance(null, 'hello') as result");
|
|
829
|
+
await runner.run();
|
|
830
|
+
const results = runner.results;
|
|
831
|
+
expect(results.length).toBe(1);
|
|
832
|
+
expect(results[0]).toEqual({ result: null });
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test("Test stringify with null returns null", async () => {
|
|
836
|
+
const runner = new Runner("RETURN stringify(null) as result");
|
|
837
|
+
await runner.run();
|
|
838
|
+
const results = runner.results;
|
|
839
|
+
expect(results.length).toBe(1);
|
|
840
|
+
expect(results[0]).toEqual({ result: null });
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
test("Test toJson with null returns null", async () => {
|
|
844
|
+
const runner = new Runner("RETURN tojson(null) as result");
|
|
845
|
+
await runner.run();
|
|
846
|
+
const results = runner.results;
|
|
847
|
+
expect(results.length).toBe(1);
|
|
848
|
+
expect(results[0]).toEqual({ result: null });
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
test("Test range with null returns null", async () => {
|
|
852
|
+
const runner = new Runner("RETURN range(null, 5) as result");
|
|
853
|
+
await runner.run();
|
|
854
|
+
const results = runner.results;
|
|
855
|
+
expect(results.length).toBe(1);
|
|
856
|
+
expect(results[0]).toEqual({ result: null });
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
test("Test toString with null returns null", async () => {
|
|
860
|
+
const runner = new Runner("RETURN toString(null) as result");
|
|
861
|
+
await runner.run();
|
|
862
|
+
const results = runner.results;
|
|
863
|
+
expect(results.length).toBe(1);
|
|
864
|
+
expect(results[0]).toEqual({ result: null });
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
test("Test keys with null returns null", async () => {
|
|
868
|
+
const runner = new Runner("RETURN keys(null) as result");
|
|
869
|
+
await runner.run();
|
|
870
|
+
const results = runner.results;
|
|
871
|
+
expect(results.length).toBe(1);
|
|
872
|
+
expect(results[0]).toEqual({ result: null });
|
|
873
|
+
});
|
|
874
|
+
|
|
761
875
|
test("Test associative array with key which is keyword", async () => {
|
|
762
876
|
const runner = new Runner("RETURN {return: 1} as aa");
|
|
763
877
|
await runner.run();
|
|
@@ -4000,6 +4114,126 @@ test("Test order by with where", async () => {
|
|
|
4000
4114
|
expect(results[4]).toEqual({ x: 3 });
|
|
4001
4115
|
});
|
|
4002
4116
|
|
|
4117
|
+
test("Test order by with property access expression", async () => {
|
|
4118
|
+
const runner = new Runner(`
|
|
4119
|
+
unwind [{name: 'Charlie', age: 30}, {name: 'Alice', age: 25}, {name: 'Bob', age: 35}] as person
|
|
4120
|
+
return person.name as name, person.age as age
|
|
4121
|
+
order by person.name asc
|
|
4122
|
+
`);
|
|
4123
|
+
await runner.run();
|
|
4124
|
+
const results = runner.results;
|
|
4125
|
+
expect(results.length).toBe(3);
|
|
4126
|
+
expect(results[0]).toEqual({ name: "Alice", age: 25 });
|
|
4127
|
+
expect(results[1]).toEqual({ name: "Bob", age: 35 });
|
|
4128
|
+
expect(results[2]).toEqual({ name: "Charlie", age: 30 });
|
|
4129
|
+
});
|
|
4130
|
+
|
|
4131
|
+
test("Test order by with function expression", async () => {
|
|
4132
|
+
const runner = new Runner(`
|
|
4133
|
+
unwind ['BANANA', 'apple', 'Cherry'] as fruit
|
|
4134
|
+
return fruit
|
|
4135
|
+
order by toLower(fruit)
|
|
4136
|
+
`);
|
|
4137
|
+
await runner.run();
|
|
4138
|
+
const results = runner.results;
|
|
4139
|
+
expect(results.length).toBe(3);
|
|
4140
|
+
expect(results[0]).toEqual({ fruit: "apple" });
|
|
4141
|
+
expect(results[1]).toEqual({ fruit: "BANANA" });
|
|
4142
|
+
expect(results[2]).toEqual({ fruit: "Cherry" });
|
|
4143
|
+
});
|
|
4144
|
+
|
|
4145
|
+
test("Test order by with function expression descending", async () => {
|
|
4146
|
+
const runner = new Runner(`
|
|
4147
|
+
unwind ['BANANA', 'apple', 'Cherry'] as fruit
|
|
4148
|
+
return fruit
|
|
4149
|
+
order by toLower(fruit) desc
|
|
4150
|
+
`);
|
|
4151
|
+
await runner.run();
|
|
4152
|
+
const results = runner.results;
|
|
4153
|
+
expect(results.length).toBe(3);
|
|
4154
|
+
expect(results[0]).toEqual({ fruit: "Cherry" });
|
|
4155
|
+
expect(results[1]).toEqual({ fruit: "BANANA" });
|
|
4156
|
+
expect(results[2]).toEqual({ fruit: "apple" });
|
|
4157
|
+
});
|
|
4158
|
+
|
|
4159
|
+
test("Test order by with nested function expression", async () => {
|
|
4160
|
+
const runner = new Runner(`
|
|
4161
|
+
unwind ['Alice', 'Bob', 'ALICE', 'bob'] as name
|
|
4162
|
+
return name
|
|
4163
|
+
order by string_distance(toLower(name), toLower('alice')) asc
|
|
4164
|
+
`);
|
|
4165
|
+
await runner.run();
|
|
4166
|
+
const results = runner.results;
|
|
4167
|
+
expect(results.length).toBe(4);
|
|
4168
|
+
// 'Alice' and 'ALICE' have distance 0 from 'alice', should come first
|
|
4169
|
+
expect(results[0].name).toBe("Alice");
|
|
4170
|
+
expect(results[1].name).toBe("ALICE");
|
|
4171
|
+
// 'Bob' and 'bob' have higher distance from 'alice'
|
|
4172
|
+
expect(results[2].name).toBe("Bob");
|
|
4173
|
+
expect(results[3].name).toBe("bob");
|
|
4174
|
+
});
|
|
4175
|
+
|
|
4176
|
+
test("Test order by with arithmetic expression", async () => {
|
|
4177
|
+
const runner = new Runner(`
|
|
4178
|
+
unwind [{a: 3, b: 1}, {a: 1, b: 5}, {a: 2, b: 2}] as item
|
|
4179
|
+
return item.a as a, item.b as b
|
|
4180
|
+
order by item.a + item.b asc
|
|
4181
|
+
`);
|
|
4182
|
+
await runner.run();
|
|
4183
|
+
const results = runner.results;
|
|
4184
|
+
expect(results.length).toBe(3);
|
|
4185
|
+
expect(results[0]).toEqual({ a: 3, b: 1 }); // sum = 4
|
|
4186
|
+
expect(results[1]).toEqual({ a: 2, b: 2 }); // sum = 4
|
|
4187
|
+
expect(results[2]).toEqual({ a: 1, b: 5 }); // sum = 6
|
|
4188
|
+
});
|
|
4189
|
+
|
|
4190
|
+
test("Test order by expression does not leak synthetic keys", async () => {
|
|
4191
|
+
const runner = new Runner(`
|
|
4192
|
+
unwind ['B', 'a', 'C'] as x
|
|
4193
|
+
return x
|
|
4194
|
+
order by toLower(x) asc
|
|
4195
|
+
`);
|
|
4196
|
+
await runner.run();
|
|
4197
|
+
const results = runner.results;
|
|
4198
|
+
expect(results.length).toBe(3);
|
|
4199
|
+
// Results should only contain 'x', no __orderBy_ keys
|
|
4200
|
+
for (const r of results) {
|
|
4201
|
+
expect(Object.keys(r)).toEqual(["x"]);
|
|
4202
|
+
}
|
|
4203
|
+
expect(results[0]).toEqual({ x: "a" });
|
|
4204
|
+
expect(results[1]).toEqual({ x: "B" });
|
|
4205
|
+
expect(results[2]).toEqual({ x: "C" });
|
|
4206
|
+
});
|
|
4207
|
+
|
|
4208
|
+
test("Test order by with expression and limit", async () => {
|
|
4209
|
+
const runner = new Runner(`
|
|
4210
|
+
unwind ['BANANA', 'apple', 'Cherry', 'date', 'ELDERBERRY'] as fruit
|
|
4211
|
+
return fruit
|
|
4212
|
+
order by toLower(fruit) asc
|
|
4213
|
+
limit 3
|
|
4214
|
+
`);
|
|
4215
|
+
await runner.run();
|
|
4216
|
+
const results = runner.results;
|
|
4217
|
+
expect(results.length).toBe(3);
|
|
4218
|
+
expect(results[0]).toEqual({ fruit: "apple" });
|
|
4219
|
+
expect(results[1]).toEqual({ fruit: "BANANA" });
|
|
4220
|
+
expect(results[2]).toEqual({ fruit: "Cherry" });
|
|
4221
|
+
});
|
|
4222
|
+
|
|
4223
|
+
test("Test order by with mixed simple and expression fields", async () => {
|
|
4224
|
+
const runner = new Runner(`
|
|
4225
|
+
unwind [{name: 'Alice', score: 3}, {name: 'Alice', score: 1}, {name: 'Bob', score: 2}] as item
|
|
4226
|
+
return item.name as name, item.score as score
|
|
4227
|
+
order by name asc, item.score desc
|
|
4228
|
+
`);
|
|
4229
|
+
await runner.run();
|
|
4230
|
+
const results = runner.results;
|
|
4231
|
+
expect(results.length).toBe(3);
|
|
4232
|
+
expect(results[0]).toEqual({ name: "Alice", score: 3 }); // Alice, score 3 desc
|
|
4233
|
+
expect(results[1]).toEqual({ name: "Alice", score: 1 }); // Alice, score 1 desc
|
|
4234
|
+
expect(results[2]).toEqual({ name: "Bob", score: 2 }); // Bob
|
|
4235
|
+
});
|
|
4236
|
+
|
|
4003
4237
|
test("Test delete virtual node operation", async () => {
|
|
4004
4238
|
const db = Database.getInstance();
|
|
4005
4239
|
// Create a virtual node first
|
|
@@ -1269,3 +1269,59 @@ test("OPTIONAL without MATCH throws error", () => {
|
|
|
1269
1269
|
const parser = new Parser();
|
|
1270
1270
|
expect(() => parser.parse("OPTIONAL RETURN 1")).toThrow("Expected MATCH after OPTIONAL");
|
|
1271
1271
|
});
|
|
1272
|
+
|
|
1273
|
+
// ORDER BY expression tests
|
|
1274
|
+
|
|
1275
|
+
test("ORDER BY with simple identifier parses correctly", () => {
|
|
1276
|
+
const parser = new Parser();
|
|
1277
|
+
const ast = parser.parse("unwind [1, 2] as x return x order by x");
|
|
1278
|
+
expect(ast).toBeDefined();
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
test("ORDER BY with property access parses correctly", () => {
|
|
1282
|
+
const parser = new Parser();
|
|
1283
|
+
const ast = parser.parse(
|
|
1284
|
+
"unwind [{name: 'Bob'}, {name: 'Alice'}] as person return person.name as name order by person.name asc"
|
|
1285
|
+
);
|
|
1286
|
+
expect(ast).toBeDefined();
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
test("ORDER BY with function call parses correctly", () => {
|
|
1290
|
+
const parser = new Parser();
|
|
1291
|
+
const ast = parser.parse(
|
|
1292
|
+
"unwind ['HELLO', 'WORLD'] as word return word order by toLower(word) asc"
|
|
1293
|
+
);
|
|
1294
|
+
expect(ast).toBeDefined();
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
test("ORDER BY with nested function calls parses correctly", () => {
|
|
1298
|
+
const parser = new Parser();
|
|
1299
|
+
const ast = parser.parse(
|
|
1300
|
+
"unwind ['Alice', 'Bob'] as name return name order by string_distance(toLower(name), toLower('alice')) asc"
|
|
1301
|
+
);
|
|
1302
|
+
expect(ast).toBeDefined();
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
test("ORDER BY with arithmetic expression parses correctly", () => {
|
|
1306
|
+
const parser = new Parser();
|
|
1307
|
+
const ast = parser.parse(
|
|
1308
|
+
"unwind [{a: 3, b: 1}, {a: 1, b: 5}] as item return item.a as a, item.b as b order by item.a + item.b desc"
|
|
1309
|
+
);
|
|
1310
|
+
expect(ast).toBeDefined();
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
test("ORDER BY with multiple expression fields parses correctly", () => {
|
|
1314
|
+
const parser = new Parser();
|
|
1315
|
+
const ast = parser.parse(
|
|
1316
|
+
"unwind [{a: 1, b: 2}] as item return item.a as a, item.b as b order by toLower(item.a) asc, item.b desc"
|
|
1317
|
+
);
|
|
1318
|
+
expect(ast).toBeDefined();
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
test("ORDER BY with expression and LIMIT parses correctly", () => {
|
|
1322
|
+
const parser = new Parser();
|
|
1323
|
+
const ast = parser.parse(
|
|
1324
|
+
"unwind ['c', 'a', 'b'] as x return x order by toLower(x) asc limit 2"
|
|
1325
|
+
);
|
|
1326
|
+
expect(ast).toBeDefined();
|
|
1327
|
+
});
|