flowquery 1.0.16 → 1.0.17

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 (318) hide show
  1. package/.github/workflows/python-publish.yml +97 -0
  2. package/dist/compute/runner.d.ts +3 -2
  3. package/dist/compute/runner.d.ts.map +1 -1
  4. package/dist/compute/runner.js +7 -7
  5. package/dist/compute/runner.js.map +1 -1
  6. package/dist/flowquery.min.js +1 -1
  7. package/dist/graph/data.d.ts +31 -0
  8. package/dist/graph/data.d.ts.map +1 -0
  9. package/dist/graph/data.js +110 -0
  10. package/dist/graph/data.js.map +1 -0
  11. package/dist/graph/database.d.ts +20 -0
  12. package/dist/graph/database.d.ts.map +1 -0
  13. package/dist/graph/database.js +77 -0
  14. package/dist/graph/database.js.map +1 -0
  15. package/dist/graph/hops.d.ts +11 -0
  16. package/dist/graph/hops.d.ts.map +1 -0
  17. package/dist/graph/hops.js +25 -0
  18. package/dist/graph/hops.js.map +1 -0
  19. package/dist/graph/node.d.ts +35 -0
  20. package/dist/graph/node.d.ts.map +1 -0
  21. package/dist/graph/node.js +113 -0
  22. package/dist/graph/node.js.map +1 -0
  23. package/dist/graph/node_data.d.ts +11 -0
  24. package/dist/graph/node_data.d.ts.map +1 -0
  25. package/dist/graph/node_data.js +20 -0
  26. package/dist/graph/node_data.js.map +1 -0
  27. package/dist/graph/node_reference.d.ts +10 -0
  28. package/dist/graph/node_reference.d.ts.map +1 -0
  29. package/dist/graph/node_reference.js +52 -0
  30. package/dist/graph/node_reference.js.map +1 -0
  31. package/dist/graph/pattern.d.ts +18 -0
  32. package/dist/graph/pattern.d.ts.map +1 -0
  33. package/dist/graph/pattern.js +114 -0
  34. package/dist/graph/pattern.js.map +1 -0
  35. package/dist/graph/pattern_expression.d.ts +14 -0
  36. package/dist/graph/pattern_expression.d.ts.map +1 -0
  37. package/dist/graph/pattern_expression.js +58 -0
  38. package/dist/graph/pattern_expression.js.map +1 -0
  39. package/dist/graph/patterns.d.ts +11 -0
  40. package/dist/graph/patterns.d.ts.map +1 -0
  41. package/dist/graph/patterns.js +49 -0
  42. package/dist/graph/patterns.js.map +1 -0
  43. package/dist/graph/physical_node.d.ts +10 -0
  44. package/dist/graph/physical_node.d.ts.map +1 -0
  45. package/dist/graph/physical_node.js +40 -0
  46. package/dist/graph/physical_node.js.map +1 -0
  47. package/dist/graph/physical_relationship.d.ts +10 -0
  48. package/dist/graph/physical_relationship.d.ts.map +1 -0
  49. package/dist/graph/physical_relationship.js +40 -0
  50. package/dist/graph/physical_relationship.js.map +1 -0
  51. package/dist/graph/relationship.d.ts +40 -0
  52. package/dist/graph/relationship.d.ts.map +1 -0
  53. package/dist/graph/relationship.js +124 -0
  54. package/dist/graph/relationship.js.map +1 -0
  55. package/dist/graph/relationship_data.d.ts +12 -0
  56. package/dist/graph/relationship_data.d.ts.map +1 -0
  57. package/dist/graph/relationship_data.js +40 -0
  58. package/dist/graph/relationship_data.js.map +1 -0
  59. package/dist/graph/relationship_match_collector.d.ts +19 -0
  60. package/dist/graph/relationship_match_collector.d.ts.map +1 -0
  61. package/dist/graph/relationship_match_collector.js +55 -0
  62. package/dist/graph/relationship_match_collector.js.map +1 -0
  63. package/dist/graph/relationship_reference.d.ts +8 -0
  64. package/dist/graph/relationship_reference.d.ts.map +1 -0
  65. package/dist/graph/relationship_reference.js +37 -0
  66. package/dist/graph/relationship_reference.js.map +1 -0
  67. package/dist/parsing/base_parser.d.ts +1 -0
  68. package/dist/parsing/base_parser.d.ts.map +1 -1
  69. package/dist/parsing/base_parser.js +4 -1
  70. package/dist/parsing/base_parser.js.map +1 -1
  71. package/dist/parsing/context.d.ts +2 -2
  72. package/dist/parsing/context.js +5 -5
  73. package/dist/parsing/expressions/boolean.d.ts +8 -0
  74. package/dist/parsing/expressions/boolean.d.ts.map +1 -0
  75. package/dist/parsing/expressions/boolean.js +26 -0
  76. package/dist/parsing/expressions/boolean.js.map +1 -0
  77. package/dist/parsing/expressions/expression.d.ts +4 -1
  78. package/dist/parsing/expressions/expression.d.ts.map +1 -1
  79. package/dist/parsing/expressions/expression.js +15 -8
  80. package/dist/parsing/expressions/expression.js.map +1 -1
  81. package/dist/parsing/expressions/operator.d.ts +1 -1
  82. package/dist/parsing/expressions/operator.d.ts.map +1 -1
  83. package/dist/parsing/expressions/operator.js.map +1 -1
  84. package/dist/parsing/functions/function_factory.d.ts +13 -13
  85. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  86. package/dist/parsing/functions/function_factory.js +20 -18
  87. package/dist/parsing/functions/function_factory.js.map +1 -1
  88. package/dist/parsing/operations/create_node.d.ts +14 -0
  89. package/dist/parsing/operations/create_node.d.ts.map +1 -0
  90. package/dist/parsing/operations/create_node.js +51 -0
  91. package/dist/parsing/operations/create_node.js.map +1 -0
  92. package/dist/parsing/operations/create_relationship.d.ts +14 -0
  93. package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
  94. package/dist/parsing/operations/create_relationship.js +51 -0
  95. package/dist/parsing/operations/create_relationship.js.map +1 -0
  96. package/dist/parsing/operations/match.d.ts +15 -0
  97. package/dist/parsing/operations/match.d.ts.map +1 -0
  98. package/dist/parsing/operations/match.js +45 -0
  99. package/dist/parsing/operations/match.js.map +1 -0
  100. package/dist/parsing/operations/operation.d.ts +1 -0
  101. package/dist/parsing/operations/operation.d.ts.map +1 -1
  102. package/dist/parsing/operations/operation.js +6 -0
  103. package/dist/parsing/operations/operation.js.map +1 -1
  104. package/dist/parsing/operations/return.d.ts +1 -0
  105. package/dist/parsing/operations/return.d.ts.map +1 -1
  106. package/dist/parsing/operations/return.js +7 -1
  107. package/dist/parsing/operations/return.js.map +1 -1
  108. package/dist/parsing/operations/where.d.ts +1 -1
  109. package/dist/parsing/operations/where.d.ts.map +1 -1
  110. package/dist/parsing/operations/where.js +4 -0
  111. package/dist/parsing/operations/where.js.map +1 -1
  112. package/dist/parsing/parser.d.ts +10 -0
  113. package/dist/parsing/parser.d.ts.map +1 -1
  114. package/dist/parsing/parser.js +344 -5
  115. package/dist/parsing/parser.js.map +1 -1
  116. package/dist/parsing/token_to_node.d.ts.map +1 -1
  117. package/dist/parsing/token_to_node.js +7 -0
  118. package/dist/parsing/token_to_node.js.map +1 -1
  119. package/dist/tokenization/keyword.d.ts +1 -0
  120. package/dist/tokenization/keyword.d.ts.map +1 -1
  121. package/dist/tokenization/keyword.js +1 -0
  122. package/dist/tokenization/keyword.js.map +1 -1
  123. package/dist/tokenization/token.d.ts +4 -0
  124. package/dist/tokenization/token.d.ts.map +1 -1
  125. package/dist/tokenization/token.js +14 -1
  126. package/dist/tokenization/token.js.map +1 -1
  127. package/dist/tokenization/token_type.d.ts +1 -0
  128. package/dist/tokenization/token_type.d.ts.map +1 -1
  129. package/dist/tokenization/token_type.js +1 -0
  130. package/dist/tokenization/token_type.js.map +1 -1
  131. package/dist/tokenization/tokenizer.d.ts +2 -1
  132. package/dist/tokenization/tokenizer.d.ts.map +1 -1
  133. package/dist/tokenization/tokenizer.js +25 -12
  134. package/dist/tokenization/tokenizer.js.map +1 -1
  135. package/docs/flowquery.min.js +1 -1
  136. package/flowquery-py/README.md +166 -0
  137. package/flowquery-py/pyproject.toml +75 -0
  138. package/flowquery-py/setup_env.ps1 +92 -0
  139. package/flowquery-py/setup_env.sh +87 -0
  140. package/flowquery-py/src/__init__.py +34 -0
  141. package/flowquery-py/src/__main__.py +10 -0
  142. package/flowquery-py/src/compute/__init__.py +5 -0
  143. package/flowquery-py/src/compute/runner.py +60 -0
  144. package/flowquery-py/src/extensibility.py +52 -0
  145. package/flowquery-py/src/graph/__init__.py +31 -0
  146. package/flowquery-py/src/graph/data.py +118 -0
  147. package/flowquery-py/src/graph/database.py +82 -0
  148. package/flowquery-py/src/graph/hops.py +43 -0
  149. package/flowquery-py/src/graph/node.py +112 -0
  150. package/flowquery-py/src/graph/node_data.py +26 -0
  151. package/flowquery-py/src/graph/node_reference.py +49 -0
  152. package/flowquery-py/src/graph/pattern.py +125 -0
  153. package/flowquery-py/src/graph/pattern_expression.py +62 -0
  154. package/flowquery-py/src/graph/patterns.py +42 -0
  155. package/flowquery-py/src/graph/physical_node.py +40 -0
  156. package/flowquery-py/src/graph/physical_relationship.py +36 -0
  157. package/flowquery-py/src/graph/relationship.py +135 -0
  158. package/flowquery-py/src/graph/relationship_data.py +33 -0
  159. package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
  160. package/flowquery-py/src/graph/relationship_reference.py +21 -0
  161. package/flowquery-py/src/io/__init__.py +5 -0
  162. package/flowquery-py/src/io/command_line.py +67 -0
  163. package/flowquery-py/src/parsing/__init__.py +17 -0
  164. package/flowquery-py/src/parsing/alias.py +20 -0
  165. package/flowquery-py/src/parsing/alias_option.py +11 -0
  166. package/flowquery-py/src/parsing/ast_node.py +146 -0
  167. package/flowquery-py/src/parsing/base_parser.py +84 -0
  168. package/flowquery-py/src/parsing/components/__init__.py +19 -0
  169. package/flowquery-py/src/parsing/components/csv.py +8 -0
  170. package/flowquery-py/src/parsing/components/from_.py +10 -0
  171. package/flowquery-py/src/parsing/components/headers.py +12 -0
  172. package/flowquery-py/src/parsing/components/json.py +8 -0
  173. package/flowquery-py/src/parsing/components/null.py +10 -0
  174. package/flowquery-py/src/parsing/components/post.py +8 -0
  175. package/flowquery-py/src/parsing/components/text.py +8 -0
  176. package/flowquery-py/src/parsing/context.py +50 -0
  177. package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
  178. package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
  179. package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
  180. package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
  181. package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
  182. package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
  183. package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
  184. package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
  185. package/flowquery-py/src/parsing/expressions/expression.py +138 -0
  186. package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
  187. package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
  188. package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
  189. package/flowquery-py/src/parsing/expressions/number.py +32 -0
  190. package/flowquery-py/src/parsing/expressions/operator.py +169 -0
  191. package/flowquery-py/src/parsing/expressions/reference.py +47 -0
  192. package/flowquery-py/src/parsing/expressions/string.py +27 -0
  193. package/flowquery-py/src/parsing/functions/__init__.py +75 -0
  194. package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
  195. package/flowquery-py/src/parsing/functions/async_function.py +62 -0
  196. package/flowquery-py/src/parsing/functions/avg.py +55 -0
  197. package/flowquery-py/src/parsing/functions/collect.py +75 -0
  198. package/flowquery-py/src/parsing/functions/function.py +68 -0
  199. package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
  200. package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
  201. package/flowquery-py/src/parsing/functions/functions.py +59 -0
  202. package/flowquery-py/src/parsing/functions/join.py +47 -0
  203. package/flowquery-py/src/parsing/functions/keys.py +34 -0
  204. package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
  205. package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
  206. package/flowquery-py/src/parsing/functions/rand.py +28 -0
  207. package/flowquery-py/src/parsing/functions/range_.py +34 -0
  208. package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
  209. package/flowquery-py/src/parsing/functions/replace.py +37 -0
  210. package/flowquery-py/src/parsing/functions/round_.py +32 -0
  211. package/flowquery-py/src/parsing/functions/size.py +32 -0
  212. package/flowquery-py/src/parsing/functions/split.py +47 -0
  213. package/flowquery-py/src/parsing/functions/stringify.py +47 -0
  214. package/flowquery-py/src/parsing/functions/sum.py +51 -0
  215. package/flowquery-py/src/parsing/functions/to_json.py +33 -0
  216. package/flowquery-py/src/parsing/functions/type_.py +47 -0
  217. package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
  218. package/flowquery-py/src/parsing/logic/__init__.py +15 -0
  219. package/flowquery-py/src/parsing/logic/case.py +29 -0
  220. package/flowquery-py/src/parsing/logic/else_.py +12 -0
  221. package/flowquery-py/src/parsing/logic/end.py +8 -0
  222. package/flowquery-py/src/parsing/logic/then.py +12 -0
  223. package/flowquery-py/src/parsing/logic/when.py +10 -0
  224. package/flowquery-py/src/parsing/operations/__init__.py +35 -0
  225. package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
  226. package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
  227. package/flowquery-py/src/parsing/operations/call.py +74 -0
  228. package/flowquery-py/src/parsing/operations/create_node.py +34 -0
  229. package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
  230. package/flowquery-py/src/parsing/operations/group_by.py +130 -0
  231. package/flowquery-py/src/parsing/operations/limit.py +22 -0
  232. package/flowquery-py/src/parsing/operations/load.py +140 -0
  233. package/flowquery-py/src/parsing/operations/match.py +29 -0
  234. package/flowquery-py/src/parsing/operations/operation.py +69 -0
  235. package/flowquery-py/src/parsing/operations/projection.py +21 -0
  236. package/flowquery-py/src/parsing/operations/return_op.py +50 -0
  237. package/flowquery-py/src/parsing/operations/unwind.py +37 -0
  238. package/flowquery-py/src/parsing/operations/where.py +41 -0
  239. package/flowquery-py/src/parsing/operations/with_op.py +18 -0
  240. package/flowquery-py/src/parsing/parser.py +1011 -0
  241. package/flowquery-py/src/parsing/token_to_node.py +109 -0
  242. package/flowquery-py/src/tokenization/__init__.py +23 -0
  243. package/flowquery-py/src/tokenization/keyword.py +48 -0
  244. package/flowquery-py/src/tokenization/operator.py +29 -0
  245. package/flowquery-py/src/tokenization/string_walker.py +158 -0
  246. package/flowquery-py/src/tokenization/symbol.py +19 -0
  247. package/flowquery-py/src/tokenization/token.py +659 -0
  248. package/flowquery-py/src/tokenization/token_mapper.py +52 -0
  249. package/flowquery-py/src/tokenization/token_type.py +21 -0
  250. package/flowquery-py/src/tokenization/tokenizer.py +214 -0
  251. package/flowquery-py/src/tokenization/trie.py +124 -0
  252. package/flowquery-py/src/utils/__init__.py +6 -0
  253. package/flowquery-py/src/utils/object_utils.py +20 -0
  254. package/flowquery-py/src/utils/string_utils.py +113 -0
  255. package/flowquery-py/tests/__init__.py +1 -0
  256. package/flowquery-py/tests/compute/__init__.py +1 -0
  257. package/flowquery-py/tests/compute/test_runner.py +1335 -0
  258. package/flowquery-py/tests/graph/__init__.py +1 -0
  259. package/flowquery-py/tests/graph/test_create.py +56 -0
  260. package/flowquery-py/tests/graph/test_data.py +73 -0
  261. package/flowquery-py/tests/graph/test_match.py +40 -0
  262. package/flowquery-py/tests/parsing/__init__.py +1 -0
  263. package/flowquery-py/tests/parsing/test_context.py +34 -0
  264. package/flowquery-py/tests/parsing/test_expression.py +49 -0
  265. package/flowquery-py/tests/parsing/test_parser.py +674 -0
  266. package/flowquery-py/tests/test_extensibility.py +611 -0
  267. package/flowquery-py/tests/tokenization/__init__.py +1 -0
  268. package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
  269. package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
  270. package/flowquery-py/tests/tokenization/test_trie.py +30 -0
  271. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  272. package/misc/apps/RAG/package.json +1 -1
  273. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
  274. package/misc/apps/RAG/src/components/index.ts +19 -10
  275. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +70 -140
  276. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +12 -0
  277. package/package.json +1 -1
  278. package/src/compute/runner.ts +24 -19
  279. package/src/graph/data.ts +112 -0
  280. package/src/graph/database.ts +63 -0
  281. package/src/graph/hops.ts +22 -0
  282. package/src/graph/node.ts +99 -0
  283. package/src/graph/node_data.ts +18 -0
  284. package/src/graph/node_reference.ts +33 -0
  285. package/src/graph/pattern.ts +101 -0
  286. package/src/graph/pattern_expression.ts +37 -0
  287. package/src/graph/patterns.ts +36 -0
  288. package/src/graph/physical_node.ts +23 -0
  289. package/src/graph/physical_relationship.ts +23 -0
  290. package/src/graph/relationship.ts +116 -0
  291. package/src/graph/relationship_data.ts +27 -0
  292. package/src/graph/relationship_match_collector.ts +58 -0
  293. package/src/graph/relationship_reference.ts +24 -0
  294. package/src/parsing/base_parser.ts +20 -14
  295. package/src/parsing/context.ts +14 -14
  296. package/src/parsing/expressions/boolean.ts +21 -0
  297. package/src/parsing/expressions/expression.ts +34 -26
  298. package/src/parsing/expressions/operator.ts +19 -1
  299. package/src/parsing/functions/function_factory.ts +45 -45
  300. package/src/parsing/operations/create_node.ts +39 -0
  301. package/src/parsing/operations/create_relationship.ts +38 -0
  302. package/src/parsing/operations/match.ts +31 -0
  303. package/src/parsing/operations/operation.ts +3 -0
  304. package/src/parsing/operations/return.ts +11 -7
  305. package/src/parsing/operations/where.ts +10 -6
  306. package/src/parsing/parser.ts +346 -8
  307. package/src/parsing/token_to_node.ts +6 -0
  308. package/src/tokenization/keyword.ts +41 -40
  309. package/src/tokenization/token.ts +21 -1
  310. package/src/tokenization/token_type.ts +2 -1
  311. package/src/tokenization/tokenizer.ts +52 -31
  312. package/tests/compute/runner.test.ts +654 -0
  313. package/tests/extensibility.test.ts +97 -93
  314. package/tests/graph/create.test.ts +36 -0
  315. package/tests/graph/data.test.ts +58 -0
  316. package/tests/graph/match.test.ts +29 -0
  317. package/tests/parsing/parser.test.ts +273 -2
  318. package/tests/tokenization/tokenizer.test.ts +90 -0
