flowquery 1.0.34 → 1.0.36

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 (197) hide show
  1. package/dist/flowquery.min.js +1 -1
  2. package/dist/graph/database.d.ts +1 -0
  3. package/dist/graph/database.d.ts.map +1 -1
  4. package/dist/graph/database.js +43 -6
  5. package/dist/graph/database.js.map +1 -1
  6. package/dist/graph/relationship.d.ts +3 -1
  7. package/dist/graph/relationship.d.ts.map +1 -1
  8. package/dist/graph/relationship.js +12 -4
  9. package/dist/graph/relationship.js.map +1 -1
  10. package/dist/graph/relationship_data.js +1 -1
  11. package/dist/graph/relationship_data.js.map +1 -1
  12. package/dist/graph/relationship_match_collector.d.ts.map +1 -1
  13. package/dist/graph/relationship_match_collector.js +6 -3
  14. package/dist/graph/relationship_match_collector.js.map +1 -1
  15. package/dist/graph/relationship_reference.js +1 -1
  16. package/dist/graph/relationship_reference.js.map +1 -1
  17. package/dist/parsing/data_structures/lookup.d.ts.map +1 -1
  18. package/dist/parsing/data_structures/lookup.js +5 -1
  19. package/dist/parsing/data_structures/lookup.js.map +1 -1
  20. package/dist/parsing/functions/coalesce.d.ts +17 -0
  21. package/dist/parsing/functions/coalesce.d.ts.map +1 -0
  22. package/dist/parsing/functions/coalesce.js +61 -0
  23. package/dist/parsing/functions/coalesce.js.map +1 -0
  24. package/dist/parsing/functions/date.d.ts +22 -0
  25. package/dist/parsing/functions/date.d.ts.map +1 -0
  26. package/dist/parsing/functions/date.js +71 -0
  27. package/dist/parsing/functions/date.js.map +1 -0
  28. package/dist/parsing/functions/datetime.d.ts +22 -0
  29. package/dist/parsing/functions/datetime.d.ts.map +1 -0
  30. package/dist/parsing/functions/datetime.js +71 -0
  31. package/dist/parsing/functions/datetime.js.map +1 -0
  32. package/dist/parsing/functions/duration.d.ts +7 -0
  33. package/dist/parsing/functions/duration.d.ts.map +1 -0
  34. package/dist/parsing/functions/duration.js +145 -0
  35. package/dist/parsing/functions/duration.js.map +1 -0
  36. package/dist/parsing/functions/element_id.d.ts +7 -0
  37. package/dist/parsing/functions/element_id.d.ts.map +1 -0
  38. package/dist/parsing/functions/element_id.js +58 -0
  39. package/dist/parsing/functions/element_id.js.map +1 -0
  40. package/dist/parsing/functions/function_factory.d.ts +21 -0
  41. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  42. package/dist/parsing/functions/function_factory.js +21 -0
  43. package/dist/parsing/functions/function_factory.js.map +1 -1
  44. package/dist/parsing/functions/head.d.ts +7 -0
  45. package/dist/parsing/functions/head.d.ts.map +1 -0
  46. package/dist/parsing/functions/head.js +53 -0
  47. package/dist/parsing/functions/head.js.map +1 -0
  48. package/dist/parsing/functions/id.d.ts +7 -0
  49. package/dist/parsing/functions/id.d.ts.map +1 -0
  50. package/dist/parsing/functions/id.js +58 -0
  51. package/dist/parsing/functions/id.js.map +1 -0
  52. package/dist/parsing/functions/last.d.ts +7 -0
  53. package/dist/parsing/functions/last.d.ts.map +1 -0
  54. package/dist/parsing/functions/last.js +53 -0
  55. package/dist/parsing/functions/last.js.map +1 -0
  56. package/dist/parsing/functions/localdatetime.d.ts +21 -0
  57. package/dist/parsing/functions/localdatetime.d.ts.map +1 -0
  58. package/dist/parsing/functions/localdatetime.js +71 -0
  59. package/dist/parsing/functions/localdatetime.js.map +1 -0
  60. package/dist/parsing/functions/localtime.d.ts +20 -0
  61. package/dist/parsing/functions/localtime.d.ts.map +1 -0
  62. package/dist/parsing/functions/localtime.js +67 -0
  63. package/dist/parsing/functions/localtime.js.map +1 -0
  64. package/dist/parsing/functions/max.d.ts +14 -0
  65. package/dist/parsing/functions/max.d.ts.map +1 -0
  66. package/dist/parsing/functions/max.js +51 -0
  67. package/dist/parsing/functions/max.js.map +1 -0
  68. package/dist/parsing/functions/min.d.ts +14 -0
  69. package/dist/parsing/functions/min.d.ts.map +1 -0
  70. package/dist/parsing/functions/min.js +51 -0
  71. package/dist/parsing/functions/min.js.map +1 -0
  72. package/dist/parsing/functions/nodes.d.ts +7 -0
  73. package/dist/parsing/functions/nodes.d.ts.map +1 -0
  74. package/dist/parsing/functions/nodes.js +63 -0
  75. package/dist/parsing/functions/nodes.js.map +1 -0
  76. package/dist/parsing/functions/predicate_sum.d.ts.map +1 -1
  77. package/dist/parsing/functions/predicate_sum.js +13 -10
  78. package/dist/parsing/functions/predicate_sum.js.map +1 -1
  79. package/dist/parsing/functions/properties.d.ts +7 -0
  80. package/dist/parsing/functions/properties.d.ts.map +1 -0
  81. package/dist/parsing/functions/properties.js +74 -0
  82. package/dist/parsing/functions/properties.js.map +1 -0
  83. package/dist/parsing/functions/relationships.d.ts +7 -0
  84. package/dist/parsing/functions/relationships.d.ts.map +1 -0
  85. package/dist/parsing/functions/relationships.js +61 -0
  86. package/dist/parsing/functions/relationships.js.map +1 -0
  87. package/dist/parsing/functions/schema.d.ts +5 -2
  88. package/dist/parsing/functions/schema.d.ts.map +1 -1
  89. package/dist/parsing/functions/schema.js +7 -4
  90. package/dist/parsing/functions/schema.js.map +1 -1
  91. package/dist/parsing/functions/tail.d.ts +7 -0
  92. package/dist/parsing/functions/tail.d.ts.map +1 -0
  93. package/dist/parsing/functions/tail.js +50 -0
  94. package/dist/parsing/functions/tail.js.map +1 -0
  95. package/dist/parsing/functions/temporal_utils.d.ts +39 -0
  96. package/dist/parsing/functions/temporal_utils.d.ts.map +1 -0
  97. package/dist/parsing/functions/temporal_utils.js +168 -0
  98. package/dist/parsing/functions/temporal_utils.js.map +1 -0
  99. package/dist/parsing/functions/time.d.ts +20 -0
  100. package/dist/parsing/functions/time.d.ts.map +1 -0
  101. package/dist/parsing/functions/time.js +67 -0
  102. package/dist/parsing/functions/time.js.map +1 -0
  103. package/dist/parsing/functions/timestamp.d.ts +17 -0
  104. package/dist/parsing/functions/timestamp.d.ts.map +1 -0
  105. package/dist/parsing/functions/timestamp.js +51 -0
  106. package/dist/parsing/functions/timestamp.js.map +1 -0
  107. package/dist/parsing/functions/to_float.d.ts +7 -0
  108. package/dist/parsing/functions/to_float.d.ts.map +1 -0
  109. package/dist/parsing/functions/to_float.js +61 -0
  110. package/dist/parsing/functions/to_float.js.map +1 -0
  111. package/dist/parsing/functions/to_integer.d.ts +7 -0
  112. package/dist/parsing/functions/to_integer.d.ts.map +1 -0
  113. package/dist/parsing/functions/to_integer.js +61 -0
  114. package/dist/parsing/functions/to_integer.js.map +1 -0
  115. package/dist/parsing/functions/trim.d.ts +7 -0
  116. package/dist/parsing/functions/trim.d.ts.map +1 -0
  117. package/dist/parsing/functions/trim.js +37 -0
  118. package/dist/parsing/functions/trim.js.map +1 -0
  119. package/dist/parsing/operations/group_by.d.ts.map +1 -1
  120. package/dist/parsing/operations/group_by.js +4 -2
  121. package/dist/parsing/operations/group_by.js.map +1 -1
  122. package/dist/parsing/parser.d.ts.map +1 -1
  123. package/dist/parsing/parser.js +15 -2
  124. package/dist/parsing/parser.js.map +1 -1
  125. package/docs/flowquery.min.js +1 -1
  126. package/flowquery-py/pyproject.toml +1 -1
  127. package/flowquery-py/src/graph/database.py +44 -11
  128. package/flowquery-py/src/graph/relationship.py +11 -3
  129. package/flowquery-py/src/graph/relationship_data.py +2 -1
  130. package/flowquery-py/src/graph/relationship_match_collector.py +7 -1
  131. package/flowquery-py/src/graph/relationship_reference.py +2 -2
  132. package/flowquery-py/src/parsing/data_structures/lookup.py +2 -0
  133. package/flowquery-py/src/parsing/functions/__init__.py +42 -2
  134. package/flowquery-py/src/parsing/functions/coalesce.py +44 -0
  135. package/flowquery-py/src/parsing/functions/date_.py +63 -0
  136. package/flowquery-py/src/parsing/functions/datetime_.py +64 -0
  137. package/flowquery-py/src/parsing/functions/duration.py +159 -0
  138. package/flowquery-py/src/parsing/functions/element_id.py +50 -0
  139. package/flowquery-py/src/parsing/functions/head.py +39 -0
  140. package/flowquery-py/src/parsing/functions/id_.py +49 -0
  141. package/flowquery-py/src/parsing/functions/last.py +39 -0
  142. package/flowquery-py/src/parsing/functions/localdatetime.py +62 -0
  143. package/flowquery-py/src/parsing/functions/localtime.py +59 -0
  144. package/flowquery-py/src/parsing/functions/max_.py +49 -0
  145. package/flowquery-py/src/parsing/functions/min_.py +49 -0
  146. package/flowquery-py/src/parsing/functions/nodes.py +48 -0
  147. package/flowquery-py/src/parsing/functions/predicate_sum.py +3 -6
  148. package/flowquery-py/src/parsing/functions/properties.py +50 -0
  149. package/flowquery-py/src/parsing/functions/relationships.py +46 -0
  150. package/flowquery-py/src/parsing/functions/schema.py +9 -5
  151. package/flowquery-py/src/parsing/functions/tail.py +37 -0
  152. package/flowquery-py/src/parsing/functions/temporal_utils.py +186 -0
  153. package/flowquery-py/src/parsing/functions/time_.py +59 -0
  154. package/flowquery-py/src/parsing/functions/timestamp.py +39 -0
  155. package/flowquery-py/src/parsing/functions/to_float.py +46 -0
  156. package/flowquery-py/src/parsing/functions/to_integer.py +46 -0
  157. package/flowquery-py/src/parsing/functions/trim.py +35 -0
  158. package/flowquery-py/src/parsing/operations/group_by.py +2 -0
  159. package/flowquery-py/src/parsing/parser.py +12 -2
  160. package/flowquery-py/tests/compute/test_runner.py +1082 -4
  161. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  162. package/package.json +1 -1
  163. package/src/graph/database.ts +42 -4
  164. package/src/graph/relationship.ts +12 -4
  165. package/src/graph/relationship_data.ts +1 -1
  166. package/src/graph/relationship_match_collector.ts +6 -2
  167. package/src/graph/relationship_reference.ts +1 -1
  168. package/src/parsing/data_structures/lookup.ts +8 -4
  169. package/src/parsing/functions/coalesce.ts +50 -0
  170. package/src/parsing/functions/date.ts +65 -0
  171. package/src/parsing/functions/datetime.ts +65 -0
  172. package/src/parsing/functions/duration.ts +143 -0
  173. package/src/parsing/functions/element_id.ts +51 -0
  174. package/src/parsing/functions/function_factory.ts +21 -0
  175. package/src/parsing/functions/head.ts +42 -0
  176. package/src/parsing/functions/id.ts +51 -0
  177. package/src/parsing/functions/last.ts +42 -0
  178. package/src/parsing/functions/localdatetime.ts +65 -0
  179. package/src/parsing/functions/localtime.ts +60 -0
  180. package/src/parsing/functions/max.ts +37 -0
  181. package/src/parsing/functions/min.ts +37 -0
  182. package/src/parsing/functions/nodes.ts +54 -0
  183. package/src/parsing/functions/predicate_sum.ts +17 -12
  184. package/src/parsing/functions/properties.ts +56 -0
  185. package/src/parsing/functions/relationships.ts +52 -0
  186. package/src/parsing/functions/schema.ts +7 -4
  187. package/src/parsing/functions/tail.ts +39 -0
  188. package/src/parsing/functions/temporal_utils.ts +180 -0
  189. package/src/parsing/functions/time.ts +60 -0
  190. package/src/parsing/functions/timestamp.ts +41 -0
  191. package/src/parsing/functions/to_float.ts +50 -0
  192. package/src/parsing/functions/to_integer.ts +50 -0
  193. package/src/parsing/functions/trim.ts +25 -0
  194. package/src/parsing/operations/group_by.ts +4 -1
  195. package/src/parsing/parser.ts +15 -2
  196. package/tests/compute/runner.test.ts +1005 -3
  197. package/tests/parsing/parser.test.ts +37 -0
