pytrilogy 0.0.2.58__py3-none-any.whl → 0.0.3.0__py3-none-any.whl

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 (75) hide show
  1. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.0.dist-info}/METADATA +9 -2
  2. pytrilogy-0.0.3.0.dist-info/RECORD +99 -0
  3. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.0.dist-info}/WHEEL +1 -1
  4. trilogy/__init__.py +2 -2
  5. trilogy/core/enums.py +1 -7
  6. trilogy/core/env_processor.py +17 -5
  7. trilogy/core/environment_helpers.py +11 -25
  8. trilogy/core/exceptions.py +4 -0
  9. trilogy/core/functions.py +695 -261
  10. trilogy/core/graph_models.py +10 -10
  11. trilogy/core/internal.py +11 -2
  12. trilogy/core/models/__init__.py +0 -0
  13. trilogy/core/models/author.py +2110 -0
  14. trilogy/core/models/build.py +1845 -0
  15. trilogy/core/models/build_environment.py +151 -0
  16. trilogy/core/models/core.py +370 -0
  17. trilogy/core/models/datasource.py +297 -0
  18. trilogy/core/models/environment.py +696 -0
  19. trilogy/core/models/execute.py +931 -0
  20. trilogy/core/optimization.py +14 -16
  21. trilogy/core/optimizations/base_optimization.py +1 -1
  22. trilogy/core/optimizations/inline_constant.py +6 -6
  23. trilogy/core/optimizations/inline_datasource.py +17 -11
  24. trilogy/core/optimizations/predicate_pushdown.py +17 -16
  25. trilogy/core/processing/concept_strategies_v3.py +180 -145
  26. trilogy/core/processing/graph_utils.py +1 -1
  27. trilogy/core/processing/node_generators/basic_node.py +19 -18
  28. trilogy/core/processing/node_generators/common.py +50 -44
  29. trilogy/core/processing/node_generators/filter_node.py +26 -13
  30. trilogy/core/processing/node_generators/group_node.py +26 -21
  31. trilogy/core/processing/node_generators/group_to_node.py +11 -8
  32. trilogy/core/processing/node_generators/multiselect_node.py +60 -43
  33. trilogy/core/processing/node_generators/node_merge_node.py +76 -38
  34. trilogy/core/processing/node_generators/rowset_node.py +57 -36
  35. trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +27 -34
  36. trilogy/core/processing/node_generators/select_merge_node.py +161 -64
  37. trilogy/core/processing/node_generators/select_node.py +13 -13
  38. trilogy/core/processing/node_generators/union_node.py +12 -11
  39. trilogy/core/processing/node_generators/unnest_node.py +9 -7
  40. trilogy/core/processing/node_generators/window_node.py +19 -16
  41. trilogy/core/processing/nodes/__init__.py +21 -18
  42. trilogy/core/processing/nodes/base_node.py +82 -66
  43. trilogy/core/processing/nodes/filter_node.py +19 -13
  44. trilogy/core/processing/nodes/group_node.py +50 -35
  45. trilogy/core/processing/nodes/merge_node.py +45 -36
  46. trilogy/core/processing/nodes/select_node_v2.py +53 -39
  47. trilogy/core/processing/nodes/union_node.py +5 -7
  48. trilogy/core/processing/nodes/unnest_node.py +7 -11
  49. trilogy/core/processing/nodes/window_node.py +9 -4
  50. trilogy/core/processing/utility.py +103 -75
  51. trilogy/core/query_processor.py +65 -47
  52. trilogy/core/statements/__init__.py +0 -0
  53. trilogy/core/statements/author.py +413 -0
  54. trilogy/core/statements/build.py +0 -0
  55. trilogy/core/statements/common.py +30 -0
  56. trilogy/core/statements/execute.py +42 -0
  57. trilogy/dialect/base.py +146 -106
  58. trilogy/dialect/common.py +9 -10
  59. trilogy/dialect/duckdb.py +1 -1
  60. trilogy/dialect/enums.py +4 -2
  61. trilogy/dialect/presto.py +1 -1
  62. trilogy/dialect/sql_server.py +1 -1
  63. trilogy/executor.py +44 -32
  64. trilogy/hooks/base_hook.py +6 -4
  65. trilogy/hooks/query_debugger.py +110 -93
  66. trilogy/parser.py +1 -1
  67. trilogy/parsing/common.py +303 -64
  68. trilogy/parsing/parse_engine.py +263 -617
  69. trilogy/parsing/render.py +50 -26
  70. trilogy/scripts/trilogy.py +2 -1
  71. pytrilogy-0.0.2.58.dist-info/RECORD +0 -87
  72. trilogy/core/models.py +0 -4960
  73. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.0.dist-info}/LICENSE.md +0 -0
  74. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.0.dist-info}/entry_points.txt +0 -0
  75. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.0.dist-info}/top_level.txt +0 -0
