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.
Files changed (94) hide show
  1. package/dist/flowquery.min.js +1 -1
  2. package/dist/parsing/functions/join.d.ts.map +1 -1
  3. package/dist/parsing/functions/join.js +6 -3
  4. package/dist/parsing/functions/join.js.map +1 -1
  5. package/dist/parsing/functions/keys.d.ts.map +1 -1
  6. package/dist/parsing/functions/keys.js +3 -5
  7. package/dist/parsing/functions/keys.js.map +1 -1
  8. package/dist/parsing/functions/range.d.ts.map +1 -1
  9. package/dist/parsing/functions/range.js +11 -3
  10. package/dist/parsing/functions/range.js.map +1 -1
  11. package/dist/parsing/functions/replace.d.ts.map +1 -1
  12. package/dist/parsing/functions/replace.js +8 -3
  13. package/dist/parsing/functions/replace.js.map +1 -1
  14. package/dist/parsing/functions/round.d.ts.map +1 -1
  15. package/dist/parsing/functions/round.js +5 -4
  16. package/dist/parsing/functions/round.js.map +1 -1
  17. package/dist/parsing/functions/size.d.ts.map +1 -1
  18. package/dist/parsing/functions/size.js +5 -4
  19. package/dist/parsing/functions/size.js.map +1 -1
  20. package/dist/parsing/functions/split.d.ts.map +1 -1
  21. package/dist/parsing/functions/split.js +12 -4
  22. package/dist/parsing/functions/split.js.map +1 -1
  23. package/dist/parsing/functions/string_distance.d.ts.map +1 -1
  24. package/dist/parsing/functions/string_distance.js +3 -0
  25. package/dist/parsing/functions/string_distance.js.map +1 -1
  26. package/dist/parsing/functions/stringify.d.ts.map +1 -1
  27. package/dist/parsing/functions/stringify.js +7 -6
  28. package/dist/parsing/functions/stringify.js.map +1 -1
  29. package/dist/parsing/functions/substring.d.ts.map +1 -1
  30. package/dist/parsing/functions/substring.js +3 -0
  31. package/dist/parsing/functions/substring.js.map +1 -1
  32. package/dist/parsing/functions/to_json.d.ts.map +1 -1
  33. package/dist/parsing/functions/to_json.js +5 -4
  34. package/dist/parsing/functions/to_json.js.map +1 -1
  35. package/dist/parsing/functions/to_lower.d.ts.map +1 -1
  36. package/dist/parsing/functions/to_lower.js +3 -0
  37. package/dist/parsing/functions/to_lower.js.map +1 -1
  38. package/dist/parsing/functions/to_string.js +1 -1
  39. package/dist/parsing/functions/to_string.js.map +1 -1
  40. package/dist/parsing/functions/trim.d.ts.map +1 -1
  41. package/dist/parsing/functions/trim.js +3 -0
  42. package/dist/parsing/functions/trim.js.map +1 -1
  43. package/dist/parsing/operations/order_by.d.ts +22 -2
  44. package/dist/parsing/operations/order_by.d.ts.map +1 -1
  45. package/dist/parsing/operations/order_by.js +54 -6
  46. package/dist/parsing/operations/order_by.js.map +1 -1
  47. package/dist/parsing/operations/return.d.ts.map +1 -1
  48. package/dist/parsing/operations/return.js +4 -0
  49. package/dist/parsing/operations/return.js.map +1 -1
  50. package/dist/parsing/parser.d.ts.map +1 -1
  51. package/dist/parsing/parser.js +4 -5
  52. package/dist/parsing/parser.js.map +1 -1
  53. package/docs/flowquery.min.js +1 -1
  54. package/flowquery-py/pyproject.toml +1 -1
  55. package/flowquery-py/src/parsing/functions/join.py +2 -0
  56. package/flowquery-py/src/parsing/functions/keys.py +1 -1
  57. package/flowquery-py/src/parsing/functions/range_.py +2 -0
  58. package/flowquery-py/src/parsing/functions/replace.py +2 -0
  59. package/flowquery-py/src/parsing/functions/round_.py +2 -0
  60. package/flowquery-py/src/parsing/functions/size.py +2 -0
  61. package/flowquery-py/src/parsing/functions/split.py +2 -0
  62. package/flowquery-py/src/parsing/functions/string_distance.py +5 -1
  63. package/flowquery-py/src/parsing/functions/stringify.py +2 -0
  64. package/flowquery-py/src/parsing/functions/substring.py +2 -0
  65. package/flowquery-py/src/parsing/functions/to_json.py +2 -0
  66. package/flowquery-py/src/parsing/functions/to_lower.py +2 -0
  67. package/flowquery-py/src/parsing/functions/to_string.py +1 -1
  68. package/flowquery-py/src/parsing/functions/trim.py +2 -0
  69. package/flowquery-py/src/parsing/operations/order_by.py +55 -13
  70. package/flowquery-py/src/parsing/operations/return_op.py +3 -0
  71. package/flowquery-py/src/parsing/parser.py +4 -5
  72. package/flowquery-py/tests/compute/test_runner.py +255 -0
  73. package/flowquery-py/tests/parsing/test_parser.py +63 -0
  74. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  75. package/package.json +1 -1
  76. package/src/parsing/functions/join.ts +8 -5
  77. package/src/parsing/functions/keys.ts +4 -6
  78. package/src/parsing/functions/range.ts +12 -4
  79. package/src/parsing/functions/replace.ts +11 -4
  80. package/src/parsing/functions/round.ts +6 -5
  81. package/src/parsing/functions/size.ts +6 -5
  82. package/src/parsing/functions/split.ts +14 -6
  83. package/src/parsing/functions/string_distance.ts +3 -0
  84. package/src/parsing/functions/stringify.ts +9 -8
  85. package/src/parsing/functions/substring.ts +3 -0
  86. package/src/parsing/functions/to_json.ts +6 -5
  87. package/src/parsing/functions/to_lower.ts +3 -0
  88. package/src/parsing/functions/to_string.ts +1 -1
  89. package/src/parsing/functions/trim.ts +3 -0
  90. package/src/parsing/operations/order_by.ts +58 -7
  91. package/src/parsing/operations/return.ts +4 -0
  92. package/src/parsing/parser.ts +4 -5
  93. package/tests/compute/runner.test.ts +234 -0
  94. 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
+ });