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,68 @@
|
|
|
1
|
+
"""Base class for all functions in FlowQuery."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional, Any
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Function(ASTNode):
|
|
9
|
+
"""Base class for all functions in FlowQuery.
|
|
10
|
+
|
|
11
|
+
Functions can have parameters and may support the DISTINCT modifier.
|
|
12
|
+
Subclasses implement specific function logic.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
func = FunctionFactory.create("sum")
|
|
16
|
+
func.parameters = [expression1, expression2]
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, name: Optional[str] = None):
|
|
20
|
+
"""Creates a new Function with the given name.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
name: The function name
|
|
24
|
+
"""
|
|
25
|
+
super().__init__()
|
|
26
|
+
self._name = name or self.__class__.__name__
|
|
27
|
+
self._expected_parameter_count: Optional[int] = None
|
|
28
|
+
self._supports_distinct: bool = False
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def parameters(self) -> List[ASTNode]:
|
|
32
|
+
"""Gets the function parameters."""
|
|
33
|
+
return self.children
|
|
34
|
+
|
|
35
|
+
@parameters.setter
|
|
36
|
+
def parameters(self, nodes: List[ASTNode]) -> None:
|
|
37
|
+
"""Sets the function parameters.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
nodes: Array of AST nodes representing the function arguments
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If the number of parameters doesn't match expected count
|
|
44
|
+
"""
|
|
45
|
+
if self._expected_parameter_count is not None and self._expected_parameter_count != len(nodes):
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f"Function {self._name} expected {self._expected_parameter_count} parameters, "
|
|
48
|
+
f"but got {len(nodes)}"
|
|
49
|
+
)
|
|
50
|
+
self.children = nodes
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def name(self) -> str:
|
|
54
|
+
return self._name
|
|
55
|
+
|
|
56
|
+
def __str__(self) -> str:
|
|
57
|
+
return f"Function ({self._name})"
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def distinct(self) -> bool:
|
|
61
|
+
return self._supports_distinct
|
|
62
|
+
|
|
63
|
+
@distinct.setter
|
|
64
|
+
def distinct(self, value: bool) -> None:
|
|
65
|
+
if self._supports_distinct:
|
|
66
|
+
self._supports_distinct = value
|
|
67
|
+
else:
|
|
68
|
+
raise ValueError(f"Function {self._name} does not support distinct")
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Factory for creating function instances by name."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from .function import Function
|
|
6
|
+
from .async_function import AsyncFunction
|
|
7
|
+
from .predicate_function import PredicateFunction
|
|
8
|
+
from .function_metadata import (
|
|
9
|
+
FunctionMetadata,
|
|
10
|
+
get_function_metadata,
|
|
11
|
+
get_registered_function_factory,
|
|
12
|
+
get_registered_function_metadata,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FunctionFactory:
|
|
17
|
+
"""Factory for creating function instances by name.
|
|
18
|
+
|
|
19
|
+
All functions are registered via the @FunctionDef decorator.
|
|
20
|
+
Maps function names (case-insensitive) to their corresponding implementation classes.
|
|
21
|
+
Supports built-in functions like sum, avg, collect, range, split, join, etc.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
sum_func = FunctionFactory.create("sum")
|
|
25
|
+
avg_func = FunctionFactory.create("AVG")
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def get_async_provider(name: str) -> Optional[Callable]:
|
|
30
|
+
"""Gets an async data provider by name.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
name: The function name (case-insensitive)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The async data provider, or None if not found
|
|
37
|
+
"""
|
|
38
|
+
return get_registered_function_factory(name.lower())
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def is_async_provider(name: str) -> bool:
|
|
42
|
+
"""Checks if a function name is registered as an async data provider.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
name: The function name (case-insensitive)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
True if the function is an async data provider
|
|
49
|
+
"""
|
|
50
|
+
return get_registered_function_factory(name.lower(), "async") is not None
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def get_metadata(name: str) -> Optional[FunctionMetadata]:
|
|
54
|
+
"""Gets metadata for a specific function.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name: The function name (case-insensitive)
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The function metadata, or None if not found
|
|
61
|
+
"""
|
|
62
|
+
return get_function_metadata(name.lower())
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def list_functions(
|
|
66
|
+
category: Optional[str] = None,
|
|
67
|
+
async_only: bool = False,
|
|
68
|
+
sync_only: bool = False
|
|
69
|
+
) -> List[FunctionMetadata]:
|
|
70
|
+
"""Lists all registered functions with their metadata.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
category: Optional category filter
|
|
74
|
+
async_only: If True, only return async functions
|
|
75
|
+
sync_only: If True, only return sync functions
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Array of function metadata
|
|
79
|
+
"""
|
|
80
|
+
result: List[FunctionMetadata] = []
|
|
81
|
+
|
|
82
|
+
for meta in get_registered_function_metadata():
|
|
83
|
+
if category and meta.category != category:
|
|
84
|
+
continue
|
|
85
|
+
if async_only and meta.category != "async":
|
|
86
|
+
continue
|
|
87
|
+
if sync_only and meta.category == "async":
|
|
88
|
+
continue
|
|
89
|
+
result.append(meta)
|
|
90
|
+
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def list_function_names() -> List[str]:
|
|
95
|
+
"""Lists all registered function names.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Array of function names
|
|
99
|
+
"""
|
|
100
|
+
return [m.name for m in get_registered_function_metadata()]
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def to_json() -> Dict[str, Any]:
|
|
104
|
+
"""Gets all function metadata as a JSON-serializable object for LLM consumption.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Object with functions grouped by category
|
|
108
|
+
"""
|
|
109
|
+
functions = FunctionFactory.list_functions()
|
|
110
|
+
categories = list(set(f.category for f in functions if f.category))
|
|
111
|
+
return {"functions": functions, "categories": categories}
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def create(name: str) -> Function:
|
|
115
|
+
"""Creates a function instance by name.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
name: The function name (case-insensitive)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A Function instance of the appropriate type
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
ValueError: If the function name is not registered
|
|
125
|
+
"""
|
|
126
|
+
lower_name = name.lower()
|
|
127
|
+
|
|
128
|
+
# Check decorator-registered functions
|
|
129
|
+
decorator_factory = get_registered_function_factory(lower_name)
|
|
130
|
+
if decorator_factory:
|
|
131
|
+
return decorator_factory()
|
|
132
|
+
|
|
133
|
+
raise ValueError(f"Unknown function: {name}")
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def create_predicate(name: str) -> PredicateFunction:
|
|
137
|
+
"""Creates a predicate function instance by name.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
name: The function name (case-insensitive)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
A PredicateFunction instance of the appropriate type
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: If the predicate function name is not registered
|
|
147
|
+
"""
|
|
148
|
+
lower_name = name.lower()
|
|
149
|
+
|
|
150
|
+
decorator_factory = get_registered_function_factory(lower_name, "predicate")
|
|
151
|
+
if decorator_factory:
|
|
152
|
+
return decorator_factory()
|
|
153
|
+
|
|
154
|
+
raise ValueError(f"Unknown predicate function: {name}")
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def create_async(name: str) -> AsyncFunction:
|
|
158
|
+
"""Creates an async function instance by name.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
name: The function name (case-insensitive)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
An AsyncFunction instance of the appropriate type
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If the async function name is not registered
|
|
168
|
+
"""
|
|
169
|
+
lower_name = name.lower()
|
|
170
|
+
decorator_factory = get_registered_function_factory(lower_name, "async")
|
|
171
|
+
if decorator_factory:
|
|
172
|
+
return decorator_factory()
|
|
173
|
+
raise ValueError(f"Unknown async function: {name}")
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Function metadata and decorator for FlowQuery functions."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, TypedDict, Union
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Type definitions
|
|
8
|
+
FunctionCategory = str # "scalar" | "aggregate" | "predicate" | "async" | string
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ParameterSchema(TypedDict, total=False):
|
|
12
|
+
"""Schema definition for function arguments."""
|
|
13
|
+
name: str
|
|
14
|
+
description: str
|
|
15
|
+
type: str # "string" | "number" | "boolean" | "object" | "array" | "null"
|
|
16
|
+
required: bool
|
|
17
|
+
default: Any
|
|
18
|
+
items: Dict[str, Any]
|
|
19
|
+
properties: Dict[str, Any]
|
|
20
|
+
enum: List[Any]
|
|
21
|
+
example: Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OutputSchema(TypedDict, total=False):
|
|
25
|
+
"""Schema definition for function output."""
|
|
26
|
+
description: str
|
|
27
|
+
type: str
|
|
28
|
+
items: Dict[str, Any]
|
|
29
|
+
properties: Dict[str, Any]
|
|
30
|
+
example: Any
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class FunctionMetadata:
|
|
35
|
+
"""Metadata for a registered function, designed for LLM consumption."""
|
|
36
|
+
name: str
|
|
37
|
+
description: str
|
|
38
|
+
category: FunctionCategory
|
|
39
|
+
parameters: List[ParameterSchema]
|
|
40
|
+
output: OutputSchema
|
|
41
|
+
examples: Optional[List[str]] = None
|
|
42
|
+
notes: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FunctionDefOptions(TypedDict, total=False):
|
|
46
|
+
"""Decorator options - metadata without the name (derived from class)."""
|
|
47
|
+
description: str
|
|
48
|
+
category: FunctionCategory
|
|
49
|
+
parameters: List[ParameterSchema]
|
|
50
|
+
output: OutputSchema
|
|
51
|
+
examples: List[str]
|
|
52
|
+
notes: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FunctionRegistry:
|
|
56
|
+
"""Centralized registry for function metadata, factories, and async providers."""
|
|
57
|
+
|
|
58
|
+
_metadata: Dict[str, FunctionMetadata] = {}
|
|
59
|
+
_factories: Dict[str, Callable[[], Any]] = {}
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def register(cls, constructor: type, options: FunctionDefOptions) -> None:
|
|
63
|
+
"""Registers a regular function class."""
|
|
64
|
+
instance = constructor()
|
|
65
|
+
display_name = getattr(instance, 'name', constructor.__name__).lower()
|
|
66
|
+
category = options.get('category', '')
|
|
67
|
+
registry_key = f"{display_name}:{category}" if category else display_name
|
|
68
|
+
|
|
69
|
+
metadata = FunctionMetadata(
|
|
70
|
+
name=display_name,
|
|
71
|
+
description=options.get('description', ''),
|
|
72
|
+
category=options.get('category', 'scalar'),
|
|
73
|
+
parameters=options.get('parameters', []),
|
|
74
|
+
output=options.get('output', {'description': '', 'type': 'any'}),
|
|
75
|
+
examples=options.get('examples'),
|
|
76
|
+
notes=options.get('notes'),
|
|
77
|
+
)
|
|
78
|
+
cls._metadata[registry_key] = metadata
|
|
79
|
+
|
|
80
|
+
if category != 'predicate':
|
|
81
|
+
cls._factories[display_name] = lambda c=constructor: c()
|
|
82
|
+
cls._factories[registry_key] = lambda c=constructor: c()
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def get_all_metadata(cls) -> List[FunctionMetadata]:
|
|
86
|
+
return list(cls._metadata.values())
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def get_metadata(cls, name: str, category: Optional[str] = None) -> Optional[FunctionMetadata]:
|
|
90
|
+
lower_name = name.lower()
|
|
91
|
+
if category:
|
|
92
|
+
return cls._metadata.get(f"{lower_name}:{category}")
|
|
93
|
+
for meta in cls._metadata.values():
|
|
94
|
+
if meta.name.lower() == lower_name:
|
|
95
|
+
return meta
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def get_factory(cls, name: str, category: Optional[str] = None) -> Optional[Callable[[], Any]]:
|
|
100
|
+
lower_name = name.lower()
|
|
101
|
+
if category:
|
|
102
|
+
return cls._factories.get(f"{lower_name}:{category}")
|
|
103
|
+
return cls._factories.get(lower_name)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def FunctionDef(options: FunctionDefOptions):
|
|
107
|
+
"""Class decorator that registers function metadata.
|
|
108
|
+
|
|
109
|
+
The function name is derived from the class's constructor.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
options: Function metadata (excluding name)
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Class decorator
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
@FunctionDef({
|
|
119
|
+
'description': "Adds two numbers",
|
|
120
|
+
'category': "scalar",
|
|
121
|
+
'parameters': [
|
|
122
|
+
{'name': "a", 'description': "First number", 'type': "number"},
|
|
123
|
+
{'name': "b", 'description': "Second number", 'type': "number"}
|
|
124
|
+
],
|
|
125
|
+
'output': {'description': "Sum of a and b", 'type': "number"},
|
|
126
|
+
})
|
|
127
|
+
class AddFunction(Function):
|
|
128
|
+
def __init__(self):
|
|
129
|
+
super().__init__("add")
|
|
130
|
+
"""
|
|
131
|
+
def decorator(cls: type) -> type:
|
|
132
|
+
FunctionRegistry.register(cls, options)
|
|
133
|
+
return cls
|
|
134
|
+
return decorator
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_registered_function_metadata() -> List[FunctionMetadata]:
|
|
138
|
+
"""Gets all registered function metadata from decorators."""
|
|
139
|
+
return FunctionRegistry.get_all_metadata()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_registered_function_factory(name: str, category: Optional[str] = None) -> Optional[Callable[[], Any]]:
|
|
143
|
+
"""Gets a registered function factory by name."""
|
|
144
|
+
return FunctionRegistry.get_factory(name, category)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_function_metadata(name: str, category: Optional[str] = None) -> Optional[FunctionMetadata]:
|
|
148
|
+
"""Gets metadata for a specific function by name."""
|
|
149
|
+
return FunctionRegistry.get_metadata(name, category)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Functions introspection function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from .function import Function
|
|
6
|
+
from .function_factory import FunctionFactory
|
|
7
|
+
from .function_metadata import FunctionDef
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@FunctionDef({
|
|
11
|
+
"description": "Lists all registered functions with their metadata. Useful for discovering available functions and their documentation.",
|
|
12
|
+
"category": "scalar",
|
|
13
|
+
"parameters": [
|
|
14
|
+
{"name": "category", "description": "Optional category to filter by (e.g., 'aggregation', 'string', 'math')", "type": "string", "required": False}
|
|
15
|
+
],
|
|
16
|
+
"output": {
|
|
17
|
+
"description": "Array of function metadata objects",
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"name": {"description": "Function name", "type": "string"},
|
|
23
|
+
"description": {"description": "What the function does", "type": "string"},
|
|
24
|
+
"category": {"description": "Function category", "type": "string"},
|
|
25
|
+
"parameters": {"description": "Array of parameter definitions", "type": "array"},
|
|
26
|
+
"output": {"description": "Output schema", "type": "object"},
|
|
27
|
+
"examples": {"description": "Usage examples", "type": "array"}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"examples": [
|
|
32
|
+
"WITH functions() AS funcs RETURN funcs",
|
|
33
|
+
"WITH functions('aggregation') AS funcs UNWIND funcs AS f RETURN f.name, f.description"
|
|
34
|
+
]
|
|
35
|
+
})
|
|
36
|
+
class Functions(Function):
|
|
37
|
+
"""Functions introspection function.
|
|
38
|
+
|
|
39
|
+
Lists all registered functions with their metadata.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
super().__init__("functions")
|
|
44
|
+
self._expected_parameter_count = None # 0 or 1 parameter
|
|
45
|
+
|
|
46
|
+
def value(self) -> Any:
|
|
47
|
+
children = self.get_children()
|
|
48
|
+
|
|
49
|
+
if len(children) == 0:
|
|
50
|
+
# Return all functions
|
|
51
|
+
return FunctionFactory.list_functions()
|
|
52
|
+
elif len(children) == 1:
|
|
53
|
+
# Filter by category
|
|
54
|
+
category = children[0].value()
|
|
55
|
+
if isinstance(category, str):
|
|
56
|
+
return FunctionFactory.list_functions(category=category)
|
|
57
|
+
raise ValueError("functions() category parameter must be a string")
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError("functions() takes 0 or 1 parameters")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Join function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from .function import Function
|
|
6
|
+
from ..ast_node import ASTNode
|
|
7
|
+
from ..expressions.string import String
|
|
8
|
+
from .function_metadata import FunctionDef
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@FunctionDef({
|
|
12
|
+
"description": "Joins an array of strings with a delimiter",
|
|
13
|
+
"category": "scalar",
|
|
14
|
+
"parameters": [
|
|
15
|
+
{"name": "array", "description": "Array of values to join", "type": "array"},
|
|
16
|
+
{"name": "delimiter", "description": "Delimiter to join with", "type": "string"}
|
|
17
|
+
],
|
|
18
|
+
"output": {"description": "Joined string", "type": "string", "example": "a,b,c"},
|
|
19
|
+
"examples": ["WITH ['a', 'b', 'c'] AS arr RETURN join(arr, ',')"]
|
|
20
|
+
})
|
|
21
|
+
class Join(Function):
|
|
22
|
+
"""Join function.
|
|
23
|
+
|
|
24
|
+
Joins an array of strings with a delimiter.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__("join")
|
|
29
|
+
self._expected_parameter_count = 2
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def parameters(self) -> List[ASTNode]:
|
|
33
|
+
return self.get_children()
|
|
34
|
+
|
|
35
|
+
@parameters.setter
|
|
36
|
+
def parameters(self, nodes: List[ASTNode]) -> None:
|
|
37
|
+
if len(nodes) == 1:
|
|
38
|
+
nodes.append(String(""))
|
|
39
|
+
for node in nodes:
|
|
40
|
+
self.add_child(node)
|
|
41
|
+
|
|
42
|
+
def value(self) -> Any:
|
|
43
|
+
array = self.get_children()[0].value()
|
|
44
|
+
delimiter = self.get_children()[1].value()
|
|
45
|
+
if not isinstance(array, list) or not isinstance(delimiter, str):
|
|
46
|
+
raise ValueError("Invalid arguments for join function")
|
|
47
|
+
return delimiter.join(str(item) for item in array)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Keys function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from .function import Function
|
|
6
|
+
from .function_metadata import FunctionDef
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@FunctionDef({
|
|
10
|
+
"description": "Returns the keys of an object (associative array) as an array",
|
|
11
|
+
"category": "scalar",
|
|
12
|
+
"parameters": [
|
|
13
|
+
{"name": "object", "description": "Object to extract keys from", "type": "object"}
|
|
14
|
+
],
|
|
15
|
+
"output": {"description": "Array of keys", "type": "array", "example": ["name", "age"]},
|
|
16
|
+
"examples": ["WITH { name: 'Alice', age: 30 } AS obj RETURN keys(obj)"]
|
|
17
|
+
})
|
|
18
|
+
class Keys(Function):
|
|
19
|
+
"""Keys function.
|
|
20
|
+
|
|
21
|
+
Returns the keys of an object (associative array) as an array.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
super().__init__("keys")
|
|
26
|
+
self._expected_parameter_count = 1
|
|
27
|
+
|
|
28
|
+
def value(self) -> Any:
|
|
29
|
+
obj = self.get_children()[0].value()
|
|
30
|
+
if obj is None:
|
|
31
|
+
return []
|
|
32
|
+
if not isinstance(obj, dict):
|
|
33
|
+
raise ValueError("keys() expects an object, not an array or primitive")
|
|
34
|
+
return list(obj.keys())
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Base class for predicate functions in FlowQuery."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from ..ast_node import ASTNode
|
|
6
|
+
from ..expressions.expression import Expression
|
|
7
|
+
from ..expressions.reference import Reference
|
|
8
|
+
from .value_holder import ValueHolder
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PredicateFunction(ASTNode):
|
|
12
|
+
"""Base class for predicate functions."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, name: Optional[str] = None):
|
|
15
|
+
super().__init__()
|
|
16
|
+
self._name = name or self.__class__.__name__
|
|
17
|
+
self._value_holder = ValueHolder()
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self) -> str:
|
|
21
|
+
return self._name
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def reference(self) -> Reference:
|
|
25
|
+
return self.first_child()
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def array(self) -> ASTNode:
|
|
29
|
+
return self.get_children()[1].first_child()
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def _return(self) -> Expression:
|
|
33
|
+
return self.get_children()[2]
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def where(self) -> Optional['Where']:
|
|
37
|
+
from ..operations.where import Where
|
|
38
|
+
if len(self.get_children()) == 4:
|
|
39
|
+
return self.get_children()[3]
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
def value(self) -> Any:
|
|
43
|
+
raise NotImplementedError("Method not implemented.")
|
|
44
|
+
|
|
45
|
+
def __str__(self) -> str:
|
|
46
|
+
return f"PredicateFunction ({self._name})"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""PredicateSum function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List, Optional
|
|
4
|
+
|
|
5
|
+
from .predicate_function import PredicateFunction
|
|
6
|
+
from .function_metadata import FunctionDef
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@FunctionDef({
|
|
10
|
+
"description": "Calculates the sum of values in an array with optional filtering. Uses list comprehension syntax: sum(variable IN array [WHERE condition] | expression)",
|
|
11
|
+
"category": "predicate",
|
|
12
|
+
"parameters": [
|
|
13
|
+
{"name": "variable", "description": "Variable name to bind each element", "type": "string"},
|
|
14
|
+
{"name": "array", "description": "Array to iterate over", "type": "array"},
|
|
15
|
+
{"name": "expression", "description": "Expression to sum for each element", "type": "any"},
|
|
16
|
+
{"name": "where", "description": "Optional filter condition", "type": "boolean", "required": False}
|
|
17
|
+
],
|
|
18
|
+
"output": {"description": "Sum of the evaluated expressions", "type": "number", "example": 6},
|
|
19
|
+
"examples": [
|
|
20
|
+
"WITH [1, 2, 3] AS nums RETURN sum(n IN nums | n)",
|
|
21
|
+
"WITH [1, 2, 3, 4] AS nums RETURN sum(n IN nums WHERE n > 1 | n * 2)"
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
class PredicateSum(PredicateFunction):
|
|
25
|
+
"""PredicateSum function.
|
|
26
|
+
|
|
27
|
+
Calculates the sum of values in an array with optional filtering.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
super().__init__("sum")
|
|
32
|
+
|
|
33
|
+
def value(self) -> Any:
|
|
34
|
+
self.reference.referred = self._value_holder
|
|
35
|
+
array = self.array.value()
|
|
36
|
+
if array is None or not isinstance(array, list):
|
|
37
|
+
raise ValueError("Invalid array for sum function")
|
|
38
|
+
|
|
39
|
+
_sum: Optional[Any] = None
|
|
40
|
+
for item in array:
|
|
41
|
+
self._value_holder.holder = item
|
|
42
|
+
if self.where is None or self.where.value():
|
|
43
|
+
if _sum is None:
|
|
44
|
+
_sum = self._return.value()
|
|
45
|
+
else:
|
|
46
|
+
_sum += self._return.value()
|
|
47
|
+
return _sum
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Rand function."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .function import Function
|
|
7
|
+
from .function_metadata import FunctionDef
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@FunctionDef({
|
|
11
|
+
"description": "Generates a random number between 0 and 1",
|
|
12
|
+
"category": "scalar",
|
|
13
|
+
"parameters": [],
|
|
14
|
+
"output": {"description": "Random number between 0 and 1", "type": "number", "example": 0.7234},
|
|
15
|
+
"examples": ["WITH rand() AS r RETURN r"]
|
|
16
|
+
})
|
|
17
|
+
class Rand(Function):
|
|
18
|
+
"""Rand function.
|
|
19
|
+
|
|
20
|
+
Generates a random number between 0 and 1.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__("rand")
|
|
25
|
+
self._expected_parameter_count = 0
|
|
26
|
+
|
|
27
|
+
def value(self) -> Any:
|
|
28
|
+
return random.random()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Range function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from .function import Function
|
|
6
|
+
from .function_metadata import FunctionDef
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@FunctionDef({
|
|
10
|
+
"description": "Generates an array of sequential integers",
|
|
11
|
+
"category": "scalar",
|
|
12
|
+
"parameters": [
|
|
13
|
+
{"name": "start", "description": "Starting number (inclusive)", "type": "number"},
|
|
14
|
+
{"name": "end", "description": "Ending number (inclusive)", "type": "number"}
|
|
15
|
+
],
|
|
16
|
+
"output": {"description": "Array of integers from start to end", "type": "array", "items": {"type": "number"}, "example": [1, 2, 3, 4, 5]},
|
|
17
|
+
"examples": ["WITH range(1, 5) AS nums RETURN nums"]
|
|
18
|
+
})
|
|
19
|
+
class Range(Function):
|
|
20
|
+
"""Range function.
|
|
21
|
+
|
|
22
|
+
Generates an array of sequential integers.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
super().__init__("range")
|
|
27
|
+
self._expected_parameter_count = 2
|
|
28
|
+
|
|
29
|
+
def value(self) -> Any:
|
|
30
|
+
start = self.get_children()[0].value()
|
|
31
|
+
end = self.get_children()[1].value()
|
|
32
|
+
if not isinstance(start, (int, float)) or not isinstance(end, (int, float)):
|
|
33
|
+
raise ValueError("Invalid arguments for range function")
|
|
34
|
+
return list(range(int(start), int(end) + 1))
|