flowquery 1.0.15 → 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.
- package/.github/workflows/python-publish.yml +97 -0
- package/dist/compute/runner.d.ts +3 -2
- package/dist/compute/runner.d.ts.map +1 -1
- package/dist/compute/runner.js +7 -7
- package/dist/compute/runner.js.map +1 -1
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/data.d.ts +31 -0
- package/dist/graph/data.d.ts.map +1 -0
- package/dist/graph/data.js +110 -0
- package/dist/graph/data.js.map +1 -0
- package/dist/graph/database.d.ts +20 -0
- package/dist/graph/database.d.ts.map +1 -0
- package/dist/graph/database.js +77 -0
- package/dist/graph/database.js.map +1 -0
- package/dist/graph/hops.d.ts +11 -0
- package/dist/graph/hops.d.ts.map +1 -0
- package/dist/graph/hops.js +25 -0
- package/dist/graph/hops.js.map +1 -0
- package/dist/graph/node.d.ts +35 -0
- package/dist/graph/node.d.ts.map +1 -0
- package/dist/graph/node.js +113 -0
- package/dist/graph/node.js.map +1 -0
- package/dist/graph/node_data.d.ts +11 -0
- package/dist/graph/node_data.d.ts.map +1 -0
- package/dist/graph/node_data.js +20 -0
- package/dist/graph/node_data.js.map +1 -0
- package/dist/graph/node_reference.d.ts +10 -0
- package/dist/graph/node_reference.d.ts.map +1 -0
- package/dist/graph/node_reference.js +52 -0
- package/dist/graph/node_reference.js.map +1 -0
- package/dist/graph/pattern.d.ts +18 -0
- package/dist/graph/pattern.d.ts.map +1 -0
- package/dist/graph/pattern.js +114 -0
- package/dist/graph/pattern.js.map +1 -0
- package/dist/graph/pattern_expression.d.ts +14 -0
- package/dist/graph/pattern_expression.d.ts.map +1 -0
- package/dist/graph/pattern_expression.js +58 -0
- package/dist/graph/pattern_expression.js.map +1 -0
- package/dist/graph/patterns.d.ts +11 -0
- package/dist/graph/patterns.d.ts.map +1 -0
- package/dist/graph/patterns.js +49 -0
- package/dist/graph/patterns.js.map +1 -0
- package/dist/graph/physical_node.d.ts +10 -0
- package/dist/graph/physical_node.d.ts.map +1 -0
- package/dist/graph/physical_node.js +40 -0
- package/dist/graph/physical_node.js.map +1 -0
- package/dist/graph/physical_relationship.d.ts +10 -0
- package/dist/graph/physical_relationship.d.ts.map +1 -0
- package/dist/graph/physical_relationship.js +40 -0
- package/dist/graph/physical_relationship.js.map +1 -0
- package/dist/graph/relationship.d.ts +40 -0
- package/dist/graph/relationship.d.ts.map +1 -0
- package/dist/graph/relationship.js +124 -0
- package/dist/graph/relationship.js.map +1 -0
- package/dist/graph/relationship_data.d.ts +12 -0
- package/dist/graph/relationship_data.d.ts.map +1 -0
- package/dist/graph/relationship_data.js +40 -0
- package/dist/graph/relationship_data.js.map +1 -0
- package/dist/graph/relationship_match_collector.d.ts +19 -0
- package/dist/graph/relationship_match_collector.d.ts.map +1 -0
- package/dist/graph/relationship_match_collector.js +55 -0
- package/dist/graph/relationship_match_collector.js.map +1 -0
- package/dist/graph/relationship_reference.d.ts +8 -0
- package/dist/graph/relationship_reference.d.ts.map +1 -0
- package/dist/graph/relationship_reference.js +37 -0
- package/dist/graph/relationship_reference.js.map +1 -0
- package/dist/parsing/base_parser.d.ts +1 -0
- package/dist/parsing/base_parser.d.ts.map +1 -1
- package/dist/parsing/base_parser.js +4 -1
- package/dist/parsing/base_parser.js.map +1 -1
- package/dist/parsing/context.d.ts +2 -2
- package/dist/parsing/context.js +5 -5
- package/dist/parsing/expressions/boolean.d.ts +8 -0
- package/dist/parsing/expressions/boolean.d.ts.map +1 -0
- package/dist/parsing/expressions/boolean.js +26 -0
- package/dist/parsing/expressions/boolean.js.map +1 -0
- package/dist/parsing/expressions/expression.d.ts +4 -1
- package/dist/parsing/expressions/expression.d.ts.map +1 -1
- package/dist/parsing/expressions/expression.js +15 -8
- package/dist/parsing/expressions/expression.js.map +1 -1
- package/dist/parsing/expressions/expression_map.d.ts +1 -0
- package/dist/parsing/expressions/expression_map.d.ts.map +1 -1
- package/dist/parsing/expressions/expression_map.js +3 -0
- package/dist/parsing/expressions/expression_map.js.map +1 -1
- package/dist/parsing/expressions/operator.d.ts +1 -1
- package/dist/parsing/expressions/operator.d.ts.map +1 -1
- package/dist/parsing/expressions/operator.js.map +1 -1
- package/dist/parsing/functions/function_factory.d.ts +13 -13
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +20 -18
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/operations/call.d.ts.map +1 -1
- package/dist/parsing/operations/call.js +3 -1
- package/dist/parsing/operations/call.js.map +1 -1
- package/dist/parsing/operations/create_node.d.ts +14 -0
- package/dist/parsing/operations/create_node.d.ts.map +1 -0
- package/dist/parsing/operations/create_node.js +51 -0
- package/dist/parsing/operations/create_node.js.map +1 -0
- package/dist/parsing/operations/create_relationship.d.ts +14 -0
- package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
- package/dist/parsing/operations/create_relationship.js +51 -0
- package/dist/parsing/operations/create_relationship.js.map +1 -0
- package/dist/parsing/operations/match.d.ts +15 -0
- package/dist/parsing/operations/match.d.ts.map +1 -0
- package/dist/parsing/operations/match.js +45 -0
- package/dist/parsing/operations/match.js.map +1 -0
- package/dist/parsing/operations/operation.d.ts +1 -0
- package/dist/parsing/operations/operation.d.ts.map +1 -1
- package/dist/parsing/operations/operation.js +6 -0
- package/dist/parsing/operations/operation.js.map +1 -1
- package/dist/parsing/operations/return.d.ts +1 -0
- package/dist/parsing/operations/return.d.ts.map +1 -1
- package/dist/parsing/operations/return.js +7 -1
- package/dist/parsing/operations/return.js.map +1 -1
- package/dist/parsing/operations/where.d.ts +1 -1
- package/dist/parsing/operations/where.d.ts.map +1 -1
- package/dist/parsing/operations/where.js +4 -0
- package/dist/parsing/operations/where.js.map +1 -1
- package/dist/parsing/parser.d.ts +10 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +344 -5
- package/dist/parsing/parser.js.map +1 -1
- package/dist/parsing/token_to_node.d.ts.map +1 -1
- package/dist/parsing/token_to_node.js +7 -0
- package/dist/parsing/token_to_node.js.map +1 -1
- package/dist/tokenization/keyword.d.ts +1 -0
- package/dist/tokenization/keyword.d.ts.map +1 -1
- package/dist/tokenization/keyword.js +1 -0
- package/dist/tokenization/keyword.js.map +1 -1
- package/dist/tokenization/token.d.ts +4 -0
- package/dist/tokenization/token.d.ts.map +1 -1
- package/dist/tokenization/token.js +14 -1
- package/dist/tokenization/token.js.map +1 -1
- package/dist/tokenization/token_type.d.ts +1 -0
- package/dist/tokenization/token_type.d.ts.map +1 -1
- package/dist/tokenization/token_type.js +1 -0
- package/dist/tokenization/token_type.js.map +1 -1
- package/dist/tokenization/tokenizer.d.ts +2 -1
- package/dist/tokenization/tokenizer.d.ts.map +1 -1
- package/dist/tokenization/tokenizer.js +25 -12
- package/dist/tokenization/tokenizer.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/README.md +166 -0
- package/flowquery-py/pyproject.toml +75 -0
- package/flowquery-py/setup_env.ps1 +92 -0
- package/flowquery-py/setup_env.sh +87 -0
- package/flowquery-py/src/__init__.py +34 -0
- package/flowquery-py/src/__main__.py +10 -0
- package/flowquery-py/src/compute/__init__.py +5 -0
- package/flowquery-py/src/compute/runner.py +60 -0
- package/flowquery-py/src/extensibility.py +52 -0
- package/flowquery-py/src/graph/__init__.py +31 -0
- package/flowquery-py/src/graph/data.py +118 -0
- package/flowquery-py/src/graph/database.py +82 -0
- package/flowquery-py/src/graph/hops.py +43 -0
- package/flowquery-py/src/graph/node.py +112 -0
- package/flowquery-py/src/graph/node_data.py +26 -0
- package/flowquery-py/src/graph/node_reference.py +49 -0
- package/flowquery-py/src/graph/pattern.py +125 -0
- package/flowquery-py/src/graph/pattern_expression.py +62 -0
- package/flowquery-py/src/graph/patterns.py +42 -0
- package/flowquery-py/src/graph/physical_node.py +40 -0
- package/flowquery-py/src/graph/physical_relationship.py +36 -0
- package/flowquery-py/src/graph/relationship.py +135 -0
- package/flowquery-py/src/graph/relationship_data.py +33 -0
- package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
- package/flowquery-py/src/graph/relationship_reference.py +21 -0
- package/flowquery-py/src/io/__init__.py +5 -0
- package/flowquery-py/src/io/command_line.py +67 -0
- package/flowquery-py/src/parsing/__init__.py +17 -0
- package/flowquery-py/src/parsing/alias.py +20 -0
- package/flowquery-py/src/parsing/alias_option.py +11 -0
- package/flowquery-py/src/parsing/ast_node.py +146 -0
- package/flowquery-py/src/parsing/base_parser.py +84 -0
- package/flowquery-py/src/parsing/components/__init__.py +19 -0
- package/flowquery-py/src/parsing/components/csv.py +8 -0
- package/flowquery-py/src/parsing/components/from_.py +10 -0
- package/flowquery-py/src/parsing/components/headers.py +12 -0
- package/flowquery-py/src/parsing/components/json.py +8 -0
- package/flowquery-py/src/parsing/components/null.py +10 -0
- package/flowquery-py/src/parsing/components/post.py +8 -0
- package/flowquery-py/src/parsing/components/text.py +8 -0
- package/flowquery-py/src/parsing/context.py +50 -0
- package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
- package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
- package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
- package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
- package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
- package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
- package/flowquery-py/src/parsing/expressions/expression.py +138 -0
- package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
- package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
- package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
- package/flowquery-py/src/parsing/expressions/number.py +32 -0
- package/flowquery-py/src/parsing/expressions/operator.py +169 -0
- package/flowquery-py/src/parsing/expressions/reference.py +47 -0
- package/flowquery-py/src/parsing/expressions/string.py +27 -0
- package/flowquery-py/src/parsing/functions/__init__.py +75 -0
- package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
- package/flowquery-py/src/parsing/functions/async_function.py +62 -0
- package/flowquery-py/src/parsing/functions/avg.py +55 -0
- package/flowquery-py/src/parsing/functions/collect.py +75 -0
- package/flowquery-py/src/parsing/functions/function.py +68 -0
- package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
- package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
- package/flowquery-py/src/parsing/functions/functions.py +59 -0
- package/flowquery-py/src/parsing/functions/join.py +47 -0
- package/flowquery-py/src/parsing/functions/keys.py +34 -0
- package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
- package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
- package/flowquery-py/src/parsing/functions/rand.py +28 -0
- package/flowquery-py/src/parsing/functions/range_.py +34 -0
- package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
- package/flowquery-py/src/parsing/functions/replace.py +37 -0
- package/flowquery-py/src/parsing/functions/round_.py +32 -0
- package/flowquery-py/src/parsing/functions/size.py +32 -0
- package/flowquery-py/src/parsing/functions/split.py +47 -0
- package/flowquery-py/src/parsing/functions/stringify.py +47 -0
- package/flowquery-py/src/parsing/functions/sum.py +51 -0
- package/flowquery-py/src/parsing/functions/to_json.py +33 -0
- package/flowquery-py/src/parsing/functions/type_.py +47 -0
- package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
- package/flowquery-py/src/parsing/logic/__init__.py +15 -0
- package/flowquery-py/src/parsing/logic/case.py +29 -0
- package/flowquery-py/src/parsing/logic/else_.py +12 -0
- package/flowquery-py/src/parsing/logic/end.py +8 -0
- package/flowquery-py/src/parsing/logic/then.py +12 -0
- package/flowquery-py/src/parsing/logic/when.py +10 -0
- package/flowquery-py/src/parsing/operations/__init__.py +35 -0
- package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
- package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
- package/flowquery-py/src/parsing/operations/call.py +74 -0
- package/flowquery-py/src/parsing/operations/create_node.py +34 -0
- package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
- package/flowquery-py/src/parsing/operations/group_by.py +130 -0
- package/flowquery-py/src/parsing/operations/limit.py +22 -0
- package/flowquery-py/src/parsing/operations/load.py +140 -0
- package/flowquery-py/src/parsing/operations/match.py +29 -0
- package/flowquery-py/src/parsing/operations/operation.py +69 -0
- package/flowquery-py/src/parsing/operations/projection.py +21 -0
- package/flowquery-py/src/parsing/operations/return_op.py +50 -0
- package/flowquery-py/src/parsing/operations/unwind.py +37 -0
- package/flowquery-py/src/parsing/operations/where.py +41 -0
- package/flowquery-py/src/parsing/operations/with_op.py +18 -0
- package/flowquery-py/src/parsing/parser.py +1011 -0
- package/flowquery-py/src/parsing/token_to_node.py +109 -0
- package/flowquery-py/src/tokenization/__init__.py +23 -0
- package/flowquery-py/src/tokenization/keyword.py +48 -0
- package/flowquery-py/src/tokenization/operator.py +29 -0
- package/flowquery-py/src/tokenization/string_walker.py +158 -0
- package/flowquery-py/src/tokenization/symbol.py +19 -0
- package/flowquery-py/src/tokenization/token.py +659 -0
- package/flowquery-py/src/tokenization/token_mapper.py +52 -0
- package/flowquery-py/src/tokenization/token_type.py +21 -0
- package/flowquery-py/src/tokenization/tokenizer.py +214 -0
- package/flowquery-py/src/tokenization/trie.py +124 -0
- package/flowquery-py/src/utils/__init__.py +6 -0
- package/flowquery-py/src/utils/object_utils.py +20 -0
- package/flowquery-py/src/utils/string_utils.py +113 -0
- package/flowquery-py/tests/__init__.py +1 -0
- package/flowquery-py/tests/compute/__init__.py +1 -0
- package/flowquery-py/tests/compute/test_runner.py +1335 -0
- package/flowquery-py/tests/graph/__init__.py +1 -0
- package/flowquery-py/tests/graph/test_create.py +56 -0
- package/flowquery-py/tests/graph/test_data.py +73 -0
- package/flowquery-py/tests/graph/test_match.py +40 -0
- package/flowquery-py/tests/parsing/__init__.py +1 -0
- package/flowquery-py/tests/parsing/test_context.py +34 -0
- package/flowquery-py/tests/parsing/test_expression.py +49 -0
- package/flowquery-py/tests/parsing/test_parser.py +674 -0
- package/flowquery-py/tests/test_extensibility.py +611 -0
- package/flowquery-py/tests/tokenization/__init__.py +1 -0
- package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
- package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
- package/flowquery-py/tests/tokenization/test_trie.py +30 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/package.json +1 -1
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
- package/misc/apps/RAG/src/components/index.ts +19 -10
- package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +21 -26
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +24 -25
- package/misc/apps/RAG/src/plugins/loaders/Form.ts +163 -147
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +103 -90
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +80 -130
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +104 -101
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +47 -36
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +89 -78
- package/package.json +1 -1
- package/src/compute/runner.ts +24 -19
- package/src/graph/data.ts +112 -0
- package/src/graph/database.ts +63 -0
- package/src/graph/hops.ts +22 -0
- package/src/graph/node.ts +99 -0
- package/src/graph/node_data.ts +18 -0
- package/src/graph/node_reference.ts +33 -0
- package/src/graph/pattern.ts +101 -0
- package/src/graph/pattern_expression.ts +37 -0
- package/src/graph/patterns.ts +36 -0
- package/src/graph/physical_node.ts +23 -0
- package/src/graph/physical_relationship.ts +23 -0
- package/src/graph/relationship.ts +116 -0
- package/src/graph/relationship_data.ts +27 -0
- package/src/graph/relationship_match_collector.ts +58 -0
- package/src/graph/relationship_reference.ts +24 -0
- package/src/parsing/base_parser.ts +20 -14
- package/src/parsing/context.ts +14 -14
- package/src/parsing/expressions/boolean.ts +21 -0
- package/src/parsing/expressions/expression.ts +34 -26
- package/src/parsing/expressions/expression_map.ts +3 -0
- package/src/parsing/expressions/operator.ts +19 -1
- package/src/parsing/functions/function_factory.ts +45 -45
- package/src/parsing/operations/call.ts +3 -1
- package/src/parsing/operations/create_node.ts +39 -0
- package/src/parsing/operations/create_relationship.ts +38 -0
- package/src/parsing/operations/match.ts +31 -0
- package/src/parsing/operations/operation.ts +3 -0
- package/src/parsing/operations/return.ts +11 -7
- package/src/parsing/operations/where.ts +10 -6
- package/src/parsing/parser.ts +346 -8
- package/src/parsing/token_to_node.ts +6 -0
- package/src/tokenization/keyword.ts +41 -40
- package/src/tokenization/token.ts +21 -1
- package/src/tokenization/token_type.ts +2 -1
- package/src/tokenization/tokenizer.ts +52 -31
- package/tests/compute/runner.test.ts +660 -6
- package/tests/extensibility.test.ts +97 -93
- package/tests/graph/create.test.ts +36 -0
- package/tests/graph/data.test.ts +58 -0
- package/tests/graph/match.test.ts +29 -0
- package/tests/parsing/parser.test.ts +276 -8
- package/tests/tokenization/tokenizer.test.ts +107 -17
|
@@ -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()
|