flowquery 1.0.18 → 1.0.21

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 (158) hide show
  1. package/.gitattributes +3 -0
  2. package/.github/workflows/python-publish.yml +56 -4
  3. package/.github/workflows/release.yml +26 -19
  4. package/.husky/pre-commit +26 -0
  5. package/README.md +37 -32
  6. package/dist/flowquery.min.js +1 -1
  7. package/dist/graph/data.d.ts +5 -4
  8. package/dist/graph/data.d.ts.map +1 -1
  9. package/dist/graph/data.js +38 -20
  10. package/dist/graph/data.js.map +1 -1
  11. package/dist/graph/node.d.ts +2 -0
  12. package/dist/graph/node.d.ts.map +1 -1
  13. package/dist/graph/node.js +23 -0
  14. package/dist/graph/node.js.map +1 -1
  15. package/dist/graph/node_data.js +1 -1
  16. package/dist/graph/node_data.js.map +1 -1
  17. package/dist/graph/pattern.d.ts.map +1 -1
  18. package/dist/graph/pattern.js +11 -4
  19. package/dist/graph/pattern.js.map +1 -1
  20. package/dist/graph/relationship.d.ts +6 -1
  21. package/dist/graph/relationship.d.ts.map +1 -1
  22. package/dist/graph/relationship.js +43 -5
  23. package/dist/graph/relationship.js.map +1 -1
  24. package/dist/graph/relationship_data.d.ts +2 -0
  25. package/dist/graph/relationship_data.d.ts.map +1 -1
  26. package/dist/graph/relationship_data.js +8 -1
  27. package/dist/graph/relationship_data.js.map +1 -1
  28. package/dist/graph/relationship_match_collector.js +2 -2
  29. package/dist/graph/relationship_match_collector.js.map +1 -1
  30. package/dist/graph/relationship_reference.d.ts.map +1 -1
  31. package/dist/graph/relationship_reference.js +2 -1
  32. package/dist/graph/relationship_reference.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/parsing/parser.d.ts +6 -0
  36. package/dist/parsing/parser.d.ts.map +1 -1
  37. package/dist/parsing/parser.js +139 -72
  38. package/dist/parsing/parser.js.map +1 -1
  39. package/docs/flowquery.min.js +1 -1
  40. package/flowquery-py/misc/data/test.json +10 -0
  41. package/flowquery-py/misc/data/users.json +242 -0
  42. package/flowquery-py/notebooks/TestFlowQuery.ipynb +440 -0
  43. package/flowquery-py/pyproject.toml +48 -2
  44. package/flowquery-py/src/__init__.py +7 -5
  45. package/flowquery-py/src/compute/runner.py +14 -10
  46. package/flowquery-py/src/extensibility.py +8 -8
  47. package/flowquery-py/src/graph/__init__.py +7 -7
  48. package/flowquery-py/src/graph/data.py +38 -20
  49. package/flowquery-py/src/graph/database.py +10 -20
  50. package/flowquery-py/src/graph/node.py +50 -19
  51. package/flowquery-py/src/graph/node_data.py +1 -1
  52. package/flowquery-py/src/graph/node_reference.py +10 -11
  53. package/flowquery-py/src/graph/pattern.py +27 -37
  54. package/flowquery-py/src/graph/pattern_expression.py +13 -11
  55. package/flowquery-py/src/graph/patterns.py +2 -2
  56. package/flowquery-py/src/graph/physical_node.py +4 -3
  57. package/flowquery-py/src/graph/physical_relationship.py +5 -5
  58. package/flowquery-py/src/graph/relationship.py +62 -14
  59. package/flowquery-py/src/graph/relationship_data.py +7 -2
  60. package/flowquery-py/src/graph/relationship_match_collector.py +15 -10
  61. package/flowquery-py/src/graph/relationship_reference.py +4 -4
  62. package/flowquery-py/src/io/command_line.py +13 -14
  63. package/flowquery-py/src/parsing/__init__.py +2 -2
  64. package/flowquery-py/src/parsing/alias_option.py +1 -1
  65. package/flowquery-py/src/parsing/ast_node.py +21 -20
  66. package/flowquery-py/src/parsing/base_parser.py +7 -7
  67. package/flowquery-py/src/parsing/components/__init__.py +3 -3
  68. package/flowquery-py/src/parsing/components/from_.py +3 -1
  69. package/flowquery-py/src/parsing/components/headers.py +2 -2
  70. package/flowquery-py/src/parsing/components/null.py +2 -2
  71. package/flowquery-py/src/parsing/context.py +7 -7
  72. package/flowquery-py/src/parsing/data_structures/associative_array.py +7 -7
  73. package/flowquery-py/src/parsing/data_structures/json_array.py +3 -3
  74. package/flowquery-py/src/parsing/data_structures/key_value_pair.py +4 -4
  75. package/flowquery-py/src/parsing/data_structures/lookup.py +2 -2
  76. package/flowquery-py/src/parsing/data_structures/range_lookup.py +2 -2
  77. package/flowquery-py/src/parsing/expressions/__init__.py +16 -16
  78. package/flowquery-py/src/parsing/expressions/expression.py +16 -13
  79. package/flowquery-py/src/parsing/expressions/expression_map.py +9 -9
  80. package/flowquery-py/src/parsing/expressions/f_string.py +3 -3
  81. package/flowquery-py/src/parsing/expressions/identifier.py +4 -3
  82. package/flowquery-py/src/parsing/expressions/number.py +3 -3
  83. package/flowquery-py/src/parsing/expressions/operator.py +16 -16
  84. package/flowquery-py/src/parsing/expressions/reference.py +3 -3
  85. package/flowquery-py/src/parsing/expressions/string.py +2 -2
  86. package/flowquery-py/src/parsing/functions/__init__.py +17 -17
  87. package/flowquery-py/src/parsing/functions/aggregate_function.py +8 -8
  88. package/flowquery-py/src/parsing/functions/async_function.py +12 -9
  89. package/flowquery-py/src/parsing/functions/avg.py +4 -4
  90. package/flowquery-py/src/parsing/functions/collect.py +6 -6
  91. package/flowquery-py/src/parsing/functions/function.py +6 -6
  92. package/flowquery-py/src/parsing/functions/function_factory.py +31 -34
  93. package/flowquery-py/src/parsing/functions/function_metadata.py +10 -11
  94. package/flowquery-py/src/parsing/functions/functions.py +14 -6
  95. package/flowquery-py/src/parsing/functions/join.py +3 -3
  96. package/flowquery-py/src/parsing/functions/keys.py +3 -3
  97. package/flowquery-py/src/parsing/functions/predicate_function.py +8 -7
  98. package/flowquery-py/src/parsing/functions/predicate_sum.py +12 -7
  99. package/flowquery-py/src/parsing/functions/rand.py +2 -2
  100. package/flowquery-py/src/parsing/functions/range_.py +9 -4
  101. package/flowquery-py/src/parsing/functions/replace.py +2 -2
  102. package/flowquery-py/src/parsing/functions/round_.py +2 -2
  103. package/flowquery-py/src/parsing/functions/size.py +2 -2
  104. package/flowquery-py/src/parsing/functions/split.py +9 -4
  105. package/flowquery-py/src/parsing/functions/stringify.py +3 -3
  106. package/flowquery-py/src/parsing/functions/sum.py +4 -4
  107. package/flowquery-py/src/parsing/functions/to_json.py +2 -2
  108. package/flowquery-py/src/parsing/functions/type_.py +3 -3
  109. package/flowquery-py/src/parsing/functions/value_holder.py +1 -1
  110. package/flowquery-py/src/parsing/logic/__init__.py +2 -2
  111. package/flowquery-py/src/parsing/logic/case.py +0 -1
  112. package/flowquery-py/src/parsing/logic/when.py +3 -1
  113. package/flowquery-py/src/parsing/operations/__init__.py +10 -10
  114. package/flowquery-py/src/parsing/operations/aggregated_return.py +3 -5
  115. package/flowquery-py/src/parsing/operations/aggregated_with.py +4 -4
  116. package/flowquery-py/src/parsing/operations/call.py +6 -7
  117. package/flowquery-py/src/parsing/operations/create_node.py +5 -4
  118. package/flowquery-py/src/parsing/operations/create_relationship.py +5 -4
  119. package/flowquery-py/src/parsing/operations/group_by.py +18 -16
  120. package/flowquery-py/src/parsing/operations/load.py +21 -19
  121. package/flowquery-py/src/parsing/operations/match.py +8 -7
  122. package/flowquery-py/src/parsing/operations/operation.py +3 -3
  123. package/flowquery-py/src/parsing/operations/projection.py +6 -6
  124. package/flowquery-py/src/parsing/operations/return_op.py +9 -5
  125. package/flowquery-py/src/parsing/operations/unwind.py +3 -2
  126. package/flowquery-py/src/parsing/operations/where.py +9 -7
  127. package/flowquery-py/src/parsing/operations/with_op.py +2 -2
  128. package/flowquery-py/src/parsing/parser.py +178 -114
  129. package/flowquery-py/src/parsing/token_to_node.py +2 -2
  130. package/flowquery-py/src/tokenization/__init__.py +4 -4
  131. package/flowquery-py/src/tokenization/keyword.py +1 -1
  132. package/flowquery-py/src/tokenization/operator.py +1 -1
  133. package/flowquery-py/src/tokenization/string_walker.py +4 -4
  134. package/flowquery-py/src/tokenization/symbol.py +1 -1
  135. package/flowquery-py/src/tokenization/token.py +11 -11
  136. package/flowquery-py/src/tokenization/token_mapper.py +10 -9
  137. package/flowquery-py/src/tokenization/token_type.py +1 -1
  138. package/flowquery-py/src/tokenization/tokenizer.py +19 -19
  139. package/flowquery-py/src/tokenization/trie.py +18 -17
  140. package/flowquery-py/src/utils/__init__.py +1 -1
  141. package/flowquery-py/src/utils/object_utils.py +3 -3
  142. package/flowquery-py/src/utils/string_utils.py +12 -12
  143. package/flowquery-py/tests/compute/test_runner.py +214 -7
  144. package/flowquery-py/tests/parsing/test_parser.py +41 -0
  145. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  146. package/package.json +1 -1
  147. package/src/graph/data.ts +38 -20
  148. package/src/graph/node.ts +23 -0
  149. package/src/graph/node_data.ts +1 -1
  150. package/src/graph/pattern.ts +13 -4
  151. package/src/graph/relationship.ts +45 -5
  152. package/src/graph/relationship_data.ts +8 -1
  153. package/src/graph/relationship_match_collector.ts +1 -1
  154. package/src/graph/relationship_reference.ts +2 -1
  155. package/src/index.ts +5 -5
  156. package/src/parsing/parser.ts +139 -71
  157. package/tests/compute/runner.test.ts +249 -79
  158. package/tests/parsing/parser.test.ts +32 -0