@@ -2,23 +2,23 @@ from dataclasses import dataclass
2
2
  from typing import List, Optional
3
3
 
4
4
  from trilogy.constants import logger
5
- from trilogy.core.models import (
6
- Comparison,
7
- Concept,
8
- Conditional,
9
- Datasource,
10
- Environment,
11
- Grain,
12
- Parenthetical,
13
- QueryDatasource,
14
- SourceType,
5
+ from trilogy.core.enums import SourceType
6
+ from trilogy.core.models.build import (
7
+ BuildComparison,
8
+ BuildConcept,
9
+ BuildConditional,
10
+ BuildDatasource,
11
+ BuildGrain,
12
+ BuildOrderBy,
13
+ BuildParenthetical,
15
14
  )
15
+ from trilogy.core.models.build_environment import BuildEnvironment
16
+ from trilogy.core.models.execute import QueryDatasource
16
17
  from trilogy.core.processing.nodes.base_node import (
17
18
  StrategyNode,
18
19
  resolve_concept_map,
19
20
  )
20
21
  from trilogy.core.processing.utility import find_nullable_concepts, is_scalar_condition
21
- from trilogy.parsing.common import concepts_to_grain_concepts
22
22
  from trilogy.utility import unique
23
23
 
24
24
  LOGGER_PREFIX = "[CONCEPT DETAIL - GROUP NODE]"
@@ -26,8 +26,8 @@ LOGGER_PREFIX = "[CONCEPT DETAIL - GROUP NODE]"
26
26
 
27
27
  @dataclass
28
28
  class GroupRequiredResponse:
29
- target: Grain
30
- upstream: Grain
29
+ target: BuildGrain
30
+ upstream: BuildGrain
31
31
  required: bool
32
32
 
33
33
 
@@ -36,19 +36,24 @@ class GroupNode(StrategyNode):
36
36
 
37
37
  def __init__(
38
38
  self,
39
- output_concepts: List[Concept],
40
- input_concepts: List[Concept],
41
- environment: Environment,
39
+ output_concepts: List[BuildConcept],
40
+ input_concepts: List[BuildConcept],
41
+ environment: BuildEnvironment,
42
42
  whole_grain: bool = False,
43
43
  parents: List["StrategyNode"] | None = None,
44
44
  depth: int = 0,
45
- partial_concepts: Optional[List[Concept]] = None,
46
- nullable_concepts: Optional[List[Concept]] = None,
45
+ partial_concepts: Optional[List[BuildConcept]] = None,
46
+ nullable_concepts: Optional[List[BuildConcept]] = None,
47
47
  force_group: bool | None = None,
48
- conditions: Conditional | Comparison | Parenthetical | None = None,
49
- preexisting_conditions: Conditional | Comparison | Parenthetical | None = None,
50
- existence_concepts: List[Concept] | None = None,
48
+ conditions: (
49
+ BuildConditional | BuildComparison | BuildParenthetical | None
50
+ ) = None,
51
+ preexisting_conditions: (
52
+ BuildConditional | BuildComparison | BuildParenthetical | None
53
+ ) = None,
54
+ existence_concepts: List[BuildConcept] | None = None,
51
55
  hidden_concepts: set[str] | None = None,
56
+ ordering: BuildOrderBy | None = None,
52
57
  ):
53
58
  super().__init__(
54
59
  input_concepts=input_concepts,
@@ -64,27 +69,33 @@ class GroupNode(StrategyNode):
64
69
  existence_concepts=existence_concepts,
65
70
  preexisting_conditions=preexisting_conditions,
66
71
  hidden_concepts=hidden_concepts,
72
+ ordering=ordering,
67
73
  )
68
74
 
69
75
  @classmethod
70
76
  def check_if_required(
71
77
  cls,
72
- downstream_concepts: List[Concept],
73
- parents: list[QueryDatasource | Datasource],
74
- environment: Environment,
78
+ downstream_concepts: List[BuildConcept],
79
+ parents: list[QueryDatasource | BuildDatasource],
80
+ environment: BuildEnvironment,
75
81
  ) -> GroupRequiredResponse:
76
- target_grain = Grain.from_concepts(
77
- concepts_to_grain_concepts(
78
- downstream_concepts,
79
- environment=environment,
80
- )
82
+ target_grain = BuildGrain.from_concepts(
83
+ downstream_concepts,
84
+ environment=environment,
81
85
  )
82
- comp_grain = Grain()
86
+
87
+ # the concepts of the souce grain might not exist in the output environment
88
+ # so we need to construct a new
89
+ concept_map: dict[str, BuildConcept] = {}
90
+ comp_grain = BuildGrain()
83
91
  for source in parents:
84
92
  comp_grain += source.grain
85
- comp_grain = Grain.from_concepts(
86
- concepts_to_grain_concepts(comp_grain.components, environment=environment)
87
- )
93
+ for x in source.output_concepts:
94
+ concept_map[x.address] = x
95
+ lookups = [
96
+ concept_map[x] if x in concept_map else x for x in comp_grain.components
97
+ ]
98
+ comp_grain = BuildGrain.from_concepts(lookups, environment=environment)
88
99
  # dynamically select if we need to group
89
100
  # because sometimes, we are already at required grain
90
101
  if comp_grain.issubset(target_grain):
@@ -93,7 +104,7 @@ class GroupNode(StrategyNode):
93
104
  return GroupRequiredResponse(target_grain, comp_grain, True)
94
105
 
95
106
  def _resolve(self) -> QueryDatasource:
96
- parent_sources: List[QueryDatasource | Datasource] = [
107
+ parent_sources: List[QueryDatasource | BuildDatasource] = [
97
108
  p.resolve() for p in self.parents
98
109
  ]
99
110
  grains = self.check_if_required(
@@ -145,13 +156,15 @@ class GroupNode(StrategyNode):
145
156
  nullable_concepts=nullable_concepts,
146
157
  hidden_concepts=self.hidden_concepts,
147
158
  condition=self.conditions,
159
+ ordering=self.ordering,
148
160
  )
149
161
  # if there is a condition on a group node and it's not scalar
150
162
  # inject an additional CTE
151
163
  if self.conditions and not is_scalar_condition(self.conditions):
152
164
  base.condition = None
153
165
  base.output_concepts = unique(
154
- base.output_concepts + self.conditions.row_arguments, "address"
166
+ list(base.output_concepts) + list(self.conditions.row_arguments),
167
+ "address",
155
168
  )
156
169
  # re-visible any hidden concepts
157
170
  base.hidden_concepts = set(
@@ -174,6 +187,7 @@ class GroupNode(StrategyNode):
174
187
  partial_concepts=self.partial_concepts,
175
188
  condition=self.conditions,
176
189
  hidden_concepts=self.hidden_concepts,
190
+ ordering=self.ordering,
177
191
  )
178
192
  return base
179
193
 
@@ -192,4 +206,5 @@ class GroupNode(StrategyNode):
192
206
  preexisting_conditions=self.preexisting_conditions,
193
207
  existence_concepts=list(self.existence_concepts),
194
208
  hidden_concepts=set(self.hidden_concepts),
209
+ ordering=self.ordering,
195
210
  )
@@ -1,20 +1,21 @@
1
1
  from typing import List, Optional, Tuple
2
2
 
3
3
  from trilogy.constants import logger
4
- from trilogy.core.models import (
5
- BaseJoin,
6
- Comparison,
7
- Concept,
8
- Conditional,
9
- Datasource,
10
- Environment,
11
- Grain,
4
+ from trilogy.core.enums import (
12
5
  JoinType,
13
- Parenthetical,
14
- QueryDatasource,
15
6
  SourceType,
16
- UnnestJoin,
17
7
  )
8
+ from trilogy.core.models.build import (
9
+ BuildComparison,
10
+ BuildConcept,
11
+ BuildConditional,
12
+ BuildDatasource,
13
+ BuildGrain,
14
+ BuildOrderBy,
15
+ BuildParenthetical,
16
+ )
17
+ from trilogy.core.models.build_environment import BuildEnvironment
18
+ from trilogy.core.models.execute import BaseJoin, QueryDatasource, UnnestJoin
18
19
  from trilogy.core.processing.nodes.base_node import (
19
20
  NodeJoin,
20
21
  StrategyNode,
@@ -27,10 +28,10 @@ LOGGER_PREFIX = "[CONCEPT DETAIL - MERGE NODE]"
27
28
 
28
29
 
29
30
  def deduplicate_nodes(
30
- merged: dict[str, QueryDatasource | Datasource],
31
+ merged: dict[str, QueryDatasource | BuildDatasource],
31
32
  logging_prefix: str,
32
- environment: Environment,
33
- ) -> tuple[bool, dict[str, QueryDatasource | Datasource], set[str]]:
33
+ environment: BuildEnvironment,
34
+ ) -> tuple[bool, dict[str, QueryDatasource | BuildDatasource], set[str]]:
34
35
  duplicates = False
35
36
  removed: set[str] = set()
36
37
  set_map: dict[str, set[str]] = {}
@@ -73,10 +74,10 @@ def deduplicate_nodes(
73
74
 
74
75
  def deduplicate_nodes_and_joins(
75
76
  joins: List[NodeJoin] | None,
76
- merged: dict[str, QueryDatasource | Datasource],
77
+ merged: dict[str, QueryDatasource | BuildDatasource],
77
78
  logging_prefix: str,
78
- environment: Environment,
79
- ) -> Tuple[List[NodeJoin] | None, dict[str, QueryDatasource | Datasource]]:
79
+ environment: BuildEnvironment,
80
+ ) -> Tuple[List[NodeJoin] | None, dict[str, QueryDatasource | BuildDatasource]]:
80
81
  # it's possible that we have more sources than we need
81
82
  duplicates = True
82
83
  while duplicates:
@@ -100,24 +101,29 @@ class MergeNode(StrategyNode):
100
101
 
101
102
  def __init__(
102
103
  self,
103
- input_concepts: List[Concept],
104
- output_concepts: List[Concept],
104
+ input_concepts: List[BuildConcept],
105
+ output_concepts: List[BuildConcept],
105
106
  environment,
106
107
  whole_grain: bool = False,
107
108
  parents: List["StrategyNode"] | None = None,
108
109
  node_joins: List[NodeJoin] | None = None,
109
110
  join_concepts: Optional[List] = None,
110
111
  force_join_type: Optional[JoinType] = None,
111
- partial_concepts: Optional[List[Concept]] = None,
112
- nullable_concepts: Optional[List[Concept]] = None,
112
+ partial_concepts: Optional[List[BuildConcept]] = None,
113
+ nullable_concepts: Optional[List[BuildConcept]] = None,
113
114
  force_group: bool | None = None,
114
115
  depth: int = 0,
115
- grain: Grain | None = None,
116
- conditions: Conditional | Comparison | Parenthetical | None = None,
117
- preexisting_conditions: Conditional | Comparison | Parenthetical | None = None,
116
+ grain: BuildGrain | None = None,
117
+ conditions: (
118
+ BuildConditional | BuildComparison | BuildParenthetical | None
119
+ ) = None,
120
+ preexisting_conditions: (
121
+ BuildConditional | BuildComparison | BuildParenthetical | None
122
+ ) = None,
118
123
  hidden_concepts: set[str] | None = None,
119
- virtual_output_concepts: List[Concept] | None = None,
120
- existence_concepts: List[Concept] | None = None,
124
+ virtual_output_concepts: List[BuildConcept] | None = None,
125
+ existence_concepts: List[BuildConcept] | None = None,
126
+ ordering: BuildOrderBy | None = None,
121
127
  ):
122
128
  super().__init__(
123
129
  input_concepts=input_concepts,
@@ -135,6 +141,7 @@ class MergeNode(StrategyNode):
135
141
  hidden_concepts=hidden_concepts,
136
142
  virtual_output_concepts=virtual_output_concepts,
137
143
  existence_concepts=existence_concepts,
144
+ ordering=ordering,
138
145
  )
139
146
  self.join_concepts = join_concepts
140
147
  self.force_join_type = force_join_type
@@ -166,7 +173,7 @@ class MergeNode(StrategyNode):
166
173
  )
167
174
  return joins
168
175
 
169
- def create_full_joins(self, dataset_list: List[QueryDatasource | Datasource]):
176
+ def create_full_joins(self, dataset_list: List[QueryDatasource | BuildDatasource]):
170
177
  joins = []
171
178
  seen = set()
172
179
  for left_value in dataset_list:
@@ -191,12 +198,12 @@ class MergeNode(StrategyNode):
191
198
  self,
192
199
  final_datasets,
193
200
  final_joins: List[NodeJoin] | None,
194
- pregrain: Grain,
195
- grain: Grain,
196
- environment: Environment,
201
+ pregrain: BuildGrain,
202
+ grain: BuildGrain,
203
+ environment: BuildEnvironment,
197
204
  ) -> List[BaseJoin | UnnestJoin]:
198
205
  # only finally, join between them for unique values
199
- dataset_list: List[QueryDatasource | Datasource] = sorted(
206
+ dataset_list: List[QueryDatasource | BuildDatasource] = sorted(
200
207
  final_datasets, key=lambda x: -len(x.grain.components)
201
208
  )
202
209
 
@@ -227,10 +234,10 @@ class MergeNode(StrategyNode):
227
234
  return joins
228
235
 
229
236
  def _resolve(self) -> QueryDatasource:
230
- parent_sources: List[QueryDatasource | Datasource] = [
237
+ parent_sources: List[QueryDatasource | BuildDatasource] = [
231
238
  p.resolve() for p in self.parents
232
239
  ]
233
- merged: dict[str, QueryDatasource | Datasource] = {}
240
+ merged: dict[str, QueryDatasource | BuildDatasource] = {}
234
241
  final_joins: List[NodeJoin] | None = self.node_joins
235
242
  for source in parent_sources:
236
243
  if source.identifier in merged:
@@ -246,7 +253,7 @@ class MergeNode(StrategyNode):
246
253
  final_joins, merged, self.logging_prefix, self.environment
247
254
  )
248
255
  # early exit if we can just return the parent
249
- final_datasets: List[QueryDatasource | Datasource] = list(merged.values())
256
+ final_datasets: List[QueryDatasource | BuildDatasource] = list(merged.values())
250
257
 
251
258
  existence_final = [
252
259
  x
@@ -254,7 +261,7 @@ class MergeNode(StrategyNode):
254
261
  if all([y in self.existence_concepts for y in x.output_concepts])
255
262
  ]
256
263
  if len(merged.keys()) == 1:
257
- final: QueryDatasource | Datasource = list(merged.values())[0]
264
+ final: QueryDatasource | BuildDatasource = list(merged.values())[0]
258
265
  if (
259
266
  set([c.address for c in final.output_concepts])
260
267
  == set([c.address for c in self.output_concepts])
@@ -288,7 +295,7 @@ class MergeNode(StrategyNode):
288
295
  )
289
296
  return dataset
290
297
 
291
- pregrain = Grain()
298
+ pregrain = BuildGrain()
292
299
  for source in final_datasets:
293
300
  pregrain += source.grain
294
301
 
@@ -352,6 +359,7 @@ class MergeNode(StrategyNode):
352
359
  force_group=force_group,
353
360
  condition=self.conditions,
354
361
  hidden_concepts=self.hidden_concepts,
362
+ ordering=self.ordering,
355
363
  )
356
364
  return qds
357
365
 
@@ -375,4 +383,5 @@ class MergeNode(StrategyNode):
375
383
  join_concepts=list(self.join_concepts) if self.join_concepts else None,
376
384
  force_join_type=self.force_join_type,
377
385
  existence_concepts=list(self.existence_concepts),
386
+ ordering=self.ordering,
378
387
  )