@@ -0,0 +1,39 @@
1
+ """Head function."""
2
+
3
+ from typing import Any
4
+
5
+ from .function import Function
6
+ from .function_metadata import FunctionDef
7
+
8
+
9
+ @FunctionDef({
10
+ "description": "Returns the first element of a list",
11
+ "category": "scalar",
12
+ "parameters": [
13
+ {"name": "list", "description": "The list to get the first element from", "type": "array"}
14
+ ],
15
+ "output": {"description": "The first element of the list", "type": "any", "example": "1"},
16
+ "examples": [
17
+ "RETURN head([1, 2, 3])",
18
+ "WITH ['a', 'b', 'c'] AS items RETURN head(items)"
19
+ ]
20
+ })
21
+ class Head(Function):
22
+ """Head function.
23
+
24
+ Returns the first element of a list.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ super().__init__("head")
29
+ self._expected_parameter_count = 1
30
+
31
+ def value(self) -> Any:
32
+ val = self.get_children()[0].value()
33
+ if val is None:
34
+ return None
35
+ if not isinstance(val, list):
36
+ raise ValueError("head() expects a list")
37
+ if len(val) == 0:
38
+ return None
39
+ return val[0]
@@ -0,0 +1,49 @@
1
+ """Id function."""
2
+
3
+ from typing import Any
4
+
5
+ from .function import Function
6
+ from .function_metadata import FunctionDef
7
+
8
+
9
+ @FunctionDef({
10
+ "description": (
11
+ "Returns the id of a node or relationship. "
12
+ "For nodes, returns the id property. For relationships, returns the type."
13
+ ),
14
+ "category": "scalar",
15
+ "parameters": [
16
+ {"name": "entity", "description": "A node or relationship to get the id from", "type": "object"}
17
+ ],
18
+ "output": {"description": "The id of the entity", "type": "any", "example": "1"},
19
+ "examples": [
20
+ "MATCH (n:Person) RETURN id(n)",
21
+ "MATCH (a)-[r]->(b) RETURN id(r)"
22
+ ]
23
+ })
24
+ class Id(Function):
25
+ """Id function.
26
+
27
+ Returns the id of a node or relationship.
28
+ """
29
+
30
+ def __init__(self) -> None:
31
+ super().__init__("id")
32
+ self._expected_parameter_count = 1
33
+
34
+ def value(self) -> Any:
35
+ obj = self.get_children()[0].value()
36
+ if obj is None:
37
+ return None
38
+ if not isinstance(obj, dict):
39
+ raise ValueError("id() expects a node or relationship")
40
+
41
+ # If it's a RelationshipMatchRecord (has type, startNode, endNode, properties)
42
+ if all(k in obj for k in ("type", "startNode", "endNode", "properties")):
43
+ return obj["type"]
44
+
45
+ # If it's a node record (has id field)
46
+ if "id" in obj:
47
+ return obj["id"]
48
+
49
+ raise ValueError("id() expects a node or relationship")
@@ -0,0 +1,39 @@
1
+ """Last function."""
2
+
3
+ from typing import Any
4
+
5
+ from .function import Function
6
+ from .function_metadata import FunctionDef
7
+
8
+
9
+ @FunctionDef({
10
+ "description": "Returns the last element of a list",
11
+ "category": "scalar",
12
+ "parameters": [
13
+ {"name": "list", "description": "The list to get the last element from", "type": "array"}
14
+ ],
15
+ "output": {"description": "The last element of the list", "type": "any", "example": "3"},
16
+ "examples": [
17
+ "RETURN last([1, 2, 3])",
18
+ "WITH ['a', 'b', 'c'] AS items RETURN last(items)"
19
+ ]
20
+ })
21
+ class Last(Function):
22
+ """Last function.
23
+
24
+ Returns the last element of a list.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ super().__init__("last")
29
+ self._expected_parameter_count = 1
30
+
31
+ def value(self) -> Any:
32
+ val = self.get_children()[0].value()
33
+ if val is None:
34
+ return None
35
+ if not isinstance(val, list):
36
+ raise ValueError("last() expects a list")
37
+ if len(val) == 0:
38
+ return None
39
+ return val[-1]
@@ -0,0 +1,62 @@
1
+ """Local datetime function."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from .function import Function
7
+ from .function_metadata import FunctionDef
8
+ from .temporal_utils import build_datetime_object, parse_temporal_arg
9
+
10
+
11
+ @FunctionDef({
12
+ "description": (
13
+ "Returns a local datetime value (no timezone). With no arguments returns the current local datetime. "
14
+ "Accepts an ISO 8601 string or a map of components."
15
+ ),
16
+ "category": "scalar",
17
+ "parameters": [
18
+ {
19
+ "name": "input",
20
+ "description": "Optional. An ISO 8601 datetime string or a map of components.",
21
+ "type": "string",
22
+ "required": False,
23
+ },
24
+ ],
25
+ "output": {
26
+ "description": (
27
+ "A datetime object with properties: year, month, day, hour, minute, second, millisecond, "
28
+ "epochMillis, epochSeconds, dayOfWeek, dayOfYear, quarter, formatted"
29
+ ),
30
+ "type": "object",
31
+ },
32
+ "examples": [
33
+ "RETURN localdatetime() AS now",
34
+ "RETURN localdatetime('2025-06-15T12:30:00') AS dt",
35
+ "WITH localdatetime() AS dt RETURN dt.hour, dt.minute",
36
+ ],
37
+ })
38
+ class LocalDatetime(Function):
39
+ """Local datetime function.
40
+
41
+ Returns a local datetime value (date + time, no timezone offset).
42
+ When called with no arguments, returns the current local datetime.
43
+ When called with a string argument, parses it as an ISO 8601 datetime.
44
+
45
+ Equivalent to Neo4j's localdatetime() function.
46
+ """
47
+
48
+ def __init__(self) -> None:
49
+ super().__init__("localdatetime")
50
+ self._expected_parameter_count = None
51
+
52
+ def value(self) -> Any:
53
+ children = self.get_children()
54
+ if len(children) > 1:
55
+ raise ValueError("localdatetime() accepts at most one argument")
56
+
57
+ if len(children) == 1:
58
+ d = parse_temporal_arg(children[0].value(), "localdatetime")
59
+ else:
60
+ d = datetime.now()
61
+
62
+ return build_datetime_object(d, utc=False)
@@ -0,0 +1,59 @@
1
+ """Local time function."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from .function import Function
7
+ from .function_metadata import FunctionDef
8
+ from .temporal_utils import build_time_object, parse_temporal_arg
9
+
10
+
11
+ @FunctionDef({
12
+ "description": (
13
+ "Returns a local time value (no timezone). With no arguments returns the current local time. "
14
+ "Accepts an ISO 8601 time string or a map of components."
15
+ ),
16
+ "category": "scalar",
17
+ "parameters": [
18
+ {
19
+ "name": "input",
20
+ "description": "Optional. An ISO 8601 time string (HH:MM:SS) or a map of components.",
21
+ "type": "string",
22
+ "required": False,
23
+ },
24
+ ],
25
+ "output": {
26
+ "description": "A time object with properties: hour, minute, second, millisecond, formatted",
27
+ "type": "object",
28
+ },
29
+ "examples": [
30
+ "RETURN localtime() AS now",
31
+ "RETURN localtime('14:30:00') AS t",
32
+ "WITH localtime() AS t RETURN t.hour, t.minute",
33
+ ],
34
+ })
35
+ class LocalTime(Function):
36
+ """Local time function.
37
+
38
+ Returns a local time value (no timezone offset).
39
+ When called with no arguments, returns the current local time.
40
+ When called with a string argument, parses it.
41
+
42
+ Equivalent to Neo4j's localtime() function.
43
+ """
44
+
45
+ def __init__(self) -> None:
46
+ super().__init__("localtime")
47
+ self._expected_parameter_count = None
48
+
49
+ def value(self) -> Any:
50
+ children = self.get_children()
51
+ if len(children) > 1:
52
+ raise ValueError("localtime() accepts at most one argument")
53
+
54
+ if len(children) == 1:
55
+ d = parse_temporal_arg(children[0].value(), "localtime")
56
+ else:
57
+ d = datetime.now()
58
+
59
+ return build_time_object(d, utc=False)
@@ -0,0 +1,49 @@
1
+ """Max aggregate function."""
2
+
3
+ from typing import Any
4
+
5
+ from .aggregate_function import AggregateFunction
6
+ from .function_metadata import FunctionDef
7
+ from .reducer_element import ReducerElement
8
+
9
+
10
+ class MaxReducerElement(ReducerElement):
11
+ """Reducer element for Max aggregate function."""
12
+
13
+ def __init__(self) -> None:
14
+ self._value: Any = None
15
+
16
+ @property
17
+ def value(self) -> Any:
18
+ return self._value
19
+
20
+ @value.setter
21
+ def value(self, val: Any) -> None:
22
+ if self._value is None or val > self._value:
23
+ self._value = val
24
+
25
+
26
+ @FunctionDef({
27
+ "description": "Returns the maximum value across grouped rows",
28
+ "category": "aggregate",
29
+ "parameters": [
30
+ {"name": "value", "description": "Value to compare", "type": "number"}
31
+ ],
32
+ "output": {"description": "Maximum value", "type": "number", "example": 10},
33
+ "examples": ["WITH [3, 1, 2] AS nums UNWIND nums AS n RETURN max(n)"]
34
+ })
35
+ class Max(AggregateFunction):
36
+ """Max aggregate function.
37
+
38
+ Returns the maximum value across grouped rows.
39
+ """
40
+
41
+ def __init__(self) -> None:
42
+ super().__init__("max")
43
+ self._expected_parameter_count = 1
44
+
45
+ def reduce(self, element: MaxReducerElement) -> None:
46
+ element.value = self.first_child().value()
47
+
48
+ def element(self) -> MaxReducerElement:
49
+ return MaxReducerElement()
@@ -0,0 +1,49 @@
1
+ """Min aggregate function."""
2
+
3
+ from typing import Any
4
+
5
+ from .aggregate_function import AggregateFunction
6
+ from .function_metadata import FunctionDef
7
+ from .reducer_element import ReducerElement
8
+
9
+
10
+ class MinReducerElement(ReducerElement):
11
+ """Reducer element for Min aggregate function."""
12
+
13
+ def __init__(self) -> None:
14
+ self._value: Any = None
15
+
16
+ @property
17
+ def value(self) -> Any:
18
+ return self._value
19
+
20
+ @value.setter
21
+ def value(self, val: Any) -> None:
22
+ if self._value is None or val < self._value:
23
+ self._value = val
24
+
25
+
26
+ @FunctionDef({
27
+ "description": "Returns the minimum value across grouped rows",
28
+ "category": "aggregate",
29
+ "parameters": [
30
+ {"name": "value", "description": "Value to compare", "type": "number"}
31
+ ],
32
+ "output": {"description": "Minimum value", "type": "number", "example": 1},
33
+ "examples": ["WITH [3, 1, 2] AS nums UNWIND nums AS n RETURN min(n)"]
34
+ })
35
+ class Min(AggregateFunction):
36
+ """Min aggregate function.
37
+
38
+ Returns the minimum value across grouped rows.
39
+ """
40
+
41
+ def __init__(self) -> None:
42
+ super().__init__("min")
43
+ self._expected_parameter_count = 1
44
+
45
+ def reduce(self, element: MinReducerElement) -> None:
46
+ element.value = self.first_child().value()
47
+
48
+ def element(self) -> MinReducerElement:
49
+ return MinReducerElement()
@@ -0,0 +1,48 @@
1
+ """Nodes 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 all nodes in a path as an array",
11
+ "category": "scalar",
12
+ "parameters": [
13
+ {"name": "path", "description": "A path value returned from a graph pattern match", "type": "array"}
14
+ ],
15
+ "output": {
16
+ "description": "Array of node records", "type": "array",
17
+ "example": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
18
+ },
19
+ "examples": ["MATCH p=(:Person)-[:KNOWS]-(:Person) RETURN nodes(p)"]
20
+ })
21
+ class Nodes(Function):
22
+ """Nodes function.
23
+
24
+ Returns all nodes in a path as an array.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ super().__init__("nodes")
29
+ self._expected_parameter_count = 1
30
+
31
+ def value(self) -> Any:
32
+ path = self.get_children()[0].value()
33
+ if path is None:
34
+ return []
35
+ if not isinstance(path, list):
36
+ raise ValueError("nodes() expects a path (array)")
37
+ # A path is an array of alternating node and relationship objects:
38
+ # [node, rel, node, rel, node, ...]
39
+ # Nodes are plain dicts (have 'id' but not all of 'type'/'startNode'/'endNode'/'properties')
40
+ # Relationships are RelationshipMatchRecords (have 'type', 'startNode', 'endNode', 'properties')
41
+ result: List[Any] = []
42
+ for element in path:
43
+ if element is None or not isinstance(element, dict):
44
+ continue
45
+ # A RelationshipMatchRecord has type, startNode, endNode, properties
46
+ if not all(k in element for k in ("type", "startNode", "endNode", "properties")):
47
+ result.append(element)
48
+ return result
@@ -1,6 +1,6 @@
1
1
  """PredicateSum function."""