@@ -0,0 +1,34 @@
1
+ """Represents a CREATE operation for creating virtual relationships."""
2
+
3
+ from typing import Any, Dict, List
4
+
5
+ from .operation import Operation
6
+ from ..ast_node import ASTNode
7
+
8
+
9
+ class CreateRelationship(Operation):
10
+ """Represents a CREATE operation for creating virtual relationships."""
11
+
12
+ def __init__(self, relationship, statement: ASTNode):
13
+ super().__init__()
14
+ self._relationship = relationship
15
+ self._statement = statement
16
+
17
+ @property
18
+ def relationship(self):
19
+ return self._relationship
20
+
21
+ @property
22
+ def statement(self) -> ASTNode:
23
+ return self._statement
24
+
25
+ async def run(self) -> None:
26
+ if self._relationship is None:
27
+ raise ValueError("Relationship is null")
28
+ from ...graph.database import Database
29
+ db = Database.get_instance()
30
+ db.add_relationship(self._relationship, self._statement)
31
+
32
+ @property
33
+ def results(self) -> List[Dict[str, Any]]:
34
+ return []
@@ -0,0 +1,130 @@
1
+ """GroupBy implementation for aggregate operations."""
2
+
3
+ from typing import Any, Dict, Generator, List, Optional
4
+
5
+ from ..expressions.expression import Expression
6
+ from ..functions.aggregate_function import AggregateFunction
7
+ from ..functions.reducer_element import ReducerElement
8
+ from .projection import Projection
9
+
10
+
11
+ class GroupByNode:
12
+ """Represents a node in the group-by tree."""
13
+
14
+ def __init__(self, value: Any = None):
15
+ self._value = value
16
+ self._children: Dict[Any, 'GroupByNode'] = {}
17
+ self._elements: Optional[List[ReducerElement]] = None
18
+
19
+ @property
20
+ def value(self) -> Any:
21
+ return self._value
22
+
23
+ @property
24
+ def children(self) -> Dict[Any, 'GroupByNode']:
25
+ return self._children
26
+
27
+ @property
28
+ def elements(self) -> Optional[List[ReducerElement]]:
29
+ return self._elements
30
+
31
+ @elements.setter
32
+ def elements(self, elements: List[ReducerElement]) -> None:
33
+ self._elements = elements
34
+
35
+
36
+ class GroupBy(Projection):
37
+ """Implements grouping and aggregation for FlowQuery operations."""
38
+
39
+ def __init__(self, expressions: List[Expression]):
40
+ super().__init__(expressions)
41
+ self._root = GroupByNode()
42
+ self._current = self._root
43
+ self._mappers: Optional[List[Expression]] = None
44
+ self._reducers: Optional[List[AggregateFunction]] = None
45
+ self._where = None
46
+
47
+ async def run(self) -> None:
48
+ self._reset_tree()
49
+ self._map()
50
+ self._reduce()
51
+
52
+ @property
53
+ def _root_node(self) -> GroupByNode:
54
+ return self._root
55
+
56
+ def _reset_tree(self) -> None:
57
+ self._current = self._root
58
+
59
+ def _map(self) -> None:
60
+ node = self._current
61
+ for mapper in self.mappers:
62
+ value = mapper.value()
63
+ child = node.children.get(value)
64
+ if child is None:
65
+ child = GroupByNode(value)
66
+ node.children[value] = child
67
+ node = child
68
+ self._current = node
69
+
70
+ def _reduce(self) -> None:
71
+ if self._current.elements is None:
72
+ self._current.elements = [reducer.element() for reducer in self.reducers]
73
+ elements = self._current.elements
74
+ for i, reducer in enumerate(self.reducers):
75
+ reducer.reduce(elements[i])
76
+
77
+ @property
78
+ def mappers(self) -> List[Expression]:
79
+ if self._mappers is None:
80
+ self._mappers = list(self._generate_mappers())
81
+ return self._mappers
82
+
83
+ def _generate_mappers(self) -> Generator[Expression, None, None]:
84
+ for expression, _ in self.expressions():
85
+ if expression.mappable():
86
+ yield expression
87
+
88
+ @property
89
+ def reducers(self) -> List[AggregateFunction]:
90
+ if self._reducers is None:
91
+ self._reducers = []
92
+ for child in self.children:
93
+ self._reducers.extend(child.reducers())
94
+ return self._reducers
95
+
96
+ def generate_results(
97
+ self,
98
+ mapper_index: int = 0,
99
+ node: Optional[GroupByNode] = None
100
+ ) -> Generator[Dict[str, Any], None, None]:
101
+ if node is None:
102
+ node = self._root
103
+
104
+ if len(node.children) > 0:
105
+ for child in node.children.values():
106
+ self.mappers[mapper_index].overridden = child.value
107
+ yield from self.generate_results(mapper_index + 1, child)
108
+ else:
109
+ if node.elements:
110
+ for i, element in enumerate(node.elements):
111
+ self.reducers[i].overridden = element.value
112
+ record: Dict[str, Any] = {}
113
+ for expression, alias in self.expressions():
114
+ record[alias] = expression.value()
115
+ if self.where_condition:
116
+ yield record
117
+
118
+ @property
119
+ def where(self):
120
+ return self._where
121
+
122
+ @where.setter
123
+ def where(self, where) -> None:
124
+ self._where = where
125
+
126
+ @property
127
+ def where_condition(self) -> bool:
128
+ if self._where is None:
129
+ return True
130
+ return self._where.value()
@@ -0,0 +1,22 @@
1
+ """Represents a LIMIT operation that limits the number of results."""
2
+
3
+ from .operation import Operation
4
+
5
+
6
+ class Limit(Operation):
7
+ """Represents a LIMIT operation that limits the number of results."""
8
+
9
+ def __init__(self, limit: int):
10
+ super().__init__()
11
+ self._count = 0
12
+ self._limit = limit
13
+
14
+ async def run(self) -> None:
15
+ if self._count >= self._limit:
16
+ return
17
+ self._count += 1
18
+ if self.next:
19
+ await self.next.run()
20
+
21
+ def reset(self) -> None:
22
+ self._count = 0
@@ -0,0 +1,140 @@
1
+ """Represents a LOAD operation that fetches data from external sources."""
2
+
3
+ import json
4
+ from typing import Any, Dict, Optional
5
+
6
+ from .operation import Operation
7
+ from ..functions.async_function import AsyncFunction
8
+
9
+
10
+ class Load(Operation):
11
+ """Represents a LOAD operation that fetches data from external sources."""
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ self._value: Any = None
16
+
17
+ @property
18
+ def type(self):
19
+ """Gets the data type (JSON, CSV, or Text)."""
20
+ return self.children[0]
21
+
22
+ @property
23
+ def from_component(self):
24
+ """Gets the From component which contains either a URL expression or an AsyncFunction."""
25
+ return self.children[1]
26
+
27
+ @property
28
+ def is_async_function(self) -> bool:
29
+ """Checks if the data source is an async function."""
30
+ return isinstance(self.from_component.first_child(), AsyncFunction)
31
+
32
+ @property
33
+ def async_function(self) -> Optional[AsyncFunction]:
34
+ """Gets the async function if the source is a function, otherwise None."""
35
+ child = self.from_component.first_child()
36
+ return child if isinstance(child, AsyncFunction) else None
37
+
38
+ @property
39
+ def from_(self) -> str:
40
+ return self.children[1].value()
41
+
42
+ @property
43
+ def headers(self) -> Dict[str, str]:
44
+ from ..components.headers import Headers
45
+ if self.child_count() > 2 and isinstance(self.children[2], Headers):
46
+ return self.children[2].value() or {}
47
+ return {}
48
+
49
+ @property
50
+ def payload(self):
51
+ from ..components.post import Post
52
+ post = None
53
+ if self.child_count() > 2 and isinstance(self.children[2], Post):
54
+ post = self.children[2]
55
+ elif self.child_count() > 3 and isinstance(self.children[3], Post):
56
+ post = self.children[3]
57
+ return post.first_child() if post else None
58
+
59
+ def _method(self) -> str:
60
+ return "GET" if self.payload is None else "POST"
61
+
62
+ def _options(self) -> Dict[str, Any]:
63
+ headers = dict(self.headers)
64
+ payload = self.payload
65
+ data = payload.value() if payload else None
66
+ if data is not None and isinstance(data, dict) and "Content-Type" not in headers:
67
+ headers["Content-Type"] = "application/json"
68
+ options: Dict[str, Any] = {
69
+ "method": self._method(),
70
+ "headers": headers,
71
+ }
72
+ if payload is not None:
73
+ options["body"] = json.dumps(payload.value())
74
+ return options
75
+
76
+ async def _load_from_function(self) -> None:
77
+ """Loads data from an async function source."""
78
+ async_func = self.async_function
79
+ if async_func is None:
80
+ return
81
+ args = async_func.get_arguments()
82
+ async for item in async_func.generate(*args):
83
+ self._value = item
84
+ if self.next:
85
+ await self.next.run()
86
+
87
+ async def _load_from_url(self) -> None:
88
+ """Loads data from a URL source."""
89
+ import aiohttp
90
+ from ..components.json import JSON as JSONComponent
91
+ from ..components.text import Text
92
+
93
+ async with aiohttp.ClientSession() as session:
94
+ options = self._options()
95
+ method = options.pop("method")
96
+ headers = options.pop("headers", {})
97
+ body = options.pop("body", None)
98
+
99
+ async with session.request(
100
+ method,
101
+ self.from_,
102
+ headers=headers,
103
+ data=body
104
+ ) as response:
105
+ if isinstance(self.type, JSONComponent):
106
+ data = await response.json()
107
+ elif isinstance(self.type, Text):
108
+ data = await response.text()
109
+ else:
110
+ data = await response.text()
111
+
112
+ if isinstance(data, list):
113
+ for item in data:
114
+ self._value = item
115
+ if self.next:
116
+ await self.next.run()
117
+ elif isinstance(data, dict):
118
+ self._value = data
119
+ if self.next:
120
+ await self.next.run()
121
+ elif isinstance(data, str):
122
+ self._value = data
123
+ if self.next:
124
+ await self.next.run()
125
+
126
+ async def load(self) -> None:
127
+ if self.is_async_function:
128
+ await self._load_from_function()
129
+ else:
130
+ await self._load_from_url()
131
+
132
+ async def run(self) -> None:
133
+ try:
134
+ await self.load()
135
+ except Exception as e:
136
+ source = self.async_function.name if self.is_async_function else self.from_
137
+ raise RuntimeError(f"Failed to load data from {source}. Error: {e}")
138
+
139
+ def value(self) -> Any:
140
+ return self._value
@@ -0,0 +1,29 @@
1
+ """Represents a MATCH operation for graph pattern matching."""
2
+
3
+ from typing import List
4
+
5
+ from .operation import Operation
6
+
7
+
8
+ class Match(Operation):
9
+ """Represents a MATCH operation for graph pattern matching."""
10
+
11
+ def __init__(self, patterns=None):
12
+ super().__init__()
13
+ from ...graph.patterns import Patterns
14
+ self._patterns = Patterns(patterns or [])
15
+
16
+ @property
17
+ def patterns(self):
18
+ return self._patterns.patterns if self._patterns else []
19
+
20
+ async def run(self) -> None:
21
+ """Executes the match operation by chaining the patterns together."""
22
+ await self._patterns.initialize()
23
+
24
+ async def to_do_next():
25
+ if self.next:
26
+ await self.next.run()
27
+
28
+ self._patterns.to_do_next = to_do_next
29
+ await self._patterns.traverse()
@@ -0,0 +1,69 @@
1
+ """Base class for all FlowQuery operations."""
2
+
3
+ from abc import ABC
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from ..ast_node import ASTNode
7
+
8
+
9
+ class Operation(ASTNode, ABC):
10
+ """Base class for all FlowQuery operations.
11
+
12
+ Operations represent the main statements in FlowQuery (WITH, UNWIND, RETURN, LOAD, WHERE).
13
+ They form a linked list structure and can be executed sequentially.
14
+ """
15
+
16
+ def __init__(self):
17
+ super().__init__()
18
+ self._previous: Optional[Operation] = None
19
+ self._next: Optional[Operation] = None
20
+
21
+ @property
22
+ def previous(self) -> Optional['Operation']:
23
+ return self._previous
24
+
25
+ @previous.setter
26
+ def previous(self, value: Optional['Operation']) -> None:
27
+ self._previous = value
28
+
29
+ @property
30
+ def next(self) -> Optional['Operation']:
31
+ return self._next
32
+
33
+ @next.setter
34
+ def next(self, value: Optional['Operation']) -> None:
35
+ self._next = value
36
+
37
+ def add_sibling(self, operation: 'Operation') -> None:
38
+ if self._parent:
39
+ self._parent.add_child(operation)
40
+ operation.previous = self
41
+ self.next = operation
42
+
43
+ @property
44
+ def is_last(self) -> bool:
45
+ return self._next is None
46
+
47
+ async def run(self) -> None:
48
+ """Executes this operation. Must be implemented by subclasses.
49
+
50
+ Raises:
51
+ NotImplementedError: If not implemented by subclass
52
+ """
53
+ raise NotImplementedError("Not implemented")
54
+
55
+ async def finish(self) -> None:
56
+ """Finishes execution by calling finish on the next operation in the chain."""
57
+ if self.next:
58
+ await self.next.finish()
59
+
60
+ async def initialize(self) -> None:
61
+ if self.next:
62
+ await self.next.initialize()
63
+
64
+ def reset(self) -> None:
65
+ pass
66
+
67
+ @property
68
+ def results(self) -> List[Dict[str, Any]]:
69
+ raise NotImplementedError("Not implemented")
@@ -0,0 +1,21 @@
1
+ """Base class for projection operations."""
2
+
3
+ from typing import Generator, List, Tuple, Optional
4
+
5
+ from ..expressions.expression import Expression
6
+ from .operation import Operation
7
+
8
+
9
+ class Projection(Operation):
10
+ """Base class for operations that project expressions."""
11
+
12
+ def __init__(self, expressions: List[Expression]):
13
+ super().__init__()
14
+ self.children = expressions
15
+
16
+ def expressions(self) -> Generator[Tuple[Expression, str], None, None]:
17
+ """Yields tuples of (expression, alias) for all child expressions."""
18
+ for i, child in enumerate(self.children):
19
+ expression: Expression = child
20
+ alias = expression.alias or f"expr{i}"
21
+ yield (expression, alias)
@@ -0,0 +1,50 @@
1
+ """Represents a RETURN operation that produces the final query results."""
2
+
3
+ import copy
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from .projection import Projection
7
+
8
+
9
+ class Return(Projection):
10
+ """Represents a RETURN operation that produces the final query results.
11
+
12
+ The RETURN operation evaluates expressions and collects them into result records.
13
+ It can optionally have a WHERE clause to filter results.
14
+
15
+ Example:
16
+ # RETURN x, y WHERE x > 0
17
+ """
18
+
19
+ def __init__(self, expressions):
20
+ super().__init__(expressions)
21
+ self._where: Optional['Where'] = None
22
+ self._results: List[Dict[str, Any]] = []
23
+
24
+ @property
25
+ def where(self) -> bool:
26
+ if self._where is None:
27
+ return True
28
+ return self._where.value()
29
+
30
+ @where.setter
31
+ def where(self, where: 'Where') -> None:
32
+ self._where = where
33
+
34
+ async def run(self) -> None:
35
+ if not self.where:
36
+ return
37
+ record: Dict[str, Any] = {}
38
+ for expression, alias in self.expressions():
39
+ raw = expression.value()
40
+ # Deep copy objects to preserve their state
41
+ value = copy.deepcopy(raw) if isinstance(raw, (dict, list)) else raw
42
+ record[alias] = value
43
+ self._results.append(record)
44
+
45
+ async def initialize(self) -> None:
46
+ self._results = []
47
+
48
+ @property
49
+ def results(self) -> List[Dict[str, Any]]:
50
+ return self._results
@@ -0,0 +1,37 @@
1
+ """Represents an UNWIND operation that iterates over arrays."""
2
+
3
+ from typing import Any
4
+
5
+ from ..expressions.expression import Expression
6
+ from .operation import Operation
7
+
8
+
9
+ class Unwind(Operation):
10
+ """Represents an UNWIND operation that iterates over an array expression."""
11
+
12
+ def __init__(self, expression: Expression):
13
+ super().__init__()
14
+ self._value: Any = None
15
+ self.add_child(expression)
16
+
17
+ @property
18
+ def expression(self) -> Expression:
19
+ return self.children[0]
20
+
21
+ @property
22
+ def as_(self) -> str:
23
+ return self.children[1].value()
24
+
25
+ async def run(self) -> None:
26
+ expression_value = self.expression.value()
27
+ if not isinstance(expression_value, list):
28
+ raise ValueError("Expected array")
29
+ for item in expression_value:
30
+ self._value = item
31
+ if self.next:
32
+ await self.next.run()
33
+ if self.next:
34
+ self.next.reset()
35
+
36
+ def value(self) -> Any:
37
+ return self._value
@@ -0,0 +1,41 @@
1
+ """Represents a WHERE operation that filters data based on a condition."""
2
+
3
+ from typing import Any
4
+
5
+ from ..expressions.expression import Expression
6
+ from .operation import Operation
7
+
8
+
9
+ class Where(Operation):
10
+ """Represents a WHERE operation that filters data based on a condition.
11
+
12
+ The WHERE operation evaluates a boolean expression and only continues
13
+ execution to the next operation if the condition is true.
14
+
15
+ Example:
16
+ # RETURN x WHERE x > 0
17
+ """
18
+
19
+ def __init__(self, expression: Expression):
20
+ """Creates a new WHERE operation with the given condition.
21
+
22
+ Args:
23
+ expression: The boolean expression to evaluate
24
+ """
25
+ super().__init__()
26
+ self.add_child(expression)
27
+
28
+ @property
29
+ def expression(self) -> Expression:
30
+ return self.children[0]
31
+
32
+ async def run(self) -> None:
33
+ for pattern in self.expression.patterns():
34
+ await pattern.fetch_data()
35
+ await pattern.evaluate()
36
+ if self.expression.value():
37
+ if self.next:
38
+ await self.next.run()
39
+
40
+ def value(self) -> Any:
41
+ return self.expression.value()
@@ -0,0 +1,18 @@
1
+ """Represents a WITH operation that defines variables or intermediate results."""
2
+
3
+ from .projection import Projection
4
+
5
+
6
+ class With(Projection):
7
+ """Represents a WITH operation that defines variables or intermediate results.
8
+
9
+ The WITH operation creates named expressions that can be referenced later in the query.
10
+ It passes control to the next operation in the chain.
11
+
12
+ Example:
13
+ # WITH x = 1, y = 2 RETURN x + y
14
+ """
15
+
16
+ async def run(self) -> None:
17
+ if self.next:
18
+ await self.next.run()