pytrilogy 0.0.2.57__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.
- {pytrilogy-0.0.2.57.dist-info → pytrilogy-0.0.3.0.dist-info}/METADATA +9 -2
- pytrilogy-0.0.3.0.dist-info/RECORD +99 -0
- {pytrilogy-0.0.2.57.dist-info → pytrilogy-0.0.3.0.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +2 -2
- trilogy/core/enums.py +1 -7
- trilogy/core/env_processor.py +17 -5
- trilogy/core/environment_helpers.py +11 -25
- trilogy/core/exceptions.py +4 -0
- trilogy/core/functions.py +695 -261
- trilogy/core/graph_models.py +10 -10
- trilogy/core/internal.py +11 -2
- trilogy/core/models/__init__.py +0 -0
- trilogy/core/models/author.py +2110 -0
- trilogy/core/models/build.py +1845 -0
- trilogy/core/models/build_environment.py +151 -0
- trilogy/core/models/core.py +370 -0
- trilogy/core/models/datasource.py +297 -0
- trilogy/core/models/environment.py +696 -0
- trilogy/core/models/execute.py +931 -0
- trilogy/core/optimization.py +17 -22
- trilogy/core/optimizations/base_optimization.py +1 -1
- trilogy/core/optimizations/inline_constant.py +6 -6
- trilogy/core/optimizations/inline_datasource.py +17 -11
- trilogy/core/optimizations/predicate_pushdown.py +17 -16
- trilogy/core/processing/concept_strategies_v3.py +181 -146
- trilogy/core/processing/graph_utils.py +1 -1
- trilogy/core/processing/node_generators/basic_node.py +19 -18
- trilogy/core/processing/node_generators/common.py +51 -45
- trilogy/core/processing/node_generators/filter_node.py +26 -13
- trilogy/core/processing/node_generators/group_node.py +26 -21
- trilogy/core/processing/node_generators/group_to_node.py +13 -10
- trilogy/core/processing/node_generators/multiselect_node.py +60 -43
- trilogy/core/processing/node_generators/node_merge_node.py +76 -38
- trilogy/core/processing/node_generators/rowset_node.py +59 -36
- trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +27 -34
- trilogy/core/processing/node_generators/select_merge_node.py +161 -64
- trilogy/core/processing/node_generators/select_node.py +13 -13
- trilogy/core/processing/node_generators/union_node.py +12 -11
- trilogy/core/processing/node_generators/unnest_node.py +9 -7
- trilogy/core/processing/node_generators/window_node.py +19 -16
- trilogy/core/processing/nodes/__init__.py +21 -18
- trilogy/core/processing/nodes/base_node.py +92 -77
- trilogy/core/processing/nodes/filter_node.py +19 -13
- trilogy/core/processing/nodes/group_node.py +55 -40
- trilogy/core/processing/nodes/merge_node.py +47 -38
- trilogy/core/processing/nodes/select_node_v2.py +54 -40
- trilogy/core/processing/nodes/union_node.py +5 -7
- trilogy/core/processing/nodes/unnest_node.py +7 -11
- trilogy/core/processing/nodes/window_node.py +9 -4
- trilogy/core/processing/utility.py +108 -80
- trilogy/core/query_processor.py +67 -49
- trilogy/core/statements/__init__.py +0 -0
- trilogy/core/statements/author.py +413 -0
- trilogy/core/statements/build.py +0 -0
- trilogy/core/statements/common.py +30 -0
- trilogy/core/statements/execute.py +42 -0
- trilogy/dialect/base.py +152 -111
- trilogy/dialect/common.py +9 -10
- trilogy/dialect/duckdb.py +1 -1
- trilogy/dialect/enums.py +4 -2
- trilogy/dialect/presto.py +1 -1
- trilogy/dialect/sql_server.py +1 -1
- trilogy/executor.py +44 -32
- trilogy/hooks/base_hook.py +6 -4
- trilogy/hooks/query_debugger.py +110 -93
- trilogy/parser.py +1 -1
- trilogy/parsing/common.py +303 -64
- trilogy/parsing/parse_engine.py +263 -617
- trilogy/parsing/render.py +50 -26
- trilogy/scripts/trilogy.py +2 -1
- pytrilogy-0.0.2.57.dist-info/RECORD +0 -87
- trilogy/core/models.py +0 -4960
- {pytrilogy-0.0.2.57.dist-info → pytrilogy-0.0.3.0.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.57.dist-info → pytrilogy-0.0.3.0.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.57.dist-info → pytrilogy-0.0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import logger
|
|
4
|
-
from trilogy.core.models import
|
|
4
|
+
from trilogy.core.models.build import BuildConcept, BuildFunction, BuildWhereClause
|
|
5
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
5
6
|
from trilogy.core.processing.nodes import History, StrategyNode, UnnestNode
|
|
6
7
|
from trilogy.core.processing.utility import padding
|
|
7
8
|
|
|
@@ -9,17 +10,17 @@ LOGGER_PREFIX = "[GEN_UNNEST_NODE]"
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def gen_unnest_node(
|
|
12
|
-
concept:
|
|
13
|
-
local_optional: List[
|
|
14
|
-
|
|
13
|
+
concept: BuildConcept,
|
|
14
|
+
local_optional: List[BuildConcept],
|
|
15
|
+
history: History,
|
|
16
|
+
environment: BuildEnvironment,
|
|
15
17
|
g,
|
|
16
18
|
depth: int,
|
|
17
19
|
source_concepts,
|
|
18
|
-
|
|
19
|
-
conditions: WhereClause | None = None,
|
|
20
|
+
conditions: BuildWhereClause | None = None,
|
|
20
21
|
) -> StrategyNode | None:
|
|
21
22
|
arguments = []
|
|
22
|
-
if isinstance(concept.lineage,
|
|
23
|
+
if isinstance(concept.lineage, BuildFunction):
|
|
23
24
|
arguments = concept.lineage.concept_arguments
|
|
24
25
|
|
|
25
26
|
equivalent_optional = [x for x in local_optional if x.lineage == concept.lineage]
|
|
@@ -56,6 +57,7 @@ def gen_unnest_node(
|
|
|
56
57
|
output_concepts=base.output_concepts,
|
|
57
58
|
environment=environment,
|
|
58
59
|
parents=[base],
|
|
60
|
+
# conditions=conditions.conditional if conditions else None,
|
|
59
61
|
preexisting_conditions=conditions.conditional if conditions else None,
|
|
60
62
|
)
|
|
61
63
|
qds = new.resolve()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import logger
|
|
4
|
-
from trilogy.core.models import
|
|
4
|
+
from trilogy.core.models.build import BuildConcept, BuildWhereClause, BuildWindowItem
|
|
5
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
5
6
|
from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
|
|
6
7
|
from trilogy.core.processing.utility import padding
|
|
7
8
|
from trilogy.utility import unique
|
|
@@ -9,40 +10,42 @@ from trilogy.utility import unique
|
|
|
9
10
|
LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
WINDOW_TYPES = (BuildWindowItem,)
|
|
14
|
+
|
|
15
|
+
|
|
12
16
|
def resolve_window_parent_concepts(
|
|
13
|
-
concept:
|
|
14
|
-
) -> tuple[
|
|
15
|
-
if not isinstance(concept.lineage,
|
|
17
|
+
concept: BuildConcept, environment: BuildEnvironment
|
|
18
|
+
) -> tuple[BuildConcept, List[BuildConcept]]:
|
|
19
|
+
if not isinstance(concept.lineage, WINDOW_TYPES):
|
|
16
20
|
raise ValueError
|
|
17
21
|
base = []
|
|
22
|
+
logger.info(concept.lineage)
|
|
18
23
|
if concept.lineage.over:
|
|
19
24
|
base += concept.lineage.over
|
|
20
25
|
if concept.lineage.order_by:
|
|
21
26
|
for item in concept.lineage.order_by:
|
|
22
|
-
|
|
23
|
-
# that introduces a circular dependency on an aggregate
|
|
24
|
-
# that is grouped by a window
|
|
25
|
-
# need to figure out how to resolve this
|
|
26
|
-
# base += [environment.concepts[item.expr.output.address]]
|
|
27
|
-
base += [item.expr.output]
|
|
27
|
+
base += item.concept_arguments
|
|
28
28
|
return concept.lineage.content, unique(base, "address")
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def gen_window_node(
|
|
32
|
-
concept:
|
|
33
|
-
local_optional: list[
|
|
34
|
-
environment:
|
|
32
|
+
concept: BuildConcept,
|
|
33
|
+
local_optional: list[BuildConcept],
|
|
34
|
+
environment: BuildEnvironment,
|
|
35
35
|
g,
|
|
36
36
|
depth: int,
|
|
37
37
|
source_concepts,
|
|
38
38
|
history: History | None = None,
|
|
39
|
-
conditions:
|
|
39
|
+
conditions: BuildWhereClause | None = None,
|
|
40
40
|
) -> StrategyNode | None:
|
|
41
41
|
base, parent_concepts = resolve_window_parent_concepts(concept, environment)
|
|
42
|
+
logger.info(
|
|
43
|
+
f"{padding(depth)}{LOGGER_PREFIX} generating window node for {concept} with parents {parent_concepts}"
|
|
44
|
+
)
|
|
42
45
|
equivalent_optional = [
|
|
43
46
|
x
|
|
44
47
|
for x in local_optional
|
|
45
|
-
if isinstance(x.lineage,
|
|
48
|
+
if isinstance(x.lineage, WINDOW_TYPES)
|
|
46
49
|
and resolve_window_parent_concepts(x, environment)[1] == parent_concepts
|
|
47
50
|
]
|
|
48
51
|
|
|
@@ -52,7 +55,7 @@ def gen_window_node(
|
|
|
52
55
|
targets = [base]
|
|
53
56
|
if equivalent_optional:
|
|
54
57
|
for x in equivalent_optional:
|
|
55
|
-
assert isinstance(x.lineage,
|
|
58
|
+
assert isinstance(x.lineage, WINDOW_TYPES)
|
|
56
59
|
targets.append(x.lineage.content)
|
|
57
60
|
|
|
58
61
|
parent_node: StrategyNode = source_concepts(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from pydantic import BaseModel, ConfigDict, Field
|
|
2
2
|
|
|
3
|
-
from trilogy.core.models import
|
|
3
|
+
from trilogy.core.models.build import BuildConcept, BuildWhereClause
|
|
4
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
5
|
+
from trilogy.core.models.environment import Environment
|
|
4
6
|
|
|
5
7
|
from .base_node import NodeJoin, StrategyNode
|
|
6
8
|
from .filter_node import FilterNode
|
|
@@ -13,6 +15,7 @@ from .window_node import WindowNode
|
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class History(BaseModel):
|
|
18
|
+
base_environment: Environment
|
|
16
19
|
history: dict[str, StrategyNode | None] = Field(default_factory=dict)
|
|
17
20
|
select_history: dict[str, StrategyNode | None] = Field(default_factory=dict)
|
|
18
21
|
started: set[str] = Field(default_factory=set)
|
|
@@ -20,9 +23,9 @@ class History(BaseModel):
|
|
|
20
23
|
|
|
21
24
|
def _concepts_to_lookup(
|
|
22
25
|
self,
|
|
23
|
-
search: list[
|
|
26
|
+
search: list[BuildConcept],
|
|
24
27
|
accept_partial: bool,
|
|
25
|
-
conditions:
|
|
28
|
+
conditions: BuildWhereClause | None = None,
|
|
26
29
|
) -> str:
|
|
27
30
|
if conditions:
|
|
28
31
|
return (
|
|
@@ -34,10 +37,10 @@ class History(BaseModel):
|
|
|
34
37
|
|
|
35
38
|
def search_to_history(
|
|
36
39
|
self,
|
|
37
|
-
search: list[
|
|
40
|
+
search: list[BuildConcept],
|
|
38
41
|
accept_partial: bool,
|
|
39
42
|
output: StrategyNode | None,
|
|
40
|
-
conditions:
|
|
43
|
+
conditions: BuildWhereClause | None = None,
|
|
41
44
|
):
|
|
42
45
|
self.history[
|
|
43
46
|
self._concepts_to_lookup(search, accept_partial, conditions=conditions)
|
|
@@ -45,8 +48,8 @@ class History(BaseModel):
|
|
|
45
48
|
|
|
46
49
|
def get_history(
|
|
47
50
|
self,
|
|
48
|
-
search: list[
|
|
49
|
-
conditions:
|
|
51
|
+
search: list[BuildConcept],
|
|
52
|
+
conditions: BuildWhereClause | None = None,
|
|
50
53
|
accept_partial: bool = False,
|
|
51
54
|
parent_key: str = "",
|
|
52
55
|
) -> StrategyNode | None | bool:
|
|
@@ -68,9 +71,9 @@ class History(BaseModel):
|
|
|
68
71
|
|
|
69
72
|
def log_start(
|
|
70
73
|
self,
|
|
71
|
-
search: list[
|
|
74
|
+
search: list[BuildConcept],
|
|
72
75
|
accept_partial: bool = False,
|
|
73
|
-
conditions:
|
|
76
|
+
conditions: BuildWhereClause | None = None,
|
|
74
77
|
):
|
|
75
78
|
self.started.add(
|
|
76
79
|
self._concepts_to_lookup(
|
|
@@ -82,9 +85,9 @@ class History(BaseModel):
|
|
|
82
85
|
|
|
83
86
|
def check_started(
|
|
84
87
|
self,
|
|
85
|
-
search: list[
|
|
88
|
+
search: list[BuildConcept],
|
|
86
89
|
accept_partial: bool = False,
|
|
87
|
-
conditions:
|
|
90
|
+
conditions: BuildWhereClause | None = None,
|
|
88
91
|
):
|
|
89
92
|
return (
|
|
90
93
|
self._concepts_to_lookup(
|
|
@@ -97,12 +100,12 @@ class History(BaseModel):
|
|
|
97
100
|
|
|
98
101
|
def _select_concepts_to_lookup(
|
|
99
102
|
self,
|
|
100
|
-
main:
|
|
101
|
-
search: list[
|
|
103
|
+
main: BuildConcept,
|
|
104
|
+
search: list[BuildConcept],
|
|
102
105
|
accept_partial: bool,
|
|
103
106
|
fail_if_not_found: bool,
|
|
104
107
|
accept_partial_optional: bool,
|
|
105
|
-
conditions:
|
|
108
|
+
conditions: BuildWhereClause | None = None,
|
|
106
109
|
) -> str:
|
|
107
110
|
return (
|
|
108
111
|
str(main.address)
|
|
@@ -116,16 +119,16 @@ class History(BaseModel):
|
|
|
116
119
|
|
|
117
120
|
def gen_select_node(
|
|
118
121
|
self,
|
|
119
|
-
concept:
|
|
120
|
-
local_optional: list[
|
|
121
|
-
environment:
|
|
122
|
+
concept: BuildConcept,
|
|
123
|
+
local_optional: list[BuildConcept],
|
|
124
|
+
environment: BuildEnvironment,
|
|
122
125
|
g,
|
|
123
126
|
depth: int,
|
|
124
127
|
source_concepts,
|
|
125
128
|
fail_if_not_found: bool = False,
|
|
126
129
|
accept_partial: bool = False,
|
|
127
130
|
accept_partial_optional: bool = False,
|
|
128
|
-
conditions:
|
|
131
|
+
conditions: BuildWhereClause | None = None,
|
|
129
132
|
) -> StrategyNode | None:
|
|
130
133
|
from trilogy.core.processing.node_generators.select_node import gen_select_node
|
|
131
134
|
|
|
@@ -4,45 +4,45 @@ from typing import List, Optional
|
|
|
4
4
|
|
|
5
5
|
from trilogy.core.enums import (
|
|
6
6
|
BooleanOperator,
|
|
7
|
+
Derivation,
|
|
7
8
|
JoinType,
|
|
8
|
-
PurposeLineage,
|
|
9
|
-
)
|
|
10
|
-
from trilogy.core.models import (
|
|
11
|
-
Comparison,
|
|
12
|
-
Concept,
|
|
13
|
-
ConceptPair,
|
|
14
|
-
Conditional,
|
|
15
|
-
Datasource,
|
|
16
|
-
Environment,
|
|
17
|
-
Grain,
|
|
18
|
-
LooseConceptList,
|
|
19
|
-
Parenthetical,
|
|
20
|
-
QueryDatasource,
|
|
21
9
|
SourceType,
|
|
22
|
-
UnnestJoin,
|
|
23
10
|
)
|
|
11
|
+
from trilogy.core.models.build import (
|
|
12
|
+
BuildComparison,
|
|
13
|
+
BuildConcept,
|
|
14
|
+
BuildConditional,
|
|
15
|
+
BuildDatasource,
|
|
16
|
+
BuildGrain,
|
|
17
|
+
BuildOrderBy,
|
|
18
|
+
BuildParenthetical,
|
|
19
|
+
LooseBuildConceptList,
|
|
20
|
+
)
|
|
21
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
22
|
+
from trilogy.core.models.execute import ConceptPair, QueryDatasource, UnnestJoin
|
|
24
23
|
from trilogy.utility import unique
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
def resolve_concept_map(
|
|
28
|
-
inputs: List[QueryDatasource |
|
|
29
|
-
targets: List[
|
|
30
|
-
inherited_inputs: List[
|
|
31
|
-
full_joins: List[
|
|
32
|
-
) -> dict[str, set[
|
|
27
|
+
inputs: List[QueryDatasource | BuildDatasource],
|
|
28
|
+
targets: List[BuildConcept],
|
|
29
|
+
inherited_inputs: List[BuildConcept],
|
|
30
|
+
full_joins: List[BuildConcept] | None = None,
|
|
31
|
+
) -> dict[str, set[BuildDatasource | QueryDatasource | UnnestJoin]]:
|
|
33
32
|
targets = targets or []
|
|
34
|
-
concept_map: dict[str, set[
|
|
33
|
+
concept_map: dict[str, set[BuildDatasource | QueryDatasource | UnnestJoin]] = (
|
|
35
34
|
defaultdict(set)
|
|
36
35
|
)
|
|
37
36
|
full_addresses = {c.address for c in full_joins} if full_joins else set()
|
|
38
37
|
inherited = set([t.address for t in inherited_inputs])
|
|
39
38
|
for input in inputs:
|
|
40
39
|
for concept in input.output_concepts:
|
|
41
|
-
if concept.address not in input.
|
|
40
|
+
if concept.address not in input.full_concepts:
|
|
42
41
|
continue
|
|
43
|
-
if
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (
|
|
43
|
+
isinstance(input, QueryDatasource)
|
|
44
|
+
and concept.address in input.hidden_concepts
|
|
45
|
+
):
|
|
46
46
|
continue
|
|
47
47
|
if concept.address in full_addresses:
|
|
48
48
|
concept_map[concept.address].add(input)
|
|
@@ -75,8 +75,8 @@ def resolve_concept_map(
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
def get_all_parent_partial(
|
|
78
|
-
all_concepts: List[
|
|
79
|
-
) -> List[
|
|
78
|
+
all_concepts: List[BuildConcept], parents: List["StrategyNode"]
|
|
79
|
+
) -> List[BuildConcept]:
|
|
80
80
|
return unique(
|
|
81
81
|
[
|
|
82
82
|
c
|
|
@@ -102,8 +102,8 @@ def get_all_parent_partial(
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def get_all_parent_nullable(
|
|
105
|
-
all_concepts: List[
|
|
106
|
-
) -> List[
|
|
105
|
+
all_concepts: List[BuildConcept], parents: List["StrategyNode"]
|
|
106
|
+
) -> List[BuildConcept]:
|
|
107
107
|
return unique(
|
|
108
108
|
[
|
|
109
109
|
c
|
|
@@ -126,28 +126,33 @@ class StrategyNode:
|
|
|
126
126
|
|
|
127
127
|
def __init__(
|
|
128
128
|
self,
|
|
129
|
-
input_concepts: List[
|
|
130
|
-
output_concepts: List[
|
|
131
|
-
environment:
|
|
129
|
+
input_concepts: List[BuildConcept],
|
|
130
|
+
output_concepts: List[BuildConcept],
|
|
131
|
+
environment: BuildEnvironment,
|
|
132
132
|
whole_grain: bool = False,
|
|
133
133
|
parents: List["StrategyNode"] | None = None,
|
|
134
|
-
partial_concepts: List[
|
|
135
|
-
nullable_concepts: List[
|
|
134
|
+
partial_concepts: List[BuildConcept] | None = None,
|
|
135
|
+
nullable_concepts: List[BuildConcept] | None = None,
|
|
136
136
|
depth: int = 0,
|
|
137
|
-
conditions:
|
|
138
|
-
|
|
137
|
+
conditions: (
|
|
138
|
+
BuildConditional | BuildComparison | BuildParenthetical | None
|
|
139
|
+
) = None,
|
|
140
|
+
preexisting_conditions: (
|
|
141
|
+
BuildConditional | BuildComparison | BuildParenthetical | None
|
|
142
|
+
) = None,
|
|
139
143
|
force_group: bool | None = None,
|
|
140
|
-
grain: Optional[
|
|
141
|
-
hidden_concepts:
|
|
142
|
-
existence_concepts: List[
|
|
143
|
-
virtual_output_concepts: List[
|
|
144
|
+
grain: Optional[BuildGrain] = None,
|
|
145
|
+
hidden_concepts: set[str] | None = None,
|
|
146
|
+
existence_concepts: List[BuildConcept] | None = None,
|
|
147
|
+
virtual_output_concepts: List[BuildConcept] | None = None,
|
|
148
|
+
ordering: BuildOrderBy | None = None,
|
|
144
149
|
):
|
|
145
|
-
self.input_concepts: List[
|
|
150
|
+
self.input_concepts: List[BuildConcept] = (
|
|
146
151
|
unique(input_concepts, "address") if input_concepts else []
|
|
147
152
|
)
|
|
148
|
-
self.input_lcl =
|
|
149
|
-
self.output_concepts: List[
|
|
150
|
-
self.output_lcl =
|
|
153
|
+
self.input_lcl = LooseBuildConceptList(concepts=self.input_concepts)
|
|
154
|
+
self.output_concepts: List[BuildConcept] = unique(output_concepts, "address")
|
|
155
|
+
self.output_lcl = LooseBuildConceptList(concepts=self.output_concepts)
|
|
151
156
|
|
|
152
157
|
self.environment = environment
|
|
153
158
|
self.whole_grain = whole_grain
|
|
@@ -159,13 +164,13 @@ class StrategyNode:
|
|
|
159
164
|
self.nullable_concepts = nullable_concepts or get_all_parent_nullable(
|
|
160
165
|
self.output_concepts, self.parents
|
|
161
166
|
)
|
|
162
|
-
|
|
167
|
+
self.ordering = ordering
|
|
163
168
|
self.depth = depth
|
|
164
169
|
self.conditions = conditions
|
|
165
170
|
self.grain = grain
|
|
166
171
|
self.force_group = force_group
|
|
167
172
|
self.tainted = False
|
|
168
|
-
self.hidden_concepts = hidden_concepts or
|
|
173
|
+
self.hidden_concepts = hidden_concepts or set()
|
|
169
174
|
self.existence_concepts = existence_concepts or []
|
|
170
175
|
self.virtual_output_concepts = virtual_output_concepts or []
|
|
171
176
|
self.preexisting_conditions = preexisting_conditions
|
|
@@ -176,7 +181,7 @@ class StrategyNode:
|
|
|
176
181
|
and self.preexisting_conditions
|
|
177
182
|
and self.conditions != self.preexisting_conditions
|
|
178
183
|
):
|
|
179
|
-
self.preexisting_conditions =
|
|
184
|
+
self.preexisting_conditions = BuildConditional(
|
|
180
185
|
left=self.conditions,
|
|
181
186
|
right=self.preexisting_conditions,
|
|
182
187
|
operator=BooleanOperator.AND,
|
|
@@ -206,16 +211,18 @@ class StrategyNode:
|
|
|
206
211
|
return self
|
|
207
212
|
|
|
208
213
|
def set_preexisting_conditions(
|
|
209
|
-
self, conditions:
|
|
214
|
+
self, conditions: BuildConditional | BuildComparison | BuildParenthetical
|
|
210
215
|
):
|
|
211
216
|
self.preexisting_conditions = conditions
|
|
212
217
|
return self
|
|
213
218
|
|
|
214
|
-
def add_condition(
|
|
219
|
+
def add_condition(
|
|
220
|
+
self, condition: BuildConditional | BuildComparison | BuildParenthetical
|
|
221
|
+
):
|
|
215
222
|
if self.conditions and condition == self.conditions:
|
|
216
223
|
return self
|
|
217
224
|
if self.conditions:
|
|
218
|
-
self.conditions =
|
|
225
|
+
self.conditions = BuildConditional(
|
|
219
226
|
left=self.conditions, right=condition, operator=BooleanOperator.AND
|
|
220
227
|
)
|
|
221
228
|
else:
|
|
@@ -237,27 +244,29 @@ class StrategyNode:
|
|
|
237
244
|
self.output_concepts, self.parents
|
|
238
245
|
)
|
|
239
246
|
|
|
240
|
-
self.partial_lcl =
|
|
247
|
+
self.partial_lcl = LooseBuildConceptList(concepts=self.partial_concepts)
|
|
241
248
|
|
|
242
|
-
def add_output_concepts(self, concepts: List[
|
|
249
|
+
def add_output_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
|
|
243
250
|
for concept in concepts:
|
|
244
251
|
if concept.address not in self.output_lcl.addresses:
|
|
245
252
|
self.output_concepts.append(concept)
|
|
246
|
-
self.output_lcl =
|
|
253
|
+
self.output_lcl = LooseBuildConceptList(concepts=self.output_concepts)
|
|
247
254
|
if rebuild:
|
|
248
255
|
self.rebuild_cache()
|
|
249
256
|
return self
|
|
250
257
|
|
|
251
|
-
def add_partial_concepts(self, concepts: List[
|
|
258
|
+
def add_partial_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
|
|
252
259
|
for concept in concepts:
|
|
253
260
|
if concept.address not in self.partial_lcl.addresses:
|
|
254
261
|
self.partial_concepts.append(concept)
|
|
255
|
-
self.partial_lcl =
|
|
262
|
+
self.partial_lcl = LooseBuildConceptList(concepts=self.partial_concepts)
|
|
256
263
|
if rebuild:
|
|
257
264
|
self.rebuild_cache()
|
|
258
265
|
return self
|
|
259
266
|
|
|
260
|
-
def add_existence_concepts(
|
|
267
|
+
def add_existence_concepts(
|
|
268
|
+
self, concepts: List[BuildConcept], rebuild: bool = True
|
|
269
|
+
):
|
|
261
270
|
for concept in concepts:
|
|
262
271
|
if concept.address not in self.output_concepts:
|
|
263
272
|
self.existence_concepts.append(concept)
|
|
@@ -265,38 +274,40 @@ class StrategyNode:
|
|
|
265
274
|
self.rebuild_cache()
|
|
266
275
|
return self
|
|
267
276
|
|
|
268
|
-
def set_output_concepts(self, concepts: List[
|
|
277
|
+
def set_output_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
|
|
269
278
|
# exit if no changes
|
|
270
279
|
if self.output_concepts == concepts:
|
|
271
280
|
return self
|
|
272
281
|
self.output_concepts = concepts
|
|
273
|
-
self.output_lcl =
|
|
282
|
+
self.output_lcl = LooseBuildConceptList(concepts=self.output_concepts)
|
|
274
283
|
|
|
275
284
|
if rebuild:
|
|
276
285
|
self.rebuild_cache()
|
|
277
286
|
return self
|
|
278
287
|
|
|
279
|
-
def add_output_concept(self, concept:
|
|
288
|
+
def add_output_concept(self, concept: BuildConcept, rebuild: bool = True):
|
|
280
289
|
return self.add_output_concepts([concept], rebuild)
|
|
281
290
|
|
|
282
|
-
def hide_output_concepts(self, concepts: List[
|
|
291
|
+
def hide_output_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
|
|
283
292
|
for x in concepts:
|
|
284
|
-
self.hidden_concepts.
|
|
293
|
+
self.hidden_concepts.add(x.address)
|
|
285
294
|
if rebuild:
|
|
286
295
|
self.rebuild_cache()
|
|
287
296
|
return self
|
|
288
297
|
|
|
289
|
-
def unhide_output_concepts(
|
|
290
|
-
self
|
|
291
|
-
|
|
292
|
-
|
|
298
|
+
def unhide_output_concepts(
|
|
299
|
+
self, concepts: List[BuildConcept], rebuild: bool = True
|
|
300
|
+
):
|
|
301
|
+
self.hidden_concepts = set(x for x in self.hidden_concepts if x not in concepts)
|
|
293
302
|
if rebuild:
|
|
294
303
|
self.rebuild_cache()
|
|
295
304
|
return self
|
|
296
305
|
|
|
297
|
-
def remove_output_concepts(
|
|
306
|
+
def remove_output_concepts(
|
|
307
|
+
self, concepts: List[BuildConcept], rebuild: bool = True
|
|
308
|
+
):
|
|
298
309
|
for x in concepts:
|
|
299
|
-
self.hidden_concepts.
|
|
310
|
+
self.hidden_concepts.add(x.address)
|
|
300
311
|
addresses = [x.address for x in concepts]
|
|
301
312
|
self.output_concepts = [
|
|
302
313
|
x for x in self.output_concepts if x.address not in addresses
|
|
@@ -306,7 +317,7 @@ class StrategyNode:
|
|
|
306
317
|
return self
|
|
307
318
|
|
|
308
319
|
@property
|
|
309
|
-
def usable_outputs(self) -> list[
|
|
320
|
+
def usable_outputs(self) -> list[BuildConcept]:
|
|
310
321
|
return [
|
|
311
322
|
x for x in self.output_concepts if x.address not in self.hidden_concepts
|
|
312
323
|
]
|
|
@@ -316,11 +327,11 @@ class StrategyNode:
|
|
|
316
327
|
return "\t" * self.depth
|
|
317
328
|
|
|
318
329
|
@property
|
|
319
|
-
def all_concepts(self) -> list[
|
|
330
|
+
def all_concepts(self) -> list[BuildConcept]:
|
|
320
331
|
return [*self.output_concepts]
|
|
321
332
|
|
|
322
333
|
@property
|
|
323
|
-
def all_used_concepts(self) -> list[
|
|
334
|
+
def all_used_concepts(self) -> list[BuildConcept]:
|
|
324
335
|
return [*self.input_concepts, *self.existence_concepts]
|
|
325
336
|
|
|
326
337
|
def __repr__(self):
|
|
@@ -333,11 +344,13 @@ class StrategyNode:
|
|
|
333
344
|
return f"{self.__class__.__name__}<{contents}>"
|
|
334
345
|
|
|
335
346
|
def _resolve(self) -> QueryDatasource:
|
|
336
|
-
parent_sources: List[QueryDatasource |
|
|
347
|
+
parent_sources: List[QueryDatasource | BuildDatasource] = [
|
|
337
348
|
p.resolve() for p in self.parents
|
|
338
349
|
]
|
|
339
350
|
|
|
340
|
-
grain =
|
|
351
|
+
grain = (
|
|
352
|
+
self.grain if self.grain else BuildGrain.from_concepts(self.output_concepts)
|
|
353
|
+
)
|
|
341
354
|
source_map = resolve_concept_map(
|
|
342
355
|
parent_sources,
|
|
343
356
|
targets=self.output_concepts,
|
|
@@ -357,11 +370,12 @@ class StrategyNode:
|
|
|
357
370
|
nullable_concepts=self.nullable_concepts,
|
|
358
371
|
force_group=self.force_group,
|
|
359
372
|
hidden_concepts=self.hidden_concepts,
|
|
373
|
+
ordering=self.ordering,
|
|
360
374
|
)
|
|
361
375
|
|
|
362
376
|
def rebuild_cache(self) -> QueryDatasource:
|
|
363
377
|
self.tainted = True
|
|
364
|
-
self.output_lcl =
|
|
378
|
+
self.output_lcl = LooseBuildConceptList(concepts=self.output_concepts)
|
|
365
379
|
if not self.resolution_cache:
|
|
366
380
|
return self.resolve()
|
|
367
381
|
self.resolution_cache = None
|
|
@@ -388,9 +402,10 @@ class StrategyNode:
|
|
|
388
402
|
preexisting_conditions=self.preexisting_conditions,
|
|
389
403
|
force_group=self.force_group,
|
|
390
404
|
grain=self.grain,
|
|
391
|
-
hidden_concepts=
|
|
405
|
+
hidden_concepts=set(self.hidden_concepts),
|
|
392
406
|
existence_concepts=list(self.existence_concepts),
|
|
393
407
|
virtual_output_concepts=list(self.virtual_output_concepts),
|
|
408
|
+
ordering=self.ordering,
|
|
394
409
|
)
|
|
395
410
|
|
|
396
411
|
|
|
@@ -398,12 +413,14 @@ class StrategyNode:
|
|
|
398
413
|
class NodeJoin:
|
|
399
414
|
left_node: StrategyNode
|
|
400
415
|
right_node: StrategyNode
|
|
401
|
-
concepts: List[
|
|
416
|
+
concepts: List[BuildConcept]
|
|
402
417
|
join_type: JoinType
|
|
403
418
|
filter_to_mutual: bool = False
|
|
404
419
|
concept_pairs: list[ConceptPair] | None = None
|
|
405
420
|
|
|
406
421
|
def __post_init__(self):
|
|
422
|
+
if self.left_node == self.right_node:
|
|
423
|
+
raise SyntaxError("Invalid join, left and right nodes are the same")
|
|
407
424
|
if self.concept_pairs:
|
|
408
425
|
return
|
|
409
426
|
final_concepts = []
|
|
@@ -424,9 +441,7 @@ class NodeJoin:
|
|
|
424
441
|
# if one datasource only has constants
|
|
425
442
|
# we can join on 1=1
|
|
426
443
|
for ds in [self.left_node, self.right_node]:
|
|
427
|
-
if all(
|
|
428
|
-
[c.derivation == PurposeLineage.CONSTANT for c in ds.all_concepts]
|
|
429
|
-
):
|
|
444
|
+
if all([c.derivation == Derivation.CONSTANT for c in ds.all_concepts]):
|
|
430
445
|
self.concepts = []
|
|
431
446
|
return
|
|
432
447
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
from trilogy.core.
|
|
4
|
-
Comparison,
|
|
5
|
-
Concept,
|
|
6
|
-
Conditional,
|
|
7
|
-
Grain,
|
|
8
|
-
Parenthetical,
|
|
3
|
+
from trilogy.core.enums import (
|
|
9
4
|
SourceType,
|
|
10
5
|
)
|
|
6
|
+
from trilogy.core.models.build import (
|
|
7
|
+
BuildComparison,
|
|
8
|
+
BuildConcept,
|
|
9
|
+
BuildConditional,
|
|
10
|
+
BuildGrain,
|
|
11
|
+
BuildParenthetical,
|
|
12
|
+
)
|
|
11
13
|
from trilogy.core.processing.nodes.base_node import StrategyNode
|
|
12
14
|
|
|
13
15
|
|
|
@@ -24,18 +26,22 @@ class FilterNode(StrategyNode):
|
|
|
24
26
|
|
|
25
27
|
def __init__(
|
|
26
28
|
self,
|
|
27
|
-
input_concepts: List[
|
|
28
|
-
output_concepts: List[
|
|
29
|
+
input_concepts: List[BuildConcept],
|
|
30
|
+
output_concepts: List[BuildConcept],
|
|
29
31
|
environment,
|
|
30
32
|
whole_grain: bool = False,
|
|
31
33
|
parents: List["StrategyNode"] | None = None,
|
|
32
34
|
depth: int = 0,
|
|
33
|
-
conditions:
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
conditions: (
|
|
36
|
+
BuildConditional | BuildComparison | BuildParenthetical | None
|
|
37
|
+
) = None,
|
|
38
|
+
preexisting_conditions: (
|
|
39
|
+
BuildConditional | BuildComparison | BuildParenthetical | None
|
|
40
|
+
) = None,
|
|
41
|
+
partial_concepts: List[BuildConcept] | None = None,
|
|
36
42
|
force_group: bool | None = False,
|
|
37
|
-
grain:
|
|
38
|
-
existence_concepts: List[
|
|
43
|
+
grain: BuildGrain | None = None,
|
|
44
|
+
existence_concepts: List[BuildConcept] | None = None,
|
|
39
45
|
):
|
|
40
46
|
super().__init__(
|
|
41
47
|
output_concepts=output_concepts,
|