2
2
 
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  from .function_metadata import FunctionDef
6
6
  from .predicate_function import PredicateFunction
@@ -41,12 +41,9 @@ class PredicateSum(PredicateFunction):
41
41
  if array is None or not isinstance(array, list):
42
42
  raise ValueError("Invalid array for sum function")
43
43
 
44
- _sum: Optional[Any] = None
44
+ _sum: int = 0
45
45
  for item in array:
46
46
  self._value_holder.holder = item
47
47
  if self.where is None or self.where.value():
48
- if _sum is None:
49
- _sum = self._return.value()
50
- else:
51
- _sum += self._return.value()
48
+ _sum += self._return.value()
52
49
  return _sum
@@ -0,0 +1,50 @@
1
+ """Properties function."""
2
+
3
+ from typing import Any
4
+
5
+ from .function import Function
6
+ from .function_metadata import FunctionDef
7
+
8
+
9
+ @FunctionDef({
10
+ "description": (
11
+ "Returns a map containing all the properties of a node, relationship, or map. "
12
+ "For nodes and relationships, internal identifiers are excluded."
13
+ ),
14
+ "category": "scalar",
15
+ "parameters": [
16
+ {"name": "entity", "description": "A node, relationship, or map to extract properties from", "type": "object"}
17
+ ],
18
+ "output": {"description": "Map of properties", "type": "object", "example": {"name": "Alice", "age": 30}},
19
+ "examples": [
20
+ "MATCH (n:Person) RETURN properties(n)",
21
+ "WITH { name: 'Alice', age: 30 } AS obj RETURN properties(obj)"
22
+ ]
23
+ })
24
+ class Properties(Function):
25
+ """Properties function.
26
+
27
+ Returns a map containing all the properties of a node, relationship, or map.
28
+ """
29
+
30
+ def __init__(self) -> None:
31
+ super().__init__("properties")
32
+ self._expected_parameter_count = 1
33
+
34
+ def value(self) -> Any:
35
+ obj = self.get_children()[0].value()
36
+ if obj is None:
37
+ return None
38
+ if not isinstance(obj, dict):
39
+ raise ValueError("properties() expects a node, relationship, or map")
40
+
41
+ # If it's a RelationshipMatchRecord (has type, startNode, endNode, properties)
42
+ if all(k in obj for k in ("type", "startNode", "endNode", "properties")):
43
+ return obj["properties"]
44
+
45
+ # If it's a node record (has id field), exclude id
46
+ if "id" in obj:
47
+ return {k: v for k, v in obj.items() if k != "id"}
48
+
49
+ # Otherwise, treat as a plain map and return a copy
50
+ return dict(obj)
@@ -0,0 +1,46 @@
1
+ """Relationships 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 all relationships in a path as an array",
11
+ "category": "scalar",
12
+ "parameters": [
13
+ {"name": "path", "description": "A path value returned from a graph pattern match", "type": "array"}
14
+ ],
15
+ "output": {
16
+ "description": "Array of relationship records", "type": "array",
17
+ "example": [{"type": "KNOWS", "properties": {"since": "2020"}}]
18
+ },
19
+ "examples": ["MATCH p=(:Person)-[:KNOWS]-(:Person) RETURN relationships(p)"]
20
+ })
21
+ class Relationships(Function):
22
+ """Relationships function.
23
+
24
+ Returns all relationships in a path as an array.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ super().__init__("relationships")
29
+ self._expected_parameter_count = 1
30
+
31
+ def value(self) -> Any:
32
+ path = self.get_children()[0].value()
33
+ if path is None:
34
+ return []
35
+ if not isinstance(path, list):
36
+ raise ValueError("relationships() expects a path (array)")
37
+ # A path is an array of alternating node and relationship objects:
38
+ # [node, rel, node, rel, node, ...]
39
+ # Relationships are RelationshipMatchRecords (have 'type', 'startNode', 'endNode', 'properties')
40
+ result: List[Any] = []
41
+ for element in path:
42
+ if element is None or not isinstance(element, dict):
43
+ continue
44
+ if all(k in element for k in ("type", "startNode", "endNode", "properties")):
45
+ result.append(element)
46
+ return result
@@ -9,23 +9,27 @@ from .function_metadata import FunctionDef
9
9
  @FunctionDef({
10
10
  "description": (
11
11
  "Returns the graph schema listing all nodes and relationships "
12
- "with a sample of their data."
12
+ "with their properties and a sample of their data."
13
13
  ),
14
14
  "category": "async",
15
15
  "parameters": [],
16
16
  "output": {
17
- "description": "Schema entry with kind, label/type, and optional sample data",
17
+ "description": "Schema entry with label/type, properties, and optional sample data",
18
18
  "type": "object",
19
19
  },
20
20
  "examples": [
21
- "CALL schema() YIELD kind, label, type, sample RETURN kind, label, type, sample",
21
+ "CALL schema() YIELD label, type, from_label, to_label, properties, sample "
22
+ "RETURN label, type, from_label, to_label, properties, sample",
22
23
  ],
23
24
  })