@@ -0,0 +1,10 @@
1
+ [
2
+ {
3
+ "test": "This is a test.",
4
+ "description": "A simple JSON file for testing."
5
+ },
6
+ {
7
+ "test": "Another test entry.",
8
+ "description": "Another simple JSON object."
9
+ }
10
+ ]
@@ -0,0 +1,242 @@
1
+ [
2
+ {
3
+ "id": 1,
4
+ "name": "Victoria Harrington",
5
+ "title": "Managing Partner",
6
+ "department": "Executive",
7
+ "email": "v.harrington@blackstone-legal.com",
8
+ "managerId": null
9
+ },
10
+ {
11
+ "id": 2,
12
+ "name": "Charles Whitmore",
13
+ "title": "Senior Partner",
14
+ "department": "Corporate Law",
15
+ "email": "c.whitmore@blackstone-legal.com",
16
+ "managerId": 1
17
+ },
18
+ {
19
+ "id": 3,
20
+ "name": "Eleanor Prescott",
21
+ "title": "Senior Partner",
22
+ "department": "Litigation",
23
+ "email": "e.prescott@blackstone-legal.com",
24
+ "managerId": 1
25
+ },
26
+ {
27
+ "id": 4,
28
+ "name": "Marcus Delgado",
29
+ "title": "Senior Partner",
30
+ "department": "Intellectual Property",
31
+ "email": "m.delgado@blackstone-legal.com",
32
+ "managerId": 1
33
+ },
34
+ {
35
+ "id": 5,
36
+ "name": "Rebecca Thornton",
37
+ "title": "Partner",
38
+ "department": "Corporate Law",
39
+ "email": "r.thornton@blackstone-legal.com",
40
+ "managerId": 2
41
+ },
42
+ {
43
+ "id": 6,
44
+ "name": "Jonathan Ashford",
45
+ "title": "Partner",
46
+ "department": "Corporate Law",
47
+ "email": "j.ashford@blackstone-legal.com",
48
+ "managerId": 2
49
+ },
50
+ {
51
+ "id": 7,
52
+ "name": "Diana Castellanos",
53
+ "title": "Partner",
54
+ "department": "Litigation",
55
+ "email": "d.castellanos@blackstone-legal.com",
56
+ "managerId": 3
57
+ },
58
+ {
59
+ "id": 8,
60
+ "name": "William Cho",
61
+ "title": "Partner",
62
+ "department": "Litigation",
63
+ "email": "w.cho@blackstone-legal.com",
64
+ "managerId": 3
65
+ },
66
+ {
67
+ "id": 9,
68
+ "name": "Natasha Okonkwo",
69
+ "title": "Partner",
70
+ "department": "Intellectual Property",
71
+ "email": "n.okonkwo@blackstone-legal.com",
72
+ "managerId": 4
73
+ },
74
+ {
75
+ "id": 10,
76
+ "name": "Samuel Brennan",
77
+ "title": "Senior Associate",
78
+ "department": "Corporate Law",
79
+ "email": "s.brennan@blackstone-legal.com",
80
+ "managerId": 5
81
+ },
82
+ {
83
+ "id": 11,
84
+ "name": "Priya Sharma",
85
+ "title": "Senior Associate",
86
+ "department": "Corporate Law",
87
+ "email": "p.sharma@blackstone-legal.com",
88
+ "managerId": 6
89
+ },
90
+ {
91
+ "id": 12,
92
+ "name": "Michael Torres",
93
+ "title": "Senior Associate",
94
+ "department": "Litigation",
95
+ "email": "m.torres@blackstone-legal.com",
96
+ "managerId": 7
97
+ },
98
+ {
99
+ "id": 13,
100
+ "name": "Katherine Webb",
101
+ "title": "Senior Associate",
102
+ "department": "Litigation",
103
+ "email": "k.webb@blackstone-legal.com",
104
+ "managerId": 8
105
+ },
106
+ {
107
+ "id": 14,
108
+ "name": "David Kim",
109
+ "title": "Senior Associate",
110
+ "department": "Intellectual Property",
111
+ "email": "d.kim@blackstone-legal.com",
112
+ "managerId": 9
113
+ },
114
+ {
115
+ "id": 15,
116
+ "name": "Lauren Mitchell",
117
+ "title": "Associate",
118
+ "department": "Corporate Law",
119
+ "email": "l.mitchell@blackstone-legal.com",
120
+ "managerId": 10
121
+ },
122
+ {
123
+ "id": 16,
124
+ "name": "Ryan Fitzgerald",
125
+ "title": "Associate",
126
+ "department": "Corporate Law",
127
+ "email": "r.fitzgerald@blackstone-legal.com",
128
+ "managerId": 10
129
+ },
130
+ {
131
+ "id": 17,
132
+ "name": "Olivia Chen",
133
+ "title": "Associate",
134
+ "department": "Corporate Law",
135
+ "email": "o.chen@blackstone-legal.com",
136
+ "managerId": 11
137
+ },
138
+ {
139
+ "id": 18,
140
+ "name": "James Patterson",
141
+ "title": "Associate",
142
+ "department": "Litigation",
143
+ "email": "j.patterson@blackstone-legal.com",
144
+ "managerId": 12
145
+ },
146
+ {
147
+ "id": 19,
148
+ "name": "Sophia Rodriguez",
149
+ "title": "Associate",
150
+ "department": "Litigation",
151
+ "email": "s.rodriguez@blackstone-legal.com",
152
+ "managerId": 13
153
+ },
154
+ {
155
+ "id": 20,
156
+ "name": "Benjamin Hayes",
157
+ "title": "Associate",
158
+ "department": "Intellectual Property",
159
+ "email": "b.hayes@blackstone-legal.com",
160
+ "managerId": 14
161
+ },
162
+ {
163
+ "id": 21,
164
+ "name": "Emily Nakamura",
165
+ "title": "Associate",
166
+ "department": "Intellectual Property",
167
+ "email": "e.nakamura@blackstone-legal.com",
168
+ "managerId": 14
169
+ },
170
+ {
171
+ "id": 22,
172
+ "name": "Thomas Grant",
173
+ "title": "Paralegal",
174
+ "department": "Corporate Law",
175
+ "email": "t.grant@blackstone-legal.com",
176
+ "managerId": 15
177
+ },
178
+ {
179
+ "id": 23,
180
+ "name": "Amanda Foster",
181
+ "title": "Paralegal",
182
+ "department": "Litigation",
183
+ "email": "a.foster@blackstone-legal.com",
184
+ "managerId": 18
185
+ },
186
+ {
187
+ "id": 24,
188
+ "name": "Daniel Reyes",
189
+ "title": "Paralegal",
190
+ "department": "Intellectual Property",
191
+ "email": "d.reyes@blackstone-legal.com",
192
+ "managerId": 20
193
+ },
194
+ {
195
+ "id": 25,
196
+ "name": "Rachel Morgan",
197
+ "title": "Legal Secretary",
198
+ "department": "Executive",
199
+ "email": "r.morgan@blackstone-legal.com",
200
+ "managerId": 1
201
+ },
202
+ {
203
+ "id": 26,
204
+ "name": "Christopher Blake",
205
+ "title": "Legal Secretary",
206
+ "department": "Corporate Law",
207
+ "email": "c.blake@blackstone-legal.com",
208
+ "managerId": 5
209
+ },
210
+ {
211
+ "id": 27,
212
+ "name": "Jessica Huang",
213
+ "title": "Legal Secretary",
214
+ "department": "Litigation",
215
+ "email": "j.huang@blackstone-legal.com",
216
+ "managerId": 7
217
+ },
218
+ {
219
+ "id": 28,
220
+ "name": "Andrew Sullivan",
221
+ "title": "Office Manager",
222
+ "department": "Operations",
223
+ "email": "a.sullivan@blackstone-legal.com",
224
+ "managerId": 1
225
+ },
226
+ {
227
+ "id": 29,
228
+ "name": "Michelle Davis",
229
+ "title": "Receptionist",
230
+ "department": "Operations",
231
+ "email": "m.davis@blackstone-legal.com",
232
+ "managerId": 28
233
+ },
234
+ {
235
+ "id": 30,
236
+ "name": "Robert Chen",
237
+ "title": "IT Administrator",
238
+ "department": "Operations",
239
+ "email": "r.chen@blackstone-legal.com",
240
+ "managerId": 28
241
+ }
242
+ ]
@@ -0,0 +1,440 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "0",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "%reload_ext autoreload\n",
11
+ "%autoreload 2\n",
12
+ "\n",
13
+ "from flowquery import Runner"
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "markdown",
18
+ "id": "1",
19
+ "metadata": {},
20
+ "source": [
21
+ "Create virtual node test"
22
+ ]
23
+ },
24
+ {
25
+ "cell_type": "code",
26
+ "execution_count": null,
27
+ "id": "2",
28
+ "metadata": {},
29
+ "outputs": [],
30
+ "source": [
31
+ "query: str = \"\"\"\n",
32
+ " create virtual (:Fact) as {\n",
33
+ " unwind range(0,6) as i\n",
34
+ " load json from \"https://catfact.ninja/fact\" as item\n",
35
+ " return i as id, item.fact\n",
36
+ " }\n",
37
+ "\"\"\"\n",
38
+ "\n",
39
+ "runner: Runner = Runner(query)\n",
40
+ "await runner.run()"
41
+ ]
42
+ },
43
+ {
44
+ "cell_type": "markdown",
45
+ "id": "3",
46
+ "metadata": {},
47
+ "source": [
48
+ "Query virtual graph node"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": null,
54
+ "id": "4",
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "runner: Runner = Runner(\"match (n:Fact) return n\")\n",
59
+ "await runner.run()\n",
60
+ "for record in runner.results:\n",
61
+ " print(record)"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "markdown",
66
+ "id": "5",
67
+ "metadata": {},
68
+ "source": [
69
+ "Test extensibility"
70
+ ]
71
+ },
72
+ {
73
+ "cell_type": "code",
74
+ "execution_count": null,
75
+ "id": "6",
76
+ "metadata": {},
77
+ "outputs": [],
78
+ "source": [
79
+ "from flowquery.extensibility import (\n",
80
+ " Function,\n",
81
+ " FunctionDef,\n",
82
+ " AggregateFunction,\n",
83
+ " ReducerElement,\n",
84
+ " AsyncFunction,\n",
85
+ " PredicateFunction\n",
86
+ ")\n",
87
+ "import aiohttp\n",
88
+ "import json\n",
89
+ "from typing import Any, List, Iterator, Union, Dict\n",
90
+ "\n",
91
+ "@FunctionDef({\n",
92
+ " \"description\": \"Converts a string to uppercase\",\n",
93
+ " \"category\": \"string\",\n",
94
+ " \"parameters\": [\n",
95
+ " {\"name\": \"text\", \"description\": \"String to convert\", \"type\": \"string\"}\n",
96
+ " ],\n",
97
+ " \"output\": {\"description\": \"Uppercase string\", \"type\": \"string\"}\n",
98
+ "})\n",
99
+ "class UpperCase(Function):\n",
100
+ " def __init__(self):\n",
101
+ " super().__init__(\"uppercase\")\n",
102
+ " self._expected_parameter_count = 1\n",
103
+ "\n",
104
+ " def value(self) -> str:\n",
105
+ " return str(self.get_children()[0].value()).upper()\n",
106
+ " \n",
107
+ "@FunctionDef({\n",
108
+ " \"description\": \"Extracts nodes from a collection\",\n",
109
+ " \"category\": \"scalar\",\n",
110
+ " \"parameters\": [\n",
111
+ " {\"name\": \"collection\", \"description\": \"Collection to extract nodes from\", \"type\": \"any[]\"}\n",
112
+ " ],\n",
113
+ " \"output\": {\"description\": \"List of nodes extracted from the collection\", \"type\": \"node[]\"}\n",
114
+ "})\n",
115
+ "class Nodes(Function):\n",
116
+ " def __init__(self):\n",
117
+ " super().__init__(\"nodes\")\n",
118
+ " self._expected_parameter_count = 1\n",
119
+ "\n",
120
+ " def value(self) -> List[Dict[str, Any]]:\n",
121
+ " pattern: List[Dict[str, Any]] = self.get_children()[0].value()\n",
122
+ " return list(self._nodes(pattern))\n",
123
+ "\n",
124
+ " def _nodes(self, pattern: List[Dict[str, Any]]) -> Iterator[Dict[str, Any]]:\n",
125
+ " for element in pattern:\n",
126
+ " if isinstance(element, dict) and \"id\" in element:\n",
127
+ " yield element\n",
128
+ " \n",
129
+ "\n",
130
+ "class ProductElement(ReducerElement):\n",
131
+ " def __init__(self):\n",
132
+ " self._value: float = 1.0\n",
133
+ "\n",
134
+ " @property\n",
135
+ " def value(self) -> float:\n",
136
+ " return self._value\n",
137
+ "\n",
138
+ " @value.setter\n",
139
+ " def value(self, v: float) -> None:\n",
140
+ " self._value = v\n",
141
+ " \n",
142
+ "@FunctionDef({\n",
143
+ " \"description\": \"Calculates the product of a list of numbers\",\n",
144
+ " \"category\": \"aggregate\",\n",
145
+ " \"parameters\": [\n",
146
+ " {\"name\": \"numbers\", \"description\": \"List of numbers to multiply\", \"type\": \"number[]\"}\n",
147
+ " ],\n",
148
+ " \"output\": {\"description\": \"Product of the numbers\", \"type\": \"number\"}\n",
149
+ "})\n",
150
+ "class Product(AggregateFunction):\n",
151
+ " def __init__(self):\n",
152
+ " super().__init__(\"product\")\n",
153
+ "\n",
154
+ " def reduce(self, element: ReducerElement) -> None:\n",
155
+ " element.value *= self.first_child().value()\n",
156
+ "\n",
157
+ " def element(self) -> ReducerElement:\n",
158
+ " return ProductElement()\n",
159
+ "\n",
160
+ "@FunctionDef({\n",
161
+ " \"description\": \"Asynchronous function that fetches data from a URL\",\n",
162
+ " \"category\": \"async\",\n",
163
+ " \"parameters\": [\n",
164
+ " {\"name\": \"url\", \"description\": \"URL to fetch data from\", \"type\": \"string\"}\n",
165
+ " ],\n",
166
+ " \"output\": {\"description\": \"Fetched data\", \"type\": \"string\"}\n",
167
+ "})\n",
168
+ "class get(AsyncFunction):\n",
169
+ " async def generate(self, url: str):\n",
170
+ " async with aiohttp.ClientSession() as session:\n",
171
+ " async with session.get(url) as response:\n",
172
+ " yield await response.json()\n",
173
+ "\n",
174
+ "@FunctionDef({\n",
175
+ " \"description\": \"Fetch json data from a file path\",\n",
176
+ " \"category\": \"async\",\n",
177
+ " \"parameters\": [\n",
178
+ " {\"name\": \"path\", \"description\": \"File path to fetch data from\", \"type\": \"string\"}\n",
179
+ " ],\n",
180
+ " \"output\": {\"description\": \"Fetched data\", \"type\": \"string\"}\n",
181
+ "})\n",
182
+ "class json_file(AsyncFunction):\n",
183
+ " async def generate(self, path: str):\n",
184
+ " with open(path, \"r\") as file:\n",
185
+ " yield json.load(file)\n",
186
+ "\n",
187
+ "@FunctionDef({\n",
188
+ " \"description\": \"Extracts values from an array with optional filtering. Uses list comprehension syntax: extract(variable IN array [WHERE condition] | expression)\",\n",
189
+ " \"category\": \"predicate\",\n",
190
+ " \"parameters\": [\n",
191
+ " {\"name\": \"variable\", \"description\": \"Variable name to bind each element\", \"type\": \"string\"},\n",
192
+ " {\"name\": \"array\", \"description\": \"Array to iterate over\", \"type\": \"array\"},\n",
193
+ " {\"name\": \"expression\", \"description\": \"Expression to return for each element\", \"type\": \"any\"},\n",
194
+ " {\"name\": \"where\", \"description\": \"Optional filter condition\", \"type\": \"boolean\", \"required\": False}\n",
195
+ " ],\n",
196
+ " \"output\": {\"description\": \"Extracted values from the array after applying the optional filter\", \"type\": \"array\", \"example\": [2, 4]},\n",
197
+ " \"examples\": [\n",
198
+ " \"WITH [1, 2, 3] AS nums RETURN extract(n IN nums | n)\",\n",
199
+ " \"WITH [1, 2, 3, 4] AS nums RETURN extract(n IN nums WHERE n > 1 | n * 2)\"\n",
200
+ " ]\n",
201
+ "})\n",
202
+ "class PredicateExtract(PredicateFunction):\n",
203
+ " \"\"\"PredicateExtract function.\n",
204
+ " \n",
205
+ " Extracts values from an array with optional filtering.\n",
206
+ " \"\"\"\n",
207
+ "\n",
208
+ " def __init__(self):\n",
209
+ " super().__init__(\"extract\")\n",
210
+ "\n",
211
+ " def value(self) -> List[Any]:\n",
212
+ " return list(self._extract())\n",
213
+ " \n",
214
+ " def _extract(self) -> Iterator[Any]:\n",
215
+ " self.reference.referred = self._value_holder\n",
216
+ " array = self.array.value()\n",
217
+ " if array is None or not isinstance(array, list):\n",
218
+ " raise ValueError(\"Invalid array for extract function\")\n",
219
+ " \n",
220
+ " for item in array:\n",
221
+ " self._value_holder.holder = item\n",
222
+ " if self.where is None or self.where.value():\n",
223
+ " yield self._return.value()\n",
224
+ "\n",
225
+ "@FunctionDef({\n",
226
+ " \"description\": \"Checks if any element in the array satisfies the condition. Uses list comprehension syntax: any(variable IN array [WHERE condition])\",\n",
227
+ " \"category\": \"predicate\",\n",
228
+ " \"parameters\": [\n",
229
+ " {\"name\": \"variable\", \"description\": \"Variable name to bind each element\", \"type\": \"string\"},\n",
230
+ " {\"name\": \"array\", \"description\": \"Array to iterate over\", \"type\": \"array\"},\n",
231
+ " {\"name\": \"where\", \"description\": \"Condition to check for each element\", \"type\": \"boolean\", \"required\": False}\n",
232
+ " ],\n",
233
+ " \"output\": {\"description\": \"True if any element satisfies the condition, otherwise false\", \"type\": \"boolean\", \"example\": True},\n",
234
+ " \"examples\": [\n",
235
+ " \"WITH [1, 2, 3] AS nums RETURN any(n IN nums | n > 2)\",\n",
236
+ " \"WITH [1, 2, 3] AS nums RETURN any(n IN nums | n > 5)\"\n",
237
+ " ]\n",
238
+ "})\n",
239
+ "class Any(PredicateFunction):\n",
240
+ " \"\"\"Any function.\n",
241
+ " \n",
242
+ " Returns true if any element in the array satisfies the condition.\n",
243
+ " \"\"\"\n",
244
+ "\n",
245
+ " def __init__(self):\n",
246
+ " super().__init__(\"any\")\n",
247
+ "\n",
248
+ " def value(self) -> bool:\n",
249
+ " return any(self._any())\n",
250
+ " \n",
251
+ " def _any(self) -> Iterator[bool]:\n",
252
+ " self.reference.referred = self._value_holder\n",
253
+ " array = self.array.value()\n",
254
+ " if array is None or not isinstance(array, list):\n",
255
+ " raise ValueError(\"Invalid array for any function\")\n",
256
+ " \n",
257
+ " for item in array:\n",
258
+ " self._value_holder.holder = item\n",
259
+ " if self.where is None or self.where.value():\n",
260
+ " yield True"
261
+ ]
262
+ },
263
+ {
264
+ "cell_type": "markdown",
265
+ "id": "7",
266
+ "metadata": {},
267
+ "source": [
268
+ "Test functions just created"
269
+ ]
270
+ },
271
+ {
272
+ "cell_type": "code",
273
+ "execution_count": null,
274
+ "id": "8",
275
+ "metadata": {},
276
+ "outputs": [],
277
+ "source": [
278
+ "runner: Runner = Runner(\"\"\"\n",
279
+ " return uppercase(\"hello world\") as uppercased\n",
280
+ "\"\"\")\n",
281
+ "await runner.run()\n",
282
+ "for record in runner.results:\n",
283
+ " print(record)\n",
284
+ "\n",
285
+ "runner: Runner = Runner(\"\"\"\n",
286
+ " unwind [1, 2, 3, 4, 5] as num\n",
287
+ " return product(num) as total_product\n",
288
+ "\"\"\")\n",
289
+ "await runner.run()\n",
290
+ "for record in runner.results:\n",
291
+ " print(record)\n",
292
+ "\n",
293
+ "runner: Runner = Runner(\"\"\"\n",
294
+ " load json from get(\"https://catfact.ninja/fact\") as result\n",
295
+ " return result.fact as cat_fact\n",
296
+ "\"\"\")\n",
297
+ "await runner.run()\n",
298
+ "for record in runner.results:\n",
299
+ " print(record)\n",
300
+ "\n",
301
+ "runner: Runner = Runner(\"\"\"\n",
302
+ " load json from json_file(\"../misc/data/test.json\") as result\n",
303
+ " unwind result as entry\n",
304
+ " return entry\n",
305
+ "\"\"\")\n",
306
+ "await runner.run()\n",
307
+ "for record in runner.results:\n",
308
+ " print(record)\n",
309
+ "\n",
310
+ "runner: Runner = Runner(\"\"\"\n",
311
+ " with [\n",
312
+ " {\"age\": 25, \"name\": \"Alice\"},\n",
313
+ " {\"age\": 30, \"name\": \"Bob\"},\n",
314
+ " {\"age\": 22, \"name\": \"Charlie\"},\n",
315
+ " {\"age\": 28, \"name\": \"Diana\"}\n",
316
+ " ] as people\n",
317
+ " return extract(p IN people | p.name WHERE p.age > 25) as names_over_25\n",
318
+ "\"\"\")\n",
319
+ "await runner.run()\n",
320
+ "for record in runner.results:\n",
321
+ " print(record)\n",
322
+ "\n",
323
+ "runner: Runner = Runner(\"\"\"\n",
324
+ " with [1, 2, 3, 4, 5] as numbers\n",
325
+ " return any(n IN numbers | n where n > 6) as has_greater_than_3\n",
326
+ "\"\"\")\n",
327
+ "await runner.run()\n",
328
+ "for record in runner.results:\n",
329
+ " print(record)"
330
+ ]
331
+ },
332
+ {
333
+ "cell_type": "markdown",
334
+ "id": "9",
335
+ "metadata": {},
336
+ "source": [
337
+ "List functions"
338
+ ]
339
+ },
340
+ {
341
+ "cell_type": "code",
342
+ "execution_count": null,
343
+ "id": "10",
344
+ "metadata": {},
345
+ "outputs": [],
346
+ "source": [
347
+ "runner: Runner = Runner(\"\"\"\n",
348
+ " unwind functions() as func\n",
349
+ " return func\n",
350
+ "\"\"\")\n",
351
+ "await runner.run()\n",
352
+ "for record in runner.results:\n",
353
+ " print(record)"
354
+ ]
355
+ },
356
+ {
357
+ "cell_type": "markdown",
358
+ "id": "11",
359
+ "metadata": {},
360
+ "source": [
361
+ "Test virtual graph"
362
+ ]
363
+ },
364
+ {
365
+ "cell_type": "code",
366
+ "execution_count": null,
367
+ "id": "12",
368
+ "metadata": {},
369
+ "outputs": [],
370
+ "source": [
371
+ "await Runner(\"\"\"\n",
372
+ " create virtual (:User) as {\n",
373
+ " load json from json_file(\n",
374
+ " \"../misc/data/users.json\"\n",
375
+ " ) as users\n",
376
+ " unwind users as user\n",
377
+ " return user.id as id,\n",
378
+ " user.name as name,\n",
379
+ " user.title as title,\n",
380
+ " user.department as department,\n",
381
+ " user.email as email,\n",
382
+ " user.managerId as managerId\n",
383
+ " }\n",
384
+ "\"\"\").run()\n",
385
+ "\n",
386
+ "await Runner(\"\"\"\n",
387
+ " create virtual (:User)-[:MANAGED_BY]->(:User) as {\n",
388
+ " load json from json_file(\n",
389
+ " \"../misc/data/users.json\"\n",
390
+ " ) as users\n",
391
+ " unwind users as user\n",
392
+ " return user.id as left_id, user.managerId as right_id\n",
393
+ " }\n",
394
+ "\"\"\").run()\n",
395
+ "\n",
396
+ "runner: Runner = Runner(\"\"\"\n",
397
+ " MATCH p=(u:User)-[:MANAGED_BY*]->(ceo:User)\n",
398
+ " WHERE NOT (ceo)-[:MANAGED_BY]->(:User)\n",
399
+ " and any(n IN nodes(p) | n where n.department = \"Litigation\")\n",
400
+ " RETURN\n",
401
+ " u.name as employee,\n",
402
+ " extract(n IN nodes(p) | n.name) as management_chain\n",
403
+ "\"\"\")\n",
404
+ "await runner.run()\n",
405
+ "print(f\"Total results: {len(runner.results)}\")\n",
406
+ "for record in runner.results:\n",
407
+ " print(record)"
408
+ ]
409
+ },
410
+ {
411
+ "cell_type": "code",
412
+ "execution_count": null,
413
+ "id": "13",
414
+ "metadata": {},
415
+ "outputs": [],
416
+ "source": []
417
+ }
418
+ ],
419
+ "metadata": {
420
+ "kernelspec": {
421
+ "display_name": "flowquery",
422
+ "language": "python",
423
+ "name": "python3"
424
+ },
425
+ "language_info": {
426
+ "codemirror_mode": {
427
+ "name": "ipython",
428
+ "version": 3
429
+ },
430
+ "file_extension": ".py",
431
+ "mimetype": "text/x-python",
432
+ "name": "python",
433
+ "nbconvert_exporter": "python",
434
+ "pygments_lexer": "ipython3",
435
+ "version": "3.10.19"
436
+ }
437
+ },
438
+ "nbformat": 4,
439
+ "nbformat_minor": 5
440
+ }