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,40 @@
1
+ """Physical node representation for FlowQuery."""
2
+
3
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..parsing.ast_node import ASTNode
7
+
8
+ from .node import Node
9
+
10
+
11
+ class PhysicalNode(Node):
12
+ """Represents a physical node in the graph database."""
13
+
14
+ def __init__(self, id_: Optional[str], label: str, properties: Optional[Dict[str, Any]] = None):
15
+ super().__init__(id_, label)
16
+ # Store additional physical properties in a separate dict
17
+ # (Node.properties is for Expression-based pattern properties)
18
+ self._physical_properties = properties or {}
19
+ self._statement: Optional["ASTNode"] = None
20
+
21
+ @property
22
+ def physical_properties(self) -> Dict[str, Any]:
23
+ """Get the physical properties (values, not expressions)."""
24
+ return self._physical_properties
25
+
26
+ @property
27
+ def statement(self) -> Optional["ASTNode"]:
28
+ return self._statement
29
+
30
+ @statement.setter
31
+ def statement(self, value: Optional["ASTNode"]) -> None:
32
+ self._statement = value
33
+
34
+ async def data(self) -> List[Dict[str, Any]]:
35
+ if self._statement is None:
36
+ raise ValueError("Statement is null")
37
+ from ..compute.runner import Runner
38
+ runner = Runner(ast=self._statement)
39
+ await runner.run()
40
+ return runner.results
@@ -0,0 +1,36 @@
1
+ """Physical relationship representation for FlowQuery."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
5
+
6
+ from .relationship import Relationship
7
+
8
+ if TYPE_CHECKING:
9
+ from ..parsing.ast_node import ASTNode
10
+
11
+
12
+ class PhysicalRelationship(Relationship):
13
+ """Represents a physical relationship in the graph database."""
14
+
15
+ def __init__(self):
16
+ super().__init__()
17
+ self._statement: Optional[ASTNode] = None
18
+
19
+ @property
20
+ def statement(self) -> Optional[ASTNode]:
21
+ """Get the statement for this relationship."""
22
+ return self._statement
23
+
24
+ @statement.setter
25
+ def statement(self, value: Optional[ASTNode]) -> None:
26
+ """Set the statement for this relationship."""
27
+ self._statement = value
28
+
29
+ async def data(self) -> List[Dict[str, Any]]:
30
+ """Execute the statement and return results."""
31
+ if self._statement is None:
32
+ raise ValueError("Statement is null")
33
+ from ..compute.runner import Runner
34
+ runner = Runner(None, self._statement)
35
+ await runner.run()
36
+ return runner.results
@@ -0,0 +1,135 @@
1
+ """Graph relationship representation for FlowQuery."""
2
+
3
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union
4
+
5
+ from ..parsing.ast_node import ASTNode
6
+ from .hops import Hops
7
+ from .relationship_match_collector import RelationshipMatchCollector, RelationshipMatchRecord
8
+
9
+ if TYPE_CHECKING:
10
+ from .node import Node
11
+ from .relationship_data import RelationshipData, RelationshipRecord
12
+
13
+
14
+ class Relationship(ASTNode):
15
+ """Represents a relationship in a graph pattern."""
16
+
17
+ def __init__(self):
18
+ super().__init__()
19
+ self._identifier: Optional[str] = None
20
+ self._type: Optional[str] = None
21
+ self._hops: Hops = Hops()
22
+ self._source: Optional['Node'] = None
23
+ self._target: Optional['Node'] = None
24
+ self._data: Optional['RelationshipData'] = None
25
+ self._value: Optional[Union[RelationshipMatchRecord, List[RelationshipMatchRecord]]] = None
26
+ self._matches: RelationshipMatchCollector = RelationshipMatchCollector()
27
+ self._properties: Dict[str, Any] = {}
28
+
29
+ @property
30
+ def identifier(self) -> Optional[str]:
31
+ return self._identifier
32
+
33
+ @identifier.setter
34
+ def identifier(self, value: str) -> None:
35
+ self._identifier = value
36
+
37
+ @property
38
+ def type(self) -> Optional[str]:
39
+ return self._type
40
+
41
+ @type.setter
42
+ def type(self, value: str) -> None:
43
+ self._type = value
44
+
45
+ @property
46
+ def hops(self) -> Hops:
47
+ return self._hops
48
+
49
+ @hops.setter
50
+ def hops(self, value: Hops) -> None:
51
+ self._hops = value
52
+
53
+ @property
54
+ def properties(self) -> Dict[str, Any]:
55
+ """Get properties from relationship data."""
56
+ if self._data:
57
+ return self._data.properties() or {}
58
+ return {}
59
+
60
+ @property
61
+ def source(self) -> Optional['Node']:
62
+ return self._source
63
+
64
+ @source.setter
65
+ def source(self, value: 'Node') -> None:
66
+ self._source = value
67
+
68
+ @property
69
+ def target(self) -> Optional['Node']:
70
+ return self._target
71
+
72
+ @target.setter
73
+ def target(self, value: 'Node') -> None:
74
+ self._target = value
75
+
76
+ # Keep start/end aliases for backward compatibility
77
+ @property
78
+ def start(self) -> Optional['Node']:
79
+ return self._source
80
+
81
+ @start.setter
82
+ def start(self, value: 'Node') -> None:
83
+ self._source = value
84
+
85
+ @property
86
+ def end(self) -> Optional['Node']:
87
+ return self._target
88
+
89
+ @end.setter
90
+ def end(self, value: 'Node') -> None:
91
+ self._target = value
92
+
93
+ def set_data(self, data: Optional['RelationshipData']) -> None:
94
+ self._data = data
95
+
96
+ def set_value(self, relationship: 'Relationship') -> None:
97
+ """Set value by pushing match to collector."""
98
+ self._matches.push(relationship)
99
+ self._value = self._matches.value()
100
+
101
+ def value(self) -> Optional[Union[RelationshipMatchRecord, List[RelationshipMatchRecord]]]:
102
+ return self._value
103
+
104
+ @property
105
+ def matches(self) -> List[RelationshipMatchRecord]:
106
+ return self._matches.matches
107
+
108
+ def set_end_node(self, node: 'Node') -> None:
109
+ """Set the end node for the current match."""
110
+ self._matches.end_node = node
111
+
112
+ async def find(self, left_id: str, hop: int = 0) -> None:
113
+ """Find relationships starting from the given node ID."""
114
+ # Save original source node
115
+ original = self._source
116
+ if hop > 0:
117
+ # For hops greater than 0, the source becomes the target of the previous hop
118
+ self._source = self._target
119
+ if hop == 0:
120
+ self._data.reset() if self._data else None
121
+
122
+ while self._data and self._data.find(left_id, hop):
123
+ data = self._data.current(hop)
124
+ if data and self._hops and hop >= self._hops.min:
125
+ self.set_value(self)
126
+ if self._target and 'right_id' in data:
127
+ await self._target.find(data['right_id'], hop)
128
+ if self._matches.is_circular():
129
+ raise ValueError("Circular relationship detected")
130
+ if self._hops and hop + 1 < self._hops.max:
131
+ await self.find(data['right_id'], hop + 1)
132
+ self._matches.pop()
133
+
134
+ # Restore original source node
135
+ self._source = original
@@ -0,0 +1,33 @@
1
+ """Relationship data class for FlowQuery."""
2
+
3
+ from typing import Any, Dict, List, Optional, TypedDict
4
+
5
+ from .data import Data
6
+
7
+
8
+ class RelationshipRecord(TypedDict, total=False):
9
+ """Represents a relationship record from the database."""
10
+ left_id: str
11
+ right_id: str
12
+
13
+
14
+ class RelationshipData(Data):
15
+ """Relationship data class extending Data with left_id-based indexing."""
16
+
17
+ def __init__(self, records: Optional[List[Dict[str, Any]]] = None):
18
+ super().__init__(records)
19
+ self._build_index("left_id")
20
+
21
+ def find(self, left_id: str, hop: int = 0) -> bool:
22
+ """Find a relationship by start node ID."""
23
+ return self._find(left_id, hop)
24
+
25
+ def properties(self) -> Optional[Dict[str, Any]]:
26
+ """Get properties of current relationship, excluding left_id and right_id."""
27
+ current = self.current()
28
+ if current:
29
+ props = dict(current)
30
+ props.pop("left_id", None)
31
+ props.pop("right_id", None)
32
+ return props
33
+ return None
@@ -0,0 +1,77 @@
1
+ """Collector for relationship match records."""
2
+
3
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING, TypedDict, Union
4
+
5
+ if TYPE_CHECKING:
6
+ from .relationship import Relationship
7
+ from .node import Node
8
+
9
+
10
+ class RelationshipMatchRecord(TypedDict, total=False):
11
+ """Represents a matched relationship record."""
12
+ type: str
13
+ startNode: Dict[str, Any]
14
+ endNode: Optional[Dict[str, Any]]
15
+ properties: Dict[str, Any]
16
+
17
+
18
+ class RelationshipMatchCollector:
19
+ """Collects relationship matches during graph traversal."""
20
+
21
+ def __init__(self):
22
+ self._matches: List[RelationshipMatchRecord] = []
23
+ self._node_ids: List[str] = []
24
+
25
+ def push(self, relationship: 'Relationship') -> RelationshipMatchRecord:
26
+ """Push a new match onto the collector."""
27
+ match: RelationshipMatchRecord = {
28
+ "type": relationship.type or "",
29
+ "startNode": relationship.source.value() if relationship.source else {},
30
+ "endNode": None,
31
+ "properties": relationship.properties,
32
+ }
33
+ self._matches.append(match)
34
+ start_node_value = match.get("startNode", {})
35
+ if isinstance(start_node_value, dict):
36
+ self._node_ids.append(start_node_value.get("id", ""))
37
+ return match
38
+
39
+ @property
40
+ def end_node(self) -> Optional[Dict[str, Any]]:
41
+ """Get the end node of the last match."""
42
+ if self._matches:
43
+ return self._matches[-1].get("endNode")
44
+ return None
45
+
46
+ @end_node.setter
47
+ def end_node(self, node: 'Node') -> None:
48
+ """Set the end node of the last match."""
49
+ if self._matches:
50
+ self._matches[-1]["endNode"] = node.value()
51
+
52
+ def pop(self) -> Optional[RelationshipMatchRecord]:
53
+ """Pop the last match from the collector."""
54
+ if self._node_ids:
55
+ self._node_ids.pop()
56
+ if self._matches:
57
+ return self._matches.pop()
58
+ return None
59
+
60
+ def value(self) -> Optional[Union[RelationshipMatchRecord, List[RelationshipMatchRecord]]]:
61
+ """Get the current value(s)."""
62
+ if len(self._matches) == 0:
63
+ return None
64
+ elif len(self._matches) == 1:
65
+ return self._matches[0]
66
+ else:
67
+ return self._matches
68
+
69
+ @property
70
+ def matches(self) -> List[RelationshipMatchRecord]:
71
+ """Get all matches."""
72
+ return self._matches
73
+
74
+ def is_circular(self) -> bool:
75
+ """Check if the collected relationships form a circular pattern."""
76
+ seen = set(self._node_ids)
77
+ return len(seen) < len(self._node_ids)
@@ -0,0 +1,21 @@
1
+ """Relationship reference for FlowQuery."""
2
+
3
+ from .relationship import Relationship
4
+ from ..parsing.ast_node import ASTNode
5
+
6
+
7
+ class RelationshipReference(Relationship):
8
+ """Represents a reference to an existing relationship variable."""
9
+
10
+ def __init__(self, relationship: Relationship, referred: ASTNode):
11
+ super().__init__()
12
+ self._referred = referred
13
+ if relationship.type:
14
+ self.type = relationship.type
15
+
16
+ @property
17
+ def referred(self) -> ASTNode:
18
+ return self._referred
19
+
20
+ def value(self):
21
+ return self._referred.value() if self._referred else None
@@ -0,0 +1,5 @@
1
+ """IO module for FlowQuery."""
2
+
3
+ from .command_line import CommandLine
4
+
5
+ __all__ = ["CommandLine"]
@@ -0,0 +1,67 @@
1
+ """Interactive command-line interface for FlowQuery."""
2
+
3
+ import asyncio
4
+ from typing import Optional
5
+
6
+ from ..compute.runner import Runner
7
+
8
+
9
+ class CommandLine:
10
+ """Interactive command-line interface for FlowQuery.
11
+
12
+ Provides a REPL (Read-Eval-Print Loop) for executing FlowQuery statements
13
+ and displaying results.
14
+
15
+ Example:
16
+ cli = CommandLine()
17
+ cli.loop() # Starts interactive mode
18
+ """
19
+
20
+ def loop(self) -> None:
21
+ """Starts the interactive command loop.
22
+
23
+ Prompts the user for FlowQuery statements, executes them, and displays results.
24
+ Type "exit" to quit the loop. End multi-line queries with ";".
25
+ """
26
+ print('Welcome to FlowQuery! Type "exit" to quit.')
27
+ print('End queries with ";" to execute. Multi-line input supported.')
28
+
29
+ while True:
30
+ try:
31
+ lines = []
32
+ prompt = "> "
33
+ while True:
34
+ line = input(prompt)
35
+ if line.strip() == "exit":
36
+ print("Exiting FlowQuery.")
37
+ return
38
+ lines.append(line)
39
+ user_input = "\n".join(lines)
40
+ if user_input.strip().endswith(";"):
41
+ break
42
+ prompt = "... "
43
+ except EOFError:
44
+ break
45
+
46
+ if user_input.strip() == "":
47
+ continue
48
+
49
+ # Remove the termination semicolon before sending to the engine
50
+ user_input = user_input.strip().rstrip(";")
51
+
52
+ try:
53
+ runner = Runner(user_input)
54
+ asyncio.run(self._execute(runner))
55
+ except Exception as e:
56
+ print(f"Error: {e}")
57
+
58
+ print("Exiting FlowQuery.")
59
+
60
+ async def _execute(self, runner: Runner) -> None:
61
+ await runner.run()
62
+ print(runner.results)
63
+
64
+
65
+ def main() -> None:
66
+ """Entry point for the flowquery CLI command."""
67
+ CommandLine().loop()
@@ -0,0 +1,17 @@
1
+ """Parsing module for FlowQuery."""
2
+
3
+ from .ast_node import ASTNode
4
+ from .context import Context
5
+ from .alias import Alias
6
+ from .alias_option import AliasOption
7
+ from .base_parser import BaseParser
8
+ from .parser import Parser
9
+
10
+ __all__ = [
11
+ "ASTNode",
12
+ "Context",
13
+ "Alias",
14
+ "AliasOption",
15
+ "BaseParser",
16
+ "Parser",
17
+ ]
@@ -0,0 +1,20 @@
1
+ """Alias node for FlowQuery AST."""
2
+
3
+ from .ast_node import ASTNode
4
+
5
+
6
+ class Alias(ASTNode):
7
+ """Represents an alias in the FlowQuery AST."""
8
+
9
+ def __init__(self, alias: str):
10
+ super().__init__()
11
+ self._alias = alias
12
+
13
+ def __str__(self) -> str:
14
+ return f"Alias ({self._alias})"
15
+
16
+ def get_alias(self) -> str:
17
+ return self._alias
18
+
19
+ def value(self) -> str:
20
+ return self._alias
@@ -0,0 +1,11 @@
1
+ """Alias option enumeration for FlowQuery parsing."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class AliasOption(Enum):
7
+ """Enumeration of alias options for parsing."""
8
+
9
+ NOT_ALLOWED = 0
10
+ OPTIONAL = 1
11
+ REQUIRED = 2
@@ -0,0 +1,146 @@
1
+ """Represents a node in the Abstract Syntax Tree (AST)."""
2
+
3
+ from __future__ import annotations
4
+ from typing import List, Any, Generator, Optional
5
+
6
+
7
+ class ASTNode:
8
+ """Represents a node in the Abstract Syntax Tree (AST).
9
+
10
+ The AST is a tree representation of the parsed FlowQuery statement structure.
11
+ Each node can have children and maintains a reference to its parent.
12
+
13
+ Example:
14
+ root = ASTNode()
15
+ child = ASTNode()
16
+ root.add_child(child)
17
+ """
18
+
19
+ def __init__(self):
20
+ self._parent: Optional[ASTNode] = None
21
+ self.children: List[ASTNode] = []
22
+
23
+ def add_child(self, child: ASTNode) -> None:
24
+ """Adds a child node to this node and sets the child's parent reference.
25
+
26
+ Args:
27
+ child: The child node to add
28
+ """
29
+ child._parent = self
30
+ self.children.append(child)
31
+
32
+ def first_child(self) -> ASTNode:
33
+ """Returns the first child node.
34
+
35
+ Returns:
36
+ The first child node
37
+
38
+ Raises:
39
+ ValueError: If the node has no children
40
+ """
41
+ if len(self.children) == 0:
42
+ raise ValueError('Expected child')
43
+ return self.children[0]
44
+
45
+ def last_child(self) -> ASTNode:
46
+ """Returns the last child node.
47
+
48
+ Returns:
49
+ The last child node
50
+
51
+ Raises:
52
+ ValueError: If the node has no children
53
+ """
54
+ if len(self.children) == 0:
55
+ raise ValueError('Expected child')
56
+ return self.children[-1]
57
+
58
+ def get_children(self) -> List[ASTNode]:
59
+ """Returns all child nodes.
60
+
61
+ Returns:
62
+ Array of child nodes
63
+ """
64
+ return self.children
65
+
66
+ def child_count(self) -> int:
67
+ """Returns the number of child nodes.
68
+
69
+ Returns:
70
+ The count of children
71
+ """
72
+ return len(self.children)
73
+
74
+ def value(self) -> Any:
75
+ """Returns the value of this node. Override in subclasses to provide specific values.
76
+
77
+ Returns:
78
+ The node's value, or None if not applicable
79
+ """
80
+ return None
81
+
82
+ def is_operator(self) -> bool:
83
+ """Checks if this node represents an operator.
84
+
85
+ Returns:
86
+ True if this is an operator node, False otherwise
87
+ """
88
+ return False
89
+
90
+ def is_operand(self) -> bool:
91
+ """Checks if this node represents an operand (the opposite of an operator).
92
+
93
+ Returns:
94
+ True if this is an operand node, False otherwise
95
+ """
96
+ return not self.is_operator()
97
+
98
+ @property
99
+ def precedence(self) -> int:
100
+ """Gets the operator precedence for this node. Higher values indicate higher precedence.
101
+
102
+ Returns:
103
+ The precedence value (0 for non-operators)
104
+ """
105
+ return 0
106
+
107
+ @property
108
+ def left_associative(self) -> bool:
109
+ """Indicates whether this operator is left-associative.
110
+
111
+ Returns:
112
+ True if left-associative, False otherwise
113
+ """
114
+ return False
115
+
116
+ def print(self) -> str:
117
+ """Prints a string representation of the AST tree starting from this node.
118
+
119
+ Returns:
120
+ A formatted string showing the tree structure
121
+ """
122
+ return '\n'.join(self._print(0))
123
+
124
+ def _print(self, indent: int) -> Generator[str, None, None]:
125
+ """Generator function for recursively printing the tree structure.
126
+
127
+ Args:
128
+ indent: The current indentation level
129
+
130
+ Yields:
131
+ Lines representing each node in the tree
132
+ """
133
+ if indent == 0:
134
+ yield self.__class__.__name__
135
+ elif indent > 0:
136
+ yield '-' * indent + f' {self}'
137
+ for child in self.children:
138
+ yield from child._print(indent + 1)
139
+
140
+ def __str__(self) -> str:
141
+ """Returns a string representation of this node. Override in subclasses for custom formatting.
142
+
143
+ Returns:
144
+ The string representation
145
+ """
146
+ return self.__class__.__name__