pytrilogy 0.0.2.47__py3-none-any.whl → 0.0.2.48__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.2.47.dist-info → pytrilogy-0.0.2.48.dist-info}/METADATA +1 -1
- pytrilogy-0.0.2.48.dist-info/RECORD +85 -0
- trilogy/__init__.py +2 -2
- trilogy/constants.py +4 -2
- trilogy/core/enums.py +7 -1
- trilogy/core/env_processor.py +1 -2
- trilogy/core/environment_helpers.py +5 -5
- trilogy/core/functions.py +11 -10
- trilogy/core/internal.py +2 -3
- trilogy/core/models.py +444 -392
- trilogy/core/optimization.py +37 -21
- trilogy/core/optimizations/__init__.py +1 -1
- trilogy/core/optimizations/base_optimization.py +6 -6
- trilogy/core/optimizations/inline_constant.py +7 -4
- trilogy/core/optimizations/inline_datasource.py +14 -5
- trilogy/core/optimizations/predicate_pushdown.py +20 -10
- trilogy/core/processing/concept_strategies_v3.py +40 -24
- trilogy/core/processing/graph_utils.py +2 -3
- trilogy/core/processing/node_generators/__init__.py +7 -5
- trilogy/core/processing/node_generators/basic_node.py +4 -4
- trilogy/core/processing/node_generators/common.py +10 -11
- trilogy/core/processing/node_generators/filter_node.py +7 -9
- trilogy/core/processing/node_generators/group_node.py +10 -11
- trilogy/core/processing/node_generators/group_to_node.py +5 -5
- trilogy/core/processing/node_generators/multiselect_node.py +10 -12
- trilogy/core/processing/node_generators/node_merge_node.py +7 -9
- trilogy/core/processing/node_generators/rowset_node.py +9 -8
- trilogy/core/processing/node_generators/select_merge_node.py +11 -10
- trilogy/core/processing/node_generators/select_node.py +5 -5
- trilogy/core/processing/node_generators/union_node.py +75 -0
- trilogy/core/processing/node_generators/unnest_node.py +2 -3
- trilogy/core/processing/node_generators/window_node.py +3 -4
- trilogy/core/processing/nodes/__init__.py +9 -5
- trilogy/core/processing/nodes/base_node.py +17 -13
- trilogy/core/processing/nodes/filter_node.py +3 -4
- trilogy/core/processing/nodes/group_node.py +8 -10
- trilogy/core/processing/nodes/merge_node.py +11 -11
- trilogy/core/processing/nodes/select_node_v2.py +8 -9
- trilogy/core/processing/nodes/union_node.py +50 -0
- trilogy/core/processing/nodes/unnest_node.py +2 -3
- trilogy/core/processing/nodes/window_node.py +2 -3
- trilogy/core/processing/utility.py +37 -40
- trilogy/core/query_processor.py +68 -44
- trilogy/dialect/base.py +95 -53
- trilogy/dialect/bigquery.py +2 -3
- trilogy/dialect/common.py +5 -4
- trilogy/dialect/config.py +0 -2
- trilogy/dialect/duckdb.py +2 -2
- trilogy/dialect/enums.py +5 -5
- trilogy/dialect/postgres.py +2 -2
- trilogy/dialect/presto.py +3 -4
- trilogy/dialect/snowflake.py +2 -2
- trilogy/dialect/sql_server.py +3 -4
- trilogy/engine.py +2 -1
- trilogy/executor.py +43 -30
- trilogy/hooks/base_hook.py +5 -4
- trilogy/hooks/graph_hook.py +2 -1
- trilogy/hooks/query_debugger.py +18 -8
- trilogy/parsing/common.py +15 -20
- trilogy/parsing/parse_engine.py +124 -88
- trilogy/parsing/render.py +32 -35
- trilogy/parsing/trilogy.lark +8 -1
- trilogy/scripts/trilogy.py +6 -4
- trilogy/utility.py +1 -1
- pytrilogy-0.0.2.47.dist-info/RECORD +0 -83
- {pytrilogy-0.0.2.47.dist-info → pytrilogy-0.0.2.48.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.47.dist-info → pytrilogy-0.0.2.48.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.2.47.dist-info → pytrilogy-0.0.2.48.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.47.dist-info → pytrilogy-0.0.2.48.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
-
from trilogy.core.models import Concept, Environment, Conditional, WhereClause
|
|
4
|
-
from trilogy.core.processing.nodes import MergeNode, History, StrategyNode
|
|
5
3
|
import networkx as nx
|
|
6
|
-
from
|
|
4
|
+
from networkx.algorithms import approximation as ax
|
|
5
|
+
|
|
7
6
|
from trilogy.constants import logger
|
|
8
|
-
from trilogy.
|
|
7
|
+
from trilogy.core.enums import PurposeLineage
|
|
9
8
|
from trilogy.core.exceptions import AmbiguousRelationshipResolutionException
|
|
9
|
+
from trilogy.core.graph_models import concept_to_node
|
|
10
|
+
from trilogy.core.models import Concept, Conditional, Environment, WhereClause
|
|
11
|
+
from trilogy.core.processing.nodes import History, MergeNode, StrategyNode
|
|
10
12
|
from trilogy.core.processing.utility import padding
|
|
11
|
-
from
|
|
12
|
-
from trilogy.core.enums import PurposeLineage
|
|
13
|
-
|
|
13
|
+
from trilogy.utility import unique
|
|
14
14
|
|
|
15
15
|
LOGGER_PREFIX = "[GEN_MERGE_NODE]"
|
|
16
16
|
AMBIGUITY_CHECK_LIMIT = 20
|
|
@@ -194,7 +194,6 @@ def resolve_weak_components(
|
|
|
194
194
|
filter_downstream: bool = True,
|
|
195
195
|
accept_partial: bool = False,
|
|
196
196
|
) -> list[list[Concept]] | None:
|
|
197
|
-
|
|
198
197
|
break_flag = False
|
|
199
198
|
found = []
|
|
200
199
|
search_graph = environment_graph.copy()
|
|
@@ -355,7 +354,6 @@ def gen_merge_node(
|
|
|
355
354
|
conditions: Conditional | None = None,
|
|
356
355
|
search_conditions: WhereClause | None = None,
|
|
357
356
|
) -> Optional[MergeNode]:
|
|
358
|
-
|
|
359
357
|
if search_conditions:
|
|
360
358
|
all_concepts = unique(all_concepts + search_conditions.row_arguments, "address")
|
|
361
359
|
for filter_downstream in [True, False]:
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from trilogy.constants import logger
|
|
4
|
+
from trilogy.core.enums import PurposeLineage
|
|
1
5
|
from trilogy.core.models import (
|
|
2
6
|
Concept,
|
|
3
7
|
Environment,
|
|
4
|
-
|
|
8
|
+
MultiSelectStatement,
|
|
5
9
|
RowsetDerivationStatement,
|
|
6
10
|
RowsetItem,
|
|
7
|
-
|
|
11
|
+
SelectStatement,
|
|
8
12
|
WhereClause,
|
|
9
13
|
)
|
|
10
|
-
from trilogy.core.processing.nodes import
|
|
11
|
-
from typing import List
|
|
12
|
-
|
|
13
|
-
from trilogy.core.enums import PurposeLineage
|
|
14
|
-
from trilogy.constants import logger
|
|
15
|
-
from trilogy.core.processing.utility import padding, concept_to_relevant_joins
|
|
14
|
+
from trilogy.core.processing.nodes import History, MergeNode, StrategyNode
|
|
16
15
|
from trilogy.core.processing.nodes.base_node import concept_list_to_grain
|
|
16
|
+
from trilogy.core.processing.utility import concept_to_relevant_joins, padding
|
|
17
17
|
|
|
18
18
|
LOGGER_PREFIX = "[GEN_ROWSET_NODE]"
|
|
19
19
|
|
|
@@ -37,6 +37,7 @@ def gen_rowset_node(
|
|
|
37
37
|
lineage: RowsetItem = concept.lineage
|
|
38
38
|
rowset: RowsetDerivationStatement = lineage.rowset
|
|
39
39
|
select: SelectStatement | MultiSelectStatement = lineage.rowset.select
|
|
40
|
+
|
|
40
41
|
node = get_query_node(environment, select, graph=g, history=history)
|
|
41
42
|
|
|
42
43
|
if not node:
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
+
import networkx as nx
|
|
4
|
+
|
|
5
|
+
from trilogy.constants import logger
|
|
6
|
+
from trilogy.core.enums import PurposeLineage
|
|
7
|
+
from trilogy.core.graph_models import concept_to_node
|
|
3
8
|
from trilogy.core.models import (
|
|
4
9
|
Concept,
|
|
10
|
+
Datasource,
|
|
5
11
|
Environment,
|
|
6
12
|
Grain,
|
|
7
|
-
Datasource,
|
|
8
|
-
WhereClause,
|
|
9
13
|
LooseConceptList,
|
|
14
|
+
WhereClause,
|
|
10
15
|
)
|
|
11
16
|
from trilogy.core.processing.nodes import (
|
|
12
|
-
MergeNode,
|
|
13
|
-
StrategyNode,
|
|
14
|
-
GroupNode,
|
|
15
17
|
ConstantNode,
|
|
18
|
+
GroupNode,
|
|
19
|
+
MergeNode,
|
|
16
20
|
SelectNode,
|
|
21
|
+
StrategyNode,
|
|
17
22
|
)
|
|
18
|
-
import networkx as nx
|
|
19
|
-
from trilogy.core.graph_models import concept_to_node
|
|
20
|
-
from trilogy.constants import logger
|
|
21
|
-
from trilogy.core.processing.utility import padding
|
|
22
|
-
from trilogy.core.enums import PurposeLineage
|
|
23
23
|
from trilogy.core.processing.nodes.base_node import (
|
|
24
24
|
concept_list_to_grain,
|
|
25
25
|
)
|
|
26
|
+
from trilogy.core.processing.utility import padding
|
|
26
27
|
|
|
27
28
|
LOGGER_PREFIX = "[GEN_ROOT_MERGE_NODE]"
|
|
28
29
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
from trilogy.constants import logger
|
|
1
2
|
from trilogy.core.enums import PurposeLineage
|
|
3
|
+
from trilogy.core.exceptions import NoDatasourceException
|
|
2
4
|
from trilogy.core.models import (
|
|
3
5
|
Concept,
|
|
4
6
|
Environment,
|
|
5
7
|
LooseConceptList,
|
|
6
8
|
WhereClause,
|
|
7
9
|
)
|
|
10
|
+
from trilogy.core.processing.node_generators.select_merge_node import (
|
|
11
|
+
gen_select_merge_node,
|
|
12
|
+
)
|
|
8
13
|
from trilogy.core.processing.nodes import (
|
|
9
14
|
StrategyNode,
|
|
10
15
|
)
|
|
11
|
-
from trilogy.core.exceptions import NoDatasourceException
|
|
12
|
-
from trilogy.constants import logger
|
|
13
16
|
from trilogy.core.processing.utility import padding
|
|
14
|
-
from trilogy.core.processing.node_generators.select_merge_node import (
|
|
15
|
-
gen_select_merge_node,
|
|
16
|
-
)
|
|
17
17
|
|
|
18
18
|
LOGGER_PREFIX = "[GEN_SELECT_NODE]"
|
|
19
19
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from trilogy.constants import logger
|
|
4
|
+
from trilogy.core.enums import FunctionType, Purpose
|
|
5
|
+
from trilogy.core.models import Concept, Function, WhereClause
|
|
6
|
+
from trilogy.core.processing.nodes import History, StrategyNode, UnionNode
|
|
7
|
+
from trilogy.core.processing.utility import padding
|
|
8
|
+
|
|
9
|
+
LOGGER_PREFIX = "[GEN_UNION_NODE]"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_union(c: Concept):
|
|
13
|
+
return isinstance(c.lineage, Function) and c.lineage.operator == FunctionType.UNION
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def gen_union_node(
|
|
17
|
+
concept: Concept,
|
|
18
|
+
local_optional: List[Concept],
|
|
19
|
+
environment,
|
|
20
|
+
g,
|
|
21
|
+
depth: int,
|
|
22
|
+
source_concepts,
|
|
23
|
+
history: History | None = None,
|
|
24
|
+
conditions: WhereClause | None = None,
|
|
25
|
+
) -> StrategyNode | None:
|
|
26
|
+
all_unions = [x for x in local_optional if is_union(x)] + [concept]
|
|
27
|
+
|
|
28
|
+
parents = []
|
|
29
|
+
keys = [x for x in all_unions if x.purpose == Purpose.KEY]
|
|
30
|
+
base = keys.pop()
|
|
31
|
+
remaining = [x for x in all_unions if x.address != base.address]
|
|
32
|
+
arguments = []
|
|
33
|
+
if isinstance(base.lineage, Function):
|
|
34
|
+
arguments = base.lineage.concept_arguments
|
|
35
|
+
for arg in arguments:
|
|
36
|
+
relevant_parents: list[Concept] = []
|
|
37
|
+
for other_union in remaining:
|
|
38
|
+
assert other_union.lineage
|
|
39
|
+
potential_parents = [
|
|
40
|
+
z for z in other_union.lineage.arguments if isinstance(z, Concept)
|
|
41
|
+
]
|
|
42
|
+
relevant_parents += [
|
|
43
|
+
x for x in potential_parents if x.keys and arg.address in x.keys
|
|
44
|
+
]
|
|
45
|
+
logger.info(
|
|
46
|
+
f"For parent arg {arg.address}, including additional union inputs {[c.address for c in relevant_parents]}"
|
|
47
|
+
)
|
|
48
|
+
parent: StrategyNode = source_concepts(
|
|
49
|
+
mandatory_list=[arg] + relevant_parents,
|
|
50
|
+
environment=environment,
|
|
51
|
+
g=g,
|
|
52
|
+
depth=depth + 1,
|
|
53
|
+
history=history,
|
|
54
|
+
conditions=conditions,
|
|
55
|
+
)
|
|
56
|
+
parent.hide_output_concepts(parent.output_concepts)
|
|
57
|
+
# parent.remove_output_concepts(parent.output_concepts)
|
|
58
|
+
parent.add_output_concept(concept)
|
|
59
|
+
for x in remaining:
|
|
60
|
+
parent.add_output_concept(x)
|
|
61
|
+
|
|
62
|
+
parents.append(parent)
|
|
63
|
+
if not parent:
|
|
64
|
+
logger.info(
|
|
65
|
+
f"{padding(depth)}{LOGGER_PREFIX} could not find union node parents"
|
|
66
|
+
)
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
return UnionNode(
|
|
70
|
+
input_concepts=[concept] + local_optional,
|
|
71
|
+
output_concepts=[concept] + local_optional,
|
|
72
|
+
environment=environment,
|
|
73
|
+
g=g,
|
|
74
|
+
parents=parents,
|
|
75
|
+
)
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
from trilogy.constants import logger
|
|
4
4
|
from trilogy.core.models import Concept, Function, WhereClause
|
|
5
|
-
from trilogy.core.processing.nodes import
|
|
5
|
+
from trilogy.core.processing.nodes import History, StrategyNode, UnnestNode
|
|
6
6
|
from trilogy.core.processing.utility import padding
|
|
7
|
-
from trilogy.constants import logger
|
|
8
7
|
|
|
9
8
|
LOGGER_PREFIX = "[GEN_UNNEST_NODE]"
|
|
10
9
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from trilogy.core.models import Concept, WindowItem, Environment, WhereClause
|
|
5
|
-
from trilogy.utility import unique
|
|
6
|
-
from trilogy.core.processing.nodes import WindowNode, StrategyNode, History
|
|
7
3
|
from trilogy.constants import logger
|
|
4
|
+
from trilogy.core.models import Concept, Environment, WhereClause, WindowItem
|
|
5
|
+
from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
|
|
8
6
|
from trilogy.core.processing.utility import padding
|
|
7
|
+
from trilogy.utility import unique
|
|
9
8
|
|
|
10
9
|
LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
|
|
11
10
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
2
|
+
|
|
3
|
+
from trilogy.core.models import Concept, Environment, WhereClause
|
|
4
|
+
|
|
5
|
+
from .base_node import NodeJoin, StrategyNode
|
|
1
6
|
from .filter_node import FilterNode
|
|
2
7
|
from .group_node import GroupNode
|
|
3
8
|
from .merge_node import MergeNode
|
|
4
|
-
from .select_node_v2 import
|
|
5
|
-
from .
|
|
6
|
-
from .base_node import StrategyNode, NodeJoin
|
|
9
|
+
from .select_node_v2 import ConstantNode, SelectNode
|
|
10
|
+
from .union_node import UnionNode
|
|
7
11
|
from .unnest_node import UnnestNode
|
|
8
|
-
from
|
|
9
|
-
from trilogy.core.models import Concept, Environment, WhereClause
|
|
12
|
+
from .window_node import WindowNode
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class History(BaseModel):
|
|
@@ -160,5 +163,6 @@ __all__ = [
|
|
|
160
163
|
"NodeJoin",
|
|
161
164
|
"ConstantNode",
|
|
162
165
|
"UnnestNode",
|
|
166
|
+
"UnionNode",
|
|
163
167
|
"History",
|
|
164
168
|
]
|
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
from typing import List, Optional, Sequence
|
|
2
1
|
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import List, Optional, Sequence
|
|
3
4
|
|
|
5
|
+
from trilogy.core.enums import (
|
|
6
|
+
BooleanOperator,
|
|
7
|
+
Granularity,
|
|
8
|
+
JoinType,
|
|
9
|
+
Purpose,
|
|
10
|
+
PurposeLineage,
|
|
11
|
+
)
|
|
4
12
|
from trilogy.core.models import (
|
|
5
|
-
|
|
6
|
-
QueryDatasource,
|
|
7
|
-
SourceType,
|
|
13
|
+
Comparison,
|
|
8
14
|
Concept,
|
|
9
|
-
|
|
15
|
+
ConceptPair,
|
|
10
16
|
Conditional,
|
|
11
|
-
UnnestJoin,
|
|
12
17
|
Datasource,
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
Environment,
|
|
19
|
+
Grain,
|
|
15
20
|
LooseConceptList,
|
|
16
|
-
|
|
21
|
+
Parenthetical,
|
|
22
|
+
QueryDatasource,
|
|
23
|
+
SourceType,
|
|
24
|
+
UnnestJoin,
|
|
17
25
|
)
|
|
18
|
-
from trilogy.core.enums import Purpose, JoinType, PurposeLineage, Granularity
|
|
19
26
|
from trilogy.utility import unique
|
|
20
|
-
from dataclasses import dataclass
|
|
21
|
-
from trilogy.core.enums import BooleanOperator
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
def concept_list_to_grain(
|
|
@@ -67,7 +72,6 @@ def resolve_concept_map(
|
|
|
67
72
|
]:
|
|
68
73
|
continue
|
|
69
74
|
if concept.address in full_addresses:
|
|
70
|
-
|
|
71
75
|
concept_map[concept.address].add(input)
|
|
72
76
|
elif concept.address not in concept_map:
|
|
73
77
|
# equi_targets = [x for x in targets if concept.address in x.pseudonyms or x.address in concept.pseudonyms]
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
from trilogy.core.models import (
|
|
5
|
-
|
|
4
|
+
Comparison,
|
|
6
5
|
Concept,
|
|
7
6
|
Conditional,
|
|
8
|
-
Comparison,
|
|
9
|
-
Parenthetical,
|
|
10
7
|
Grain,
|
|
8
|
+
Parenthetical,
|
|
9
|
+
SourceType,
|
|
11
10
|
)
|
|
12
11
|
from trilogy.core.processing.nodes.base_node import StrategyNode
|
|
13
12
|
|
|
@@ -2,25 +2,24 @@ from typing import List, Optional
|
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import logger
|
|
4
4
|
from trilogy.core.models import (
|
|
5
|
-
|
|
6
|
-
QueryDatasource,
|
|
7
|
-
Datasource,
|
|
8
|
-
SourceType,
|
|
5
|
+
Comparison,
|
|
9
6
|
Concept,
|
|
7
|
+
Conditional,
|
|
8
|
+
Datasource,
|
|
10
9
|
Environment,
|
|
10
|
+
Grain,
|
|
11
11
|
LooseConceptList,
|
|
12
|
-
Conditional,
|
|
13
|
-
Comparison,
|
|
14
12
|
Parenthetical,
|
|
13
|
+
QueryDatasource,
|
|
14
|
+
SourceType,
|
|
15
15
|
)
|
|
16
16
|
from trilogy.core.processing.nodes.base_node import (
|
|
17
17
|
StrategyNode,
|
|
18
|
-
resolve_concept_map,
|
|
19
18
|
concept_list_to_grain,
|
|
19
|
+
resolve_concept_map,
|
|
20
20
|
)
|
|
21
|
+
from trilogy.core.processing.utility import find_nullable_concepts, is_scalar_condition
|
|
21
22
|
from trilogy.utility import unique
|
|
22
|
-
from trilogy.core.processing.utility import is_scalar_condition
|
|
23
|
-
from trilogy.core.processing.utility import find_nullable_concepts
|
|
24
23
|
|
|
25
24
|
LOGGER_PREFIX = "[CONCEPT DETAIL - GROUP NODE]"
|
|
26
25
|
|
|
@@ -96,7 +95,6 @@ class GroupNode(StrategyNode):
|
|
|
96
95
|
# otherwise if no group by, just treat it as a select
|
|
97
96
|
source_type = SourceType.SELECT
|
|
98
97
|
else:
|
|
99
|
-
|
|
100
98
|
logger.info(
|
|
101
99
|
f"{self.logging_prefix}{LOGGER_PREFIX} Group node has different grain than parents; forcing group"
|
|
102
100
|
f" upstream grains {[str(source.grain) for source in parent_sources]}"
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
from typing import List, Optional, Tuple
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
from trilogy.constants import logger
|
|
5
4
|
from trilogy.core.models import (
|
|
6
5
|
BaseJoin,
|
|
6
|
+
Comparison,
|
|
7
|
+
Concept,
|
|
8
|
+
Conditional,
|
|
9
|
+
Datasource,
|
|
10
|
+
Environment,
|
|
7
11
|
Grain,
|
|
8
12
|
JoinType,
|
|
13
|
+
Parenthetical,
|
|
9
14
|
QueryDatasource,
|
|
10
|
-
Datasource,
|
|
11
15
|
SourceType,
|
|
12
|
-
Concept,
|
|
13
16
|
UnnestJoin,
|
|
14
|
-
Conditional,
|
|
15
|
-
Comparison,
|
|
16
|
-
Parenthetical,
|
|
17
|
-
Environment,
|
|
18
17
|
)
|
|
19
|
-
from trilogy.utility import unique
|
|
20
18
|
from trilogy.core.processing.nodes.base_node import (
|
|
19
|
+
NodeJoin,
|
|
21
20
|
StrategyNode,
|
|
22
21
|
resolve_concept_map,
|
|
23
|
-
NodeJoin,
|
|
24
22
|
)
|
|
25
|
-
from trilogy.core.processing.utility import
|
|
23
|
+
from trilogy.core.processing.utility import find_nullable_concepts, get_node_joins
|
|
24
|
+
from trilogy.utility import unique
|
|
26
25
|
|
|
27
26
|
LOGGER_PREFIX = "[CONCEPT DETAIL - MERGE NODE]"
|
|
28
27
|
|
|
@@ -37,7 +36,8 @@ def deduplicate_nodes(
|
|
|
37
36
|
set_map: dict[str, set[str]] = {}
|
|
38
37
|
for k, v in merged.items():
|
|
39
38
|
unique_outputs = [
|
|
40
|
-
environment.
|
|
39
|
+
# the concept may be a in a different environment for a rowset.
|
|
40
|
+
(environment.concepts.get(x.address) or x).address
|
|
41
41
|
for x in v.output_concepts
|
|
42
42
|
if x not in v.partial_concepts
|
|
43
43
|
]
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
from trilogy.constants import logger
|
|
5
4
|
from trilogy.core.constants import CONSTANT_DATASET
|
|
6
5
|
from trilogy.core.enums import Purpose, PurposeLineage
|
|
7
6
|
from trilogy.core.models import (
|
|
7
|
+
Comparison,
|
|
8
|
+
Concept,
|
|
9
|
+
Conditional,
|
|
10
|
+
Datasource,
|
|
11
|
+
Environment,
|
|
8
12
|
Function,
|
|
9
13
|
Grain,
|
|
14
|
+
Parenthetical,
|
|
10
15
|
QueryDatasource,
|
|
11
16
|
SourceType,
|
|
12
|
-
Concept,
|
|
13
|
-
Environment,
|
|
14
17
|
UnnestJoin,
|
|
15
|
-
Datasource,
|
|
16
|
-
Conditional,
|
|
17
|
-
Comparison,
|
|
18
|
-
Parenthetical,
|
|
19
18
|
)
|
|
20
|
-
from trilogy.utility import unique
|
|
21
19
|
from trilogy.core.processing.nodes.base_node import StrategyNode, resolve_concept_map
|
|
22
|
-
|
|
20
|
+
from trilogy.utility import unique
|
|
23
21
|
|
|
24
22
|
LOGGER_PREFIX = "[CONCEPT DETAIL - SELECT NODE]"
|
|
25
23
|
|
|
@@ -98,6 +96,7 @@ class SelectNode(StrategyNode):
|
|
|
98
96
|
PurposeLineage.BASIC,
|
|
99
97
|
PurposeLineage.ROWSET,
|
|
100
98
|
PurposeLineage.BASIC,
|
|
99
|
+
PurposeLineage.UNION,
|
|
101
100
|
):
|
|
102
101
|
source_map[x.address] = set()
|
|
103
102
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from trilogy.core.models import (
|
|
4
|
+
Concept,
|
|
5
|
+
QueryDatasource,
|
|
6
|
+
SourceType,
|
|
7
|
+
)
|
|
8
|
+
from trilogy.core.processing.nodes.base_node import StrategyNode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class UnionNode(StrategyNode):
|
|
12
|
+
"""Union nodes represent combining two keyspaces"""
|
|
13
|
+
|
|
14
|
+
source_type = SourceType.UNION
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
input_concepts: List[Concept],
|
|
19
|
+
output_concepts: List[Concept],
|
|
20
|
+
environment,
|
|
21
|
+
g,
|
|
22
|
+
whole_grain: bool = False,
|
|
23
|
+
parents: List["StrategyNode"] | None = None,
|
|
24
|
+
depth: int = 0,
|
|
25
|
+
):
|
|
26
|
+
super().__init__(
|
|
27
|
+
input_concepts=input_concepts,
|
|
28
|
+
output_concepts=output_concepts,
|
|
29
|
+
environment=environment,
|
|
30
|
+
g=g,
|
|
31
|
+
whole_grain=whole_grain,
|
|
32
|
+
parents=parents,
|
|
33
|
+
depth=depth,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def _resolve(self) -> QueryDatasource:
|
|
37
|
+
"""We need to ensure that any filtered values are removed from the output to avoid inappropriate references"""
|
|
38
|
+
base = super()._resolve()
|
|
39
|
+
return base
|
|
40
|
+
|
|
41
|
+
def copy(self) -> "UnionNode":
|
|
42
|
+
return UnionNode(
|
|
43
|
+
input_concepts=list(self.input_concepts),
|
|
44
|
+
output_concepts=list(self.output_concepts),
|
|
45
|
+
environment=self.environment,
|
|
46
|
+
g=self.g,
|
|
47
|
+
whole_grain=self.whole_grain,
|
|
48
|
+
parents=self.parents,
|
|
49
|
+
depth=self.depth,
|
|
50
|
+
)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from trilogy.core.
|
|
5
|
-
from trilogy.core.processing.nodes.base_node import StrategyNode, QueryDatasource
|
|
3
|
+
from trilogy.core.models import Concept, SourceType
|
|
4
|
+
from trilogy.core.processing.nodes.base_node import QueryDatasource, StrategyNode
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class WindowNode(StrategyNode):
|
|
@@ -1,52 +1,48 @@
|
|
|
1
|
-
from
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from logging import Logger
|
|
4
|
+
from typing import Any, Dict, List, Set, Tuple
|
|
5
|
+
|
|
2
6
|
import networkx as nx
|
|
7
|
+
|
|
8
|
+
from trilogy.core.enums import BooleanOperator, FunctionClass, Granularity, Purpose
|
|
3
9
|
from trilogy.core.models import (
|
|
4
|
-
|
|
5
|
-
|
|
10
|
+
CTE,
|
|
11
|
+
AggregateWrapper,
|
|
6
12
|
BaseJoin,
|
|
13
|
+
CaseElse,
|
|
14
|
+
CaseWhen,
|
|
15
|
+
Comparison,
|
|
7
16
|
Concept,
|
|
8
|
-
|
|
9
|
-
LooseConceptList,
|
|
10
|
-
Environment,
|
|
17
|
+
ConceptPair,
|
|
11
18
|
Conditional,
|
|
12
|
-
|
|
13
|
-
Comparison,
|
|
14
|
-
Parenthetical,
|
|
15
|
-
Function,
|
|
16
|
-
FilterItem,
|
|
17
|
-
MagicConstants,
|
|
18
|
-
WindowItem,
|
|
19
|
-
AggregateWrapper,
|
|
19
|
+
Datasource,
|
|
20
20
|
DataType,
|
|
21
|
-
ConceptPair,
|
|
22
|
-
UnnestJoin,
|
|
23
|
-
CaseWhen,
|
|
24
|
-
CaseElse,
|
|
25
|
-
MapWrapper,
|
|
26
|
-
ListWrapper,
|
|
27
|
-
MapType,
|
|
28
21
|
DatePart,
|
|
29
|
-
|
|
22
|
+
Environment,
|
|
23
|
+
FilterItem,
|
|
24
|
+
Function,
|
|
25
|
+
JoinType,
|
|
30
26
|
ListType,
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
ListWrapper,
|
|
28
|
+
LooseConceptList,
|
|
29
|
+
MagicConstants,
|
|
30
|
+
MapType,
|
|
31
|
+
MapWrapper,
|
|
33
32
|
MultiSelectStatement,
|
|
34
|
-
|
|
33
|
+
NumericType,
|
|
34
|
+
Parenthetical,
|
|
35
35
|
ProcessedQuery,
|
|
36
|
+
QueryDatasource,
|
|
37
|
+
SelectStatement,
|
|
38
|
+
SubselectComparison,
|
|
39
|
+
TupleWrapper,
|
|
40
|
+
UnionCTE,
|
|
41
|
+
UnnestJoin,
|
|
42
|
+
WindowItem,
|
|
36
43
|
)
|
|
37
|
-
|
|
38
|
-
from trilogy.core.enums import Purpose, Granularity, BooleanOperator
|
|
39
|
-
from enum import Enum
|
|
40
44
|
from trilogy.utility import unique
|
|
41
45
|
|
|
42
|
-
from logging import Logger
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
from trilogy.core.enums import FunctionClass
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
from dataclasses import dataclass
|
|
49
|
-
|
|
50
46
|
|
|
51
47
|
class NodeType(Enum):
|
|
52
48
|
CONCEPT = 1
|
|
@@ -314,7 +310,6 @@ def get_node_joins(
|
|
|
314
310
|
environment: Environment,
|
|
315
311
|
# concepts:List[Concept],
|
|
316
312
|
):
|
|
317
|
-
|
|
318
313
|
graph = nx.Graph()
|
|
319
314
|
partials: dict[str, list[str]] = {}
|
|
320
315
|
ds_node_map: dict[str, QueryDatasource | Datasource] = {}
|
|
@@ -536,7 +531,9 @@ def find_nullable_concepts(
|
|
|
536
531
|
return list(sorted(final_nullable))
|
|
537
532
|
|
|
538
533
|
|
|
539
|
-
def sort_select_output_processed(
|
|
534
|
+
def sort_select_output_processed(
|
|
535
|
+
cte: CTE | UnionCTE, query: ProcessedQuery
|
|
536
|
+
) -> CTE | UnionCTE:
|
|
540
537
|
hidden_addresses = [c.address for c in query.hidden_columns]
|
|
541
538
|
output_addresses = [
|
|
542
539
|
c.address for c in query.output_columns if c.address not in hidden_addresses
|
|
@@ -552,8 +549,8 @@ def sort_select_output_processed(cte: CTE, query: ProcessedQuery) -> CTE:
|
|
|
552
549
|
|
|
553
550
|
|
|
554
551
|
def sort_select_output(
|
|
555
|
-
cte: CTE, query: SelectStatement | MultiSelectStatement | ProcessedQuery
|
|
556
|
-
) -> CTE:
|
|
552
|
+
cte: CTE | UnionCTE, query: SelectStatement | MultiSelectStatement | ProcessedQuery
|
|
553
|
+
) -> CTE | UnionCTE:
|
|
557
554
|
if isinstance(query, ProcessedQuery):
|
|
558
555
|
return sort_select_output_processed(cte, query)
|
|
559
556
|
hidden_addresses = [c.address for c in query.hidden_components]
|