24
25
  class Schema(AsyncFunction):
25
26
  """Returns the graph schema of the database.
26
27
 
27
- Lists all nodes and relationships with their labels/types and a sample
28
- of their data (excluding id from nodes, left_id and right_id from relationships).
28
+ Lists all nodes and relationships with their labels/types, properties,
29
+ and a sample of their data (excluding id from nodes, left_id and right_id from relationships).
30
+
31
+ Nodes: {label, properties, sample}
32
+ Relationships: {type, from_label, to_label, properties, sample}
29
33
  """
30
34
 
31
35
  async def generate(self) -> AsyncGenerator[Any, None]:
@@ -0,0 +1,37 @@
1
+ """Tail function."""
2
+
3
+ from typing import Any
4
+
5
+ from .function import Function
6
+ from .function_metadata import FunctionDef
7
+
8
+
9
+ @FunctionDef({
10
+ "description": "Returns all elements of a list except the first",
11
+ "category": "scalar",
12
+ "parameters": [
13
+ {"name": "list", "description": "The list to get all but the first element from", "type": "array"}
14
+ ],
15
+ "output": {"description": "All elements except the first", "type": "array", "example": [2, 3]},
16
+ "examples": [
17
+ "RETURN tail([1, 2, 3])",
18
+ "WITH ['a', 'b', 'c'] AS items RETURN tail(items)"
19
+ ]
20
+ })
21
+ class Tail(Function):
22
+ """Tail function.
23
+
24
+ Returns all elements of a list except the first.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ super().__init__("tail")
29
+ self._expected_parameter_count = 1
30
+
31
+ def value(self) -> Any:
32
+ val = self.get_children()[0].value()
33
+ if val is None:
34
+ return None
35
+ if not isinstance(val, list):
36
+ raise ValueError("tail() expects a list")
37
+ return val[1:]