@@ -2,20 +2,19 @@ from typing import List, Optional
2
2
 
3
3
  from trilogy.constants import logger
4
4
  from trilogy.core.constants import CONSTANT_DATASET
5
- from trilogy.core.enums import Purpose, PurposeLineage
6
- from trilogy.core.models import (
7
- Comparison,
8
- Concept,
9
- Conditional,
10
- Datasource,
11
- Environment,
12
- Function,
13
- Grain,
14
- Parenthetical,
15
- QueryDatasource,
16
- SourceType,
17
- UnnestJoin,
5
+ from trilogy.core.enums import Derivation, Purpose, SourceType
6
+ from trilogy.core.models.build import (
7
+ BuildComparison,
8
+ BuildConcept,
9
+ BuildConditional,
10
+ BuildDatasource,
11
+ BuildFunction,
12
+ BuildGrain,
13
+ BuildOrderBy,
14
+ BuildParenthetical,
18
15
  )
16
+ from trilogy.core.models.build_environment import BuildEnvironment
17
+ from trilogy.core.models.execute import QueryDatasource, UnnestJoin
19
18
  from trilogy.core.processing.nodes.base_node import StrategyNode, resolve_concept_map
20
19
  from trilogy.utility import unique
21
20
 
@@ -31,21 +30,26 @@ class SelectNode(StrategyNode):
31
30
 
32
31
  def __init__(
33
32
  self,
34
- input_concepts: List[Concept],
35
- output_concepts: List[Concept],
36
- environment: Environment,
37
- datasource: Datasource | None = None,
33
+ input_concepts: List[BuildConcept],
34
+ output_concepts: List[BuildConcept],
35
+ environment: BuildEnvironment,
36
+ datasource: BuildDatasource | None = None,
38
37
  whole_grain: bool = False,
39
38
  parents: List["StrategyNode"] | None = None,
40
39
  depth: int = 0,
41
- partial_concepts: List[Concept] | None = None,
42
- nullable_concepts: List[Concept] | None = None,
40
+ partial_concepts: List[BuildConcept] | None = None,
41
+ nullable_concepts: List[BuildConcept] | None = None,
43
42
  accept_partial: bool = False,
44
- grain: Optional[Grain] = None,
43
+ grain: Optional[BuildGrain] = None,
45
44
  force_group: bool | None = False,
46
- conditions: Conditional | Comparison | Parenthetical | None = None,
47
- preexisting_conditions: Conditional | Comparison | Parenthetical | None = None,
45
+ conditions: (
46
+ BuildConditional | BuildComparison | BuildParenthetical | None
47
+ ) = None,
48
+ preexisting_conditions: (
49
+ BuildConditional | BuildComparison | BuildParenthetical | None
50
+ ) = None,
48
51
  hidden_concepts: set[str] | None = None,
52
+ ordering: BuildOrderBy | None = None,
49
53
  ):
50
54
  super().__init__(
51
55
  input_concepts=input_concepts,
@@ -61,6 +65,7 @@ class SelectNode(StrategyNode):
61
65
  conditions=conditions,
62
66
  preexisting_conditions=preexisting_conditions,
63
67
  hidden_concepts=hidden_concepts,
68
+ ordering=ordering,
64
69
  )
65
70
  self.accept_partial = accept_partial
66
71
  self.datasource = datasource
@@ -75,31 +80,31 @@ class SelectNode(StrategyNode):
75
80
  ) -> QueryDatasource:
76
81
  if not self.datasource:
77
82
  raise ValueError("Datasource not provided")
78
- datasource: Datasource = self.datasource
83
+ datasource: BuildDatasource = self.datasource
79
84
 
80
- all_concepts_final: List[Concept] = unique(self.all_concepts, "address")
81
- source_map: dict[str, set[Datasource | QueryDatasource | UnnestJoin]] = {
85
+ all_concepts_final: List[BuildConcept] = unique(self.all_concepts, "address")
86
+ source_map: dict[str, set[BuildDatasource | QueryDatasource | UnnestJoin]] = {
82
87
  concept.address: {datasource} for concept in self.input_concepts
83
88
  }
84
89
 
85
90
  derived_concepts = [
86
91
  c
87
92
  for c in datasource.columns
88
- if isinstance(c.alias, Function) and c.concept.address in source_map
93
+ if isinstance(c.alias, BuildFunction) and c.concept.address in source_map
89
94
  ]
90
95
  for c in derived_concepts:
91
- if not isinstance(c.alias, Function):
96
+ if not isinstance(c.alias, BuildFunction):
92
97
  continue
93
98
  for x in c.alias.concept_arguments:
94
99
  source_map[x.address] = {datasource}
95
100
  for x in all_concepts_final:
96
101
  if x.address not in source_map and x.derivation in (
97
- PurposeLineage.MULTISELECT,
98
- PurposeLineage.FILTER,
99
- PurposeLineage.BASIC,
100
- PurposeLineage.ROWSET,
101
- PurposeLineage.BASIC,
102
- PurposeLineage.UNION,
102
+ Derivation.MULTISELECT,
103
+ Derivation.FILTER,
104
+ Derivation.BASIC,
105
+ Derivation.ROWSET,
106
+ Derivation.BASIC,
107
+ Derivation.UNION,
103
108
  ):
104
109
  source_map[x.address] = set()
105
110
 
@@ -109,7 +114,7 @@ class SelectNode(StrategyNode):
109
114
  if self.force_group is False:
110
115
  grain = datasource.grain
111
116
  else:
112
- grain = self.grain or Grain()
117
+ grain = self.grain or BuildGrain()
113
118
  return QueryDatasource(
114
119
  input_concepts=self.input_concepts,
115
120
  output_concepts=all_concepts_final,
@@ -127,10 +132,11 @@ class SelectNode(StrategyNode):
127
132
  # select nodes should never group
128
133
  force_group=self.force_group,
129
134
  hidden_concepts=self.hidden_concepts,
135
+ ordering=self.ordering,
130
136
  )
131
137
 
132
138
  def resolve_from_constant_datasources(self) -> QueryDatasource:
133
- datasource = Datasource(
139
+ datasource = BuildDatasource(
134
140
  name=CONSTANT_DATASET, address=CONSTANT_DATASET, columns=[]
135
141
  )
136
142
  return QueryDatasource(
@@ -144,6 +150,7 @@ class SelectNode(StrategyNode):
144
150
  partial_concepts=[],
145
151
  source_type=SourceType.CONSTANT,
146
152
  hidden_concepts=self.hidden_concepts,
153
+ ordering=self.ordering,
147
154
  )
148
155
 
149
156
  def _resolve(self) -> QueryDatasource:
@@ -152,10 +159,10 @@ class SelectNode(StrategyNode):
152
159
  if all(
153
160
  [
154
161
  (
155
- c.derivation == PurposeLineage.CONSTANT
162
+ c.derivation == Derivation.CONSTANT
156
163
  or (
157
164
  c.purpose == Purpose.CONSTANT
158
- and c.derivation == PurposeLineage.MULTISELECT
165
+ and c.derivation == Derivation.MULTISELECT
159
166
  )
160
167
  )
161
168
  for c in self.all_concepts
@@ -165,6 +172,8 @@ class SelectNode(StrategyNode):
165
172
  f"{self.logging_prefix}{LOGGER_PREFIX} have a constant datasource"
166
173
  )
167
174
  resolution = self.resolve_from_constant_datasources()
175
+ return resolution
176
+
168
177
  if self.datasource and not resolution:
169
178
  resolution = self.resolve_from_provided_datasource()
170
179
 
@@ -172,7 +181,7 @@ class SelectNode(StrategyNode):
172
181
  if not resolution:
173
182
  return super()._resolve()
174
183
  # zip in our parent source map
175
- parent_sources: List[QueryDatasource | Datasource] = [
184
+ parent_sources: List[QueryDatasource | BuildDatasource] = [
176
185
  p.resolve() for p in self.parents
177
186
  ]
178
187
 
@@ -187,7 +196,7 @@ class SelectNode(StrategyNode):
187
196
  if v and k not in resolution.source_map:
188
197
  resolution.source_map[k] = v
189
198
  if not resolution:
190
- raise ValueError("No select node could be generated")
199
+ raise ValueError(f"No select node could be generated for {self}")
191
200
  return resolution
192
201
 
193
202
  def copy(self) -> "SelectNode":
@@ -207,6 +216,7 @@ class SelectNode(StrategyNode):
207
216
  conditions=self.conditions,
208
217
  preexisting_conditions=self.preexisting_conditions,
209
218
  hidden_concepts=self.hidden_concepts,
219
+ ordering=self.ordering,
210
220
  )
211
221
 
212
222
 
@@ -224,4 +234,8 @@ class ConstantNode(SelectNode):
224
234
  conditions=self.conditions,
225
235
  preexisting_conditions=self.preexisting_conditions,
226
236
  hidden_concepts=self.hidden_concepts,
237
+ ordering=self.ordering,
227
238
  )
239
+
240
+ def _resolve(self) -> QueryDatasource:
241
+ return self.resolve_from_constant_datasources()
@@ -1,10 +1,8 @@
1
1
  from typing import List
2
2
 
3
- from trilogy.core.models import (
4
- Concept,
5
- QueryDatasource,
6
- SourceType,
7
- )
3
+ from trilogy.core.enums import SourceType
4
+ from trilogy.core.models.build import BuildConcept
5
+ from trilogy.core.models.execute import QueryDatasource
8
6
  from trilogy.core.processing.nodes.base_node import StrategyNode
9
7
 
10
8
 
@@ -15,8 +13,8 @@ class UnionNode(StrategyNode):
15
13
 
16
14
  def __init__(
17
15
  self,
18
- input_concepts: List[Concept],
19
- output_concepts: List[Concept],
16
+ input_concepts: List[BuildConcept],
17
+ output_concepts: List[BuildConcept],
20
18
  environment,
21
19
  whole_grain: bool = False,
22
20
  parents: List["StrategyNode"] | None = None,
@@ -1,12 +1,8 @@
1
1
  from typing import List
2
2
 
3
- from trilogy.core.models import (
4
- Concept,
5
- Function,
6
- QueryDatasource,
7
- SourceType,
8
- UnnestJoin,
9
- )
3
+ from trilogy.core.enums import SourceType
4
+ from trilogy.core.models.build import BuildConcept, BuildFunction
5
+ from trilogy.core.models.execute import QueryDatasource, UnnestJoin
10
6
  from trilogy.core.processing.nodes.base_node import StrategyNode
11
7
 
12
8
 
@@ -19,9 +15,9 @@ class UnnestNode(StrategyNode):
19
15
 
20
16
  def __init__(
21
17
  self,
22
- unnest_concepts: List[Concept],
23
- input_concepts: List[Concept],
24
- output_concepts: List[Concept],
18
+ unnest_concepts: List[BuildConcept],
19
+ input_concepts: List[BuildConcept],
20
+ output_concepts: List[BuildConcept],
25
21
  environment,
26
22
  whole_grain: bool = False,
27
23
  parents: List["StrategyNode"] | None = None,
@@ -41,7 +37,7 @@ class UnnestNode(StrategyNode):
41
37
  """We need to ensure that any filtered values are removed from the output to avoid inappropriate references"""
42
38
  base = super()._resolve()
43
39
  lineage = self.unnest_concepts[0].lineage
44
- assert isinstance(lineage, Function)
40
+ assert isinstance(lineage, BuildFunction)
45
41
  final = "_".join(set([c.address for c in self.unnest_concepts]))
46
42
  unnest = UnnestJoin(
47
43
  concepts=self.unnest_concepts,
@@ -1,7 +1,9 @@
1
1
  from typing import List
2
2
 
3
- from trilogy.core.models import Concept, SourceType
4
- from trilogy.core.processing.nodes.base_node import QueryDatasource, StrategyNode
3
+ from trilogy.core.enums import SourceType
4
+ from trilogy.core.models.build import BuildConcept, BuildOrderBy
5
+ from trilogy.core.models.execute import QueryDatasource
6
+ from trilogy.core.processing.nodes.base_node import StrategyNode
5
7
 
6
8
 
7
9
  class WindowNode(StrategyNode):
@@ -9,12 +11,13 @@ class WindowNode(StrategyNode):
9
11
 
10
12
  def __init__(
11
13
  self,
12
- input_concepts: List[Concept],
13
- output_concepts: List[Concept],
14
+ input_concepts: List[BuildConcept],
15
+ output_concepts: List[BuildConcept],
14
16
  environment,
15
17
  whole_grain: bool = False,
16
18
  parents: List["StrategyNode"] | None = None,
17
19
  depth: int = 0,
20
+ ordering: BuildOrderBy | None = None,
18
21
  ):
19
22
  super().__init__(
20
23
  input_concepts=input_concepts,
@@ -23,6 +26,7 @@ class WindowNode(StrategyNode):
23
26
  whole_grain=whole_grain,
24
27
  parents=parents,
25
28
  depth=depth,
29
+ ordering=ordering,
26
30
  )
27
31
 
28
32
  def _resolve(self) -> QueryDatasource:
@@ -37,4 +41,5 @@ class WindowNode(StrategyNode):
37
41
  whole_grain=self.whole_grain,
38
42
  parents=self.parents,
39
43
  depth=self.depth,
44
+ ordering=self.ordering,
40
45
  )