pytrilogy 0.0.2.10__tar.gz → 0.0.2.11__tar.gz
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.10/pytrilogy.egg-info → pytrilogy-0.0.2.11}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_models.py +18 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_parsing.py +24 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/models.py +26 -5
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/concept_strategies_v3.py +2 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/filter_node.py +20 -3
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/group_node.py +2 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/node_merge_node.py +6 -1
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/query_processor.py +11 -3
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/base.py +1 -4
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/parse_engine.py +41 -14
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/trilogy.lark +5 -1
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/README.md +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pyproject.toml +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/setup.cfg +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/setup.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_select.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/utility.py +0 -0
|
@@ -12,6 +12,7 @@ from trilogy.core.models import (
|
|
|
12
12
|
Comparison,
|
|
13
13
|
Join,
|
|
14
14
|
JoinKey,
|
|
15
|
+
Concept,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
|
|
@@ -69,6 +70,23 @@ def test_concept(test_environment, test_environment_graph):
|
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
|
|
73
|
+
def test_concept_filter(test_environment, test_environment_graph):
|
|
74
|
+
test_concept: Concept = list(test_environment.concepts.values())[0]
|
|
75
|
+
new = test_concept.with_filter(
|
|
76
|
+
Comparison(left=1, right="abc", operator=ComparisonOperator.EQ)
|
|
77
|
+
)
|
|
78
|
+
new2 = test_concept.with_filter(
|
|
79
|
+
Comparison(left=1, right="abc", operator=ComparisonOperator.EQ)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
assert new.name == new2.name != test_concept.name
|
|
83
|
+
|
|
84
|
+
new3 = new.with_filter(
|
|
85
|
+
Comparison(left=1, right="abc", operator=ComparisonOperator.EQ)
|
|
86
|
+
)
|
|
87
|
+
assert new3 == new
|
|
88
|
+
|
|
89
|
+
|
|
72
90
|
def test_conditional(test_environment, test_environment_graph):
|
|
73
91
|
test_concept = list(test_environment.concepts.values())[-1]
|
|
74
92
|
|
|
@@ -492,3 +492,27 @@ select x;
|
|
|
492
492
|
results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
|
|
493
493
|
|
|
494
494
|
assert "abcdef as test" in results, results
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def test_filter_concise():
|
|
498
|
+
|
|
499
|
+
text = """
|
|
500
|
+
key x int;
|
|
501
|
+
key y int;
|
|
502
|
+
|
|
503
|
+
datasource test (
|
|
504
|
+
x:x,
|
|
505
|
+
y:y)
|
|
506
|
+
grain(x)
|
|
507
|
+
address `abc:def`
|
|
508
|
+
;
|
|
509
|
+
|
|
510
|
+
auto filtered_test <- x ? y > 10;
|
|
511
|
+
|
|
512
|
+
select filtered_test;
|
|
513
|
+
"""
|
|
514
|
+
env, parsed = parse_text(text)
|
|
515
|
+
|
|
516
|
+
results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
|
|
517
|
+
|
|
518
|
+
assert "filtered_test" in results, results
|
|
@@ -801,15 +801,18 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
|
|
|
801
801
|
) -> "Concept":
|
|
802
802
|
from trilogy.utility import string_to_hash
|
|
803
803
|
|
|
804
|
-
|
|
804
|
+
if self.lineage and isinstance(self.lineage, FilterItem):
|
|
805
|
+
if self.lineage.where.conditional == condition:
|
|
806
|
+
return self
|
|
807
|
+
hash = string_to_hash(self.name + str(condition))
|
|
805
808
|
new = Concept(
|
|
806
|
-
name=f"{self.name}
|
|
809
|
+
name=f"{self.name}_filter_{hash}",
|
|
807
810
|
datatype=self.datatype,
|
|
808
811
|
purpose=self.purpose,
|
|
809
812
|
metadata=self.metadata,
|
|
810
813
|
lineage=FilterItem(content=self, where=WhereClause(conditional=condition)),
|
|
811
|
-
keys=None,
|
|
812
|
-
grain=
|
|
814
|
+
keys=(self.keys if self.purpose == Purpose.PROPERTY else None),
|
|
815
|
+
grain=self.grain if self.grain else Grain(components=[]),
|
|
813
816
|
namespace=self.namespace,
|
|
814
817
|
modifiers=self.modifiers,
|
|
815
818
|
pseudonyms=self.pseudonyms,
|
|
@@ -842,6 +845,16 @@ class Grain(Mergeable, BaseModel):
|
|
|
842
845
|
v2 = sorted(final, key=lambda x: x.name)
|
|
843
846
|
return v2
|
|
844
847
|
|
|
848
|
+
def with_filter(
|
|
849
|
+
self,
|
|
850
|
+
condition: "Conditional | Comparison | Parenthetical",
|
|
851
|
+
environment: Environment | None = None,
|
|
852
|
+
) -> "Grain":
|
|
853
|
+
return Grain(
|
|
854
|
+
components=[c.with_filter(condition, environment) for c in self.components],
|
|
855
|
+
nested=self.nested,
|
|
856
|
+
)
|
|
857
|
+
|
|
845
858
|
@property
|
|
846
859
|
def components_copy(self) -> List[Concept]:
|
|
847
860
|
return [*self.components]
|
|
@@ -1680,6 +1693,9 @@ class SelectStatement(Mergeable, Namespaced, SelectTypeMixin, BaseModel):
|
|
|
1680
1693
|
)
|
|
1681
1694
|
):
|
|
1682
1695
|
output.append(item)
|
|
1696
|
+
# TODO: explore implicit filtering more
|
|
1697
|
+
# if self.where_clause.conditional and self.where_clause_category == SelectFiltering.IMPLICIT:
|
|
1698
|
+
# output =[x.with_filter(self.where_clause.conditional) for x in output]
|
|
1683
1699
|
return Grain(
|
|
1684
1700
|
components=unique(output, "address"), where_clause=self.where_clause
|
|
1685
1701
|
)
|
|
@@ -4074,8 +4090,13 @@ class RowsetDerivationStatement(Namespaced, BaseModel):
|
|
|
4074
4090
|
output: list[Concept] = []
|
|
4075
4091
|
orig: dict[str, Concept] = {}
|
|
4076
4092
|
for orig_concept in self.select.output_components:
|
|
4093
|
+
name = orig_concept.name
|
|
4094
|
+
if isinstance(orig_concept.lineage, FilterItem):
|
|
4095
|
+
if orig_concept.lineage.where == self.select.where_clause:
|
|
4096
|
+
name = orig_concept.lineage.content.name
|
|
4097
|
+
|
|
4077
4098
|
new_concept = Concept(
|
|
4078
|
-
name=
|
|
4099
|
+
name=name,
|
|
4079
4100
|
datatype=orig_concept.datatype,
|
|
4080
4101
|
purpose=orig_concept.purpose,
|
|
4081
4102
|
lineage=RowsetItem(
|
|
@@ -191,6 +191,7 @@ def generate_candidates_restrictive(
|
|
|
191
191
|
):
|
|
192
192
|
combos.append(local_candidates)
|
|
193
193
|
combos.append(grain_check)
|
|
194
|
+
# combos.append(local_candidates)
|
|
194
195
|
# append the empty set for sourcing concept by itself last
|
|
195
196
|
combos.append([])
|
|
196
197
|
return combos
|
|
@@ -645,6 +646,7 @@ def _search_concepts(
|
|
|
645
646
|
depth=depth,
|
|
646
647
|
source_concepts=search_concepts,
|
|
647
648
|
history=history,
|
|
649
|
+
search_conditions=conditions,
|
|
648
650
|
)
|
|
649
651
|
|
|
650
652
|
if expanded:
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
@@ -39,6 +39,15 @@ def gen_filter_node(
|
|
|
39
39
|
raise SyntaxError('Filter node must have a lineage of type "FilterItem"')
|
|
40
40
|
where = concept.lineage.where
|
|
41
41
|
|
|
42
|
+
optional_included: list[Concept] = []
|
|
43
|
+
for x in local_optional:
|
|
44
|
+
if isinstance(x.lineage, FilterItem):
|
|
45
|
+
if concept.lineage.where == where:
|
|
46
|
+
logger.info(
|
|
47
|
+
f"{padding(depth)}{LOGGER_PREFIX} fetching {x.lineage.content.address} as optional parent with same filter conditions "
|
|
48
|
+
)
|
|
49
|
+
parent_row_concepts.append(x.lineage.content)
|
|
50
|
+
optional_included.append(x)
|
|
42
51
|
logger.info(
|
|
43
52
|
f"{padding(depth)}{LOGGER_PREFIX} filter {concept.address} derived from {immediate_parent.address} row parents {[x.address for x in parent_row_concepts]} and {[[y.address] for x in parent_existence_concepts for y in x]} existence parents"
|
|
44
53
|
)
|
|
@@ -49,6 +58,7 @@ def gen_filter_node(
|
|
|
49
58
|
g=g,
|
|
50
59
|
depth=depth + 1,
|
|
51
60
|
history=history,
|
|
61
|
+
conditions=conditions,
|
|
52
62
|
)
|
|
53
63
|
|
|
54
64
|
flattened_existence = [x for y in parent_existence_concepts for x in y]
|
|
@@ -88,6 +98,11 @@ def gen_filter_node(
|
|
|
88
98
|
f"{padding(depth)}{LOGGER_PREFIX} query conditions are the same as filter conditions, can optimize across all concepts"
|
|
89
99
|
)
|
|
90
100
|
optimized_pushdown = True
|
|
101
|
+
elif optional_included == local_optional:
|
|
102
|
+
logger.info(
|
|
103
|
+
f"{padding(depth)}{LOGGER_PREFIX} all optional concepts are included in the filter, can optimize across all concepts"
|
|
104
|
+
)
|
|
105
|
+
optimized_pushdown = True
|
|
91
106
|
if optimized_pushdown:
|
|
92
107
|
if isinstance(row_parent, SelectNode):
|
|
93
108
|
logger.info(
|
|
@@ -116,6 +131,7 @@ def gen_filter_node(
|
|
|
116
131
|
x
|
|
117
132
|
for x in local_optional
|
|
118
133
|
if x.address in [y.address for y in parent.output_concepts]
|
|
134
|
+
or x.address in [y.address for y in optional_included]
|
|
119
135
|
]
|
|
120
136
|
parent.add_parents(core_parents)
|
|
121
137
|
parent.add_condition(where.conditional)
|
|
@@ -175,6 +191,7 @@ def gen_filter_node(
|
|
|
175
191
|
] + outputs
|
|
176
192
|
filter_node.rebuild_cache()
|
|
177
193
|
return filter_node
|
|
194
|
+
|
|
178
195
|
enrich_node = source_concepts( # this fetches the parent + join keys
|
|
179
196
|
# to then connect to the rest of the query
|
|
180
197
|
mandatory_list=[immediate_parent] + parent_row_concepts + local_optional,
|
|
@@ -182,10 +199,11 @@ def gen_filter_node(
|
|
|
182
199
|
g=g,
|
|
183
200
|
depth=depth + 1,
|
|
184
201
|
history=history,
|
|
202
|
+
conditions=conditions,
|
|
185
203
|
)
|
|
186
204
|
if not enrich_node:
|
|
187
205
|
return filter_node
|
|
188
|
-
|
|
206
|
+
return MergeNode(
|
|
189
207
|
input_concepts=[concept, immediate_parent] + local_optional,
|
|
190
208
|
output_concepts=[
|
|
191
209
|
concept,
|
|
@@ -206,8 +224,7 @@ def gen_filter_node(
|
|
|
206
224
|
[immediate_parent] + parent_row_concepts
|
|
207
225
|
),
|
|
208
226
|
join_type=JoinType.LEFT_OUTER,
|
|
209
|
-
filter_to_mutual=
|
|
227
|
+
filter_to_mutual=True,
|
|
210
228
|
)
|
|
211
229
|
],
|
|
212
230
|
)
|
|
213
|
-
return x
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
-
from trilogy.core.models import Concept, Environment, Conditional
|
|
3
|
+
from trilogy.core.models import Concept, Environment, Conditional, WhereClause
|
|
4
4
|
from trilogy.core.processing.nodes import MergeNode, History, StrategyNode
|
|
5
5
|
import networkx as nx
|
|
6
6
|
from trilogy.core.graph_models import concept_to_node
|
|
@@ -260,6 +260,7 @@ def subgraphs_to_merge_node(
|
|
|
260
260
|
source_concepts,
|
|
261
261
|
history,
|
|
262
262
|
conditions,
|
|
263
|
+
search_conditions: WhereClause | None = None,
|
|
263
264
|
enable_early_exit: bool = True,
|
|
264
265
|
):
|
|
265
266
|
parents: List[StrategyNode] = []
|
|
@@ -277,6 +278,7 @@ def subgraphs_to_merge_node(
|
|
|
277
278
|
g=g,
|
|
278
279
|
depth=depth + 1,
|
|
279
280
|
history=history,
|
|
281
|
+
conditions=search_conditions,
|
|
280
282
|
)
|
|
281
283
|
if not parent:
|
|
282
284
|
logger.info(
|
|
@@ -315,6 +317,7 @@ def gen_merge_node(
|
|
|
315
317
|
accept_partial: bool = False,
|
|
316
318
|
history: History | None = None,
|
|
317
319
|
conditions: Conditional | None = None,
|
|
320
|
+
search_conditions: WhereClause | None = None,
|
|
318
321
|
) -> Optional[MergeNode]:
|
|
319
322
|
|
|
320
323
|
for filter_downstream in [True, False]:
|
|
@@ -339,6 +342,7 @@ def gen_merge_node(
|
|
|
339
342
|
source_concepts=source_concepts,
|
|
340
343
|
history=history,
|
|
341
344
|
conditions=conditions,
|
|
345
|
+
search_conditions=search_conditions,
|
|
342
346
|
)
|
|
343
347
|
# one concept handling may need to be kicked to alias
|
|
344
348
|
if len(all_concepts) == 1:
|
|
@@ -354,6 +358,7 @@ def gen_merge_node(
|
|
|
354
358
|
history=history,
|
|
355
359
|
conditions=conditions,
|
|
356
360
|
enable_early_exit=False,
|
|
361
|
+
search_conditions=search_conditions,
|
|
357
362
|
)
|
|
358
363
|
if test:
|
|
359
364
|
return test
|
|
@@ -145,7 +145,9 @@ def generate_source_map(
|
|
|
145
145
|
names = set([x.name for x in ev])
|
|
146
146
|
ematches = [cte.name for cte in all_new_ctes if cte.source.name in names]
|
|
147
147
|
existence_source_map[ek] = ematches
|
|
148
|
-
return {
|
|
148
|
+
return {
|
|
149
|
+
k: [] if not v else list(set(v)) for k, v in source_map.items()
|
|
150
|
+
}, existence_source_map
|
|
149
151
|
|
|
150
152
|
|
|
151
153
|
def datasource_to_query_datasource(datasource: Datasource) -> QueryDatasource:
|
|
@@ -191,6 +193,8 @@ def resolve_cte_base_name_and_alias_v2(
|
|
|
191
193
|
raw_joins: List[Join | InstantiatedUnnestJoin],
|
|
192
194
|
) -> Tuple[str | None, str | None]:
|
|
193
195
|
joins: List[Join] = [join for join in raw_joins if isinstance(join, Join)]
|
|
196
|
+
# INFO trilogy:query_processor.py:263 Finished building source map for civet with 3 parents, have {'local.relevant_customers': ['fowl', 'fowl'],
|
|
197
|
+
# 'customer.demographics.gender': ['mandrill'], 'customer.id': ['mandrill'], 'customer.demographics.id': ['mandrill'], 'customer.id_9268029262289908': [], 'customer.demographics.gender_1513806568509111': []}, query_datasource had non-empty keys ['local.relevant_customers', 'customer.demographics.gender', 'customer.id', 'customer.demographics.id'] and existence had non-empty keys []
|
|
194
198
|
if (
|
|
195
199
|
len(source.datasources) == 1
|
|
196
200
|
and isinstance(source.datasources[0], Datasource)
|
|
@@ -212,12 +216,16 @@ def resolve_cte_base_name_and_alias_v2(
|
|
|
212
216
|
|
|
213
217
|
counts: dict[str, int] = defaultdict(lambda: 0)
|
|
214
218
|
output_addresses = [x.address for x in source.output_concepts]
|
|
219
|
+
input_address = [x.address for x in source.input_concepts]
|
|
215
220
|
for k, v in source_map.items():
|
|
216
221
|
for vx in v:
|
|
217
222
|
if k in output_addresses:
|
|
218
223
|
counts[vx] = counts[vx] + 1
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
|
|
225
|
+
if k in input_address:
|
|
226
|
+
counts[vx] = counts[vx] + 1
|
|
227
|
+
|
|
228
|
+
counts[vx] = counts[vx]
|
|
221
229
|
if counts:
|
|
222
230
|
return max(counts, key=counts.get), max(counts, key=counts.get) # type: ignore
|
|
223
231
|
return None, None
|
|
@@ -281,10 +281,7 @@ class BaseDialect:
|
|
|
281
281
|
rval = f"{self.WINDOW_FUNCTION_MAP[c.lineage.type](concept = self.render_concept_sql(c.lineage.content, cte=cte, alias=False), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
|
|
282
282
|
elif isinstance(c.lineage, FilterItem):
|
|
283
283
|
# for cases when we've optimized this
|
|
284
|
-
if
|
|
285
|
-
len(cte.output_columns) == 1
|
|
286
|
-
and cte.condition == c.lineage.where.conditional
|
|
287
|
-
):
|
|
284
|
+
if cte.condition == c.lineage.where.conditional:
|
|
288
285
|
rval = self.render_expr(c.lineage.content, cte=cte)
|
|
289
286
|
else:
|
|
290
287
|
rval = f"CASE WHEN {self.render_expr(c.lineage.where.conditional, cte=cte)} THEN {self.render_concept_sql(c.lineage.content, cte=cte, alias=False)} ELSE NULL END"
|
|
@@ -983,6 +983,7 @@ class ParseToObjects(Transformer):
|
|
|
983
983
|
order_by=order_by,
|
|
984
984
|
meta=Metadata(line_number=meta.line),
|
|
985
985
|
)
|
|
986
|
+
|
|
986
987
|
for item in select_items:
|
|
987
988
|
# we don't know the grain of an aggregate at assignment time
|
|
988
989
|
# so rebuild at this point in the tree
|
|
@@ -1000,21 +1001,43 @@ class ParseToObjects(Transformer):
|
|
|
1000
1001
|
)
|
|
1001
1002
|
self.environment.add_concept(new_concept, meta=meta)
|
|
1002
1003
|
item.content.output = new_concept
|
|
1004
|
+
# TODO: revisit if we can push down every filter
|
|
1005
|
+
# else:
|
|
1006
|
+
# item.content = (
|
|
1007
|
+
# item.content.with_filter(
|
|
1008
|
+
# output.where_clause.conditional, environment=self.environment
|
|
1009
|
+
# )
|
|
1010
|
+
# if output.where_clause
|
|
1011
|
+
# and output.where_clause_category == SelectFiltering.IMPLICIT
|
|
1012
|
+
# else item.content
|
|
1013
|
+
# )
|
|
1014
|
+
|
|
1003
1015
|
if order_by:
|
|
1004
1016
|
for orderitem in order_by.items:
|
|
1005
|
-
if (
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1017
|
+
if isinstance(orderitem.expr, Concept):
|
|
1018
|
+
if orderitem.expr.purpose == Purpose.METRIC:
|
|
1019
|
+
orderitem.expr = orderitem.expr.with_select_context(
|
|
1020
|
+
output.grain,
|
|
1021
|
+
conditional=(
|
|
1022
|
+
output.where_clause.conditional
|
|
1023
|
+
if output.where_clause
|
|
1024
|
+
and output.where_clause_category
|
|
1025
|
+
== SelectFiltering.IMPLICIT
|
|
1026
|
+
else None
|
|
1027
|
+
),
|
|
1028
|
+
environment=self.environment,
|
|
1029
|
+
)
|
|
1030
|
+
# TODO :push down every filter
|
|
1031
|
+
# else:
|
|
1032
|
+
# orderitem.expr = (
|
|
1033
|
+
# orderitem.expr.with_filter(
|
|
1034
|
+
# output.where_clause.conditional,
|
|
1035
|
+
# environment=self.environment,
|
|
1036
|
+
# )
|
|
1037
|
+
# if output.where_clause
|
|
1038
|
+
# and output.where_clause_category == SelectFiltering.IMPLICIT
|
|
1039
|
+
# else orderitem.expr
|
|
1040
|
+
# )
|
|
1018
1041
|
return output
|
|
1019
1042
|
|
|
1020
1043
|
@v_args(meta=True)
|
|
@@ -1237,7 +1260,11 @@ class ParseToObjects(Transformer):
|
|
|
1237
1260
|
|
|
1238
1261
|
def filter_item(self, args) -> FilterItem:
|
|
1239
1262
|
where: WhereClause
|
|
1240
|
-
string_concept,
|
|
1263
|
+
string_concept, raw = args
|
|
1264
|
+
if isinstance(raw, WhereClause):
|
|
1265
|
+
where = raw
|
|
1266
|
+
else:
|
|
1267
|
+
where = WhereClause(conditional=raw)
|
|
1241
1268
|
concept = self.environment.concepts[string_concept]
|
|
1242
1269
|
return FilterItem(content=concept, where=where)
|
|
1243
1270
|
|
|
@@ -83,7 +83,11 @@
|
|
|
83
83
|
raw_function: "bind" "sql" IDENTIFIER "(" function_binding_list ")" "->" data_type "as"i MULTILINE_STRING
|
|
84
84
|
|
|
85
85
|
// user_id where state = Mexico
|
|
86
|
-
|
|
86
|
+
_filter_alt: IDENTIFIER "?" conditional
|
|
87
|
+
_filter_base: "filter"i IDENTIFIER where
|
|
88
|
+
filter_item: _filter_base | _filter_alt
|
|
89
|
+
|
|
90
|
+
|
|
87
91
|
|
|
88
92
|
// rank/lag/lead
|
|
89
93
|
WINDOW_TYPE: ("row_number"i|"rank"i|"lag"i|"lead"i | "sum"i) /[\s]+/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.10 → pytrilogy-0.0.2.11}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|