pytrilogy 0.0.3.68__tar.gz → 0.0.3.70__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.3.68/pytrilogy.egg-info → pytrilogy-0.0.3.70}/PKG-INFO +34 -19
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/README.md +33 -18
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70/pytrilogy.egg-info}/PKG-INFO +34 -19
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/functions.py +2 -2
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/author.py +1 -3
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/build.py +20 -7
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/discovery_node_factory.py +1 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/group_node.py +22 -3
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/select_merge_node.py +15 -8
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/query_processor.py +1 -2
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/base.py +39 -6
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/bigquery.py +19 -2
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/duckdb.py +17 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/common.py +3 -2
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/parse_engine.py +5 -1
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/trilogy.lark +1 -1
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/setup.cfg +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/setup.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/discovery_loop.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.70
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -40,46 +40,61 @@ Dynamic: summary
|
|
|
40
40
|
[](https://trilogydata.dev/)
|
|
41
41
|
[](https://discord.gg/Z4QSSuqGEd)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
The Trilogy language is an experiment in better SQL for analytics - a streamlined SQL that replaces tables/joins with a lightweight semantic binding layer and provides easy reuse and composability. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
[pytrilogy](https://github.com/trilogy-data/pytrilogy) is the reference implementation, written in Python.
|
|
46
|
+
|
|
47
|
+
Trilogy concretely solves these common problems in karge, SQL based analytics teams:
|
|
48
|
+
- decoupling consumption code from specific physical assets
|
|
49
|
+
- better testability and change management
|
|
50
|
+
- reduced boilerplate and opportunity for OLAP style optimization at scale
|
|
51
|
+
|
|
52
|
+
Trilogy can be especially powerful as a frontend consumption language, since the decoupling from the physical layout makes dynamic and interactive dashboards backed by SQL tables much easier to create.
|
|
46
53
|
|
|
47
54
|
> [!TIP]
|
|
48
|
-
>
|
|
55
|
+
> You can try Trilogy in a [open-source studio](https://trilogydata.dev/trilogy-studio-core/). More details on the language can be found on the [documentation](https://trilogydata.dev/).
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
We recommend the studio as the fastest way to explore Trilogy. For deeper work and integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
Installation: `pip install pytrilogy`
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
### Trilogy Looks Like SQL
|
|
55
62
|
|
|
56
|
-
Trilogy looks like SQL:
|
|
57
63
|
```sql
|
|
64
|
+
import names;
|
|
65
|
+
|
|
66
|
+
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
67
|
+
|
|
68
|
+
def initcap(word) -> upper(substring(word, 1, 1)) || substring(word, 2, len(word));
|
|
69
|
+
|
|
58
70
|
WHERE
|
|
59
|
-
name
|
|
71
|
+
@initcap(name) in top_names
|
|
60
72
|
SELECT
|
|
61
73
|
name,
|
|
62
|
-
|
|
74
|
+
sum(births) as name_count
|
|
63
75
|
ORDER BY
|
|
64
76
|
name_count desc
|
|
65
77
|
LIMIT 10;
|
|
66
78
|
```
|
|
67
79
|
## Goals
|
|
68
|
-
|
|
80
|
+
Versus SQL, Trilogy aims to:
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
Keep:
|
|
71
83
|
- Correctness
|
|
72
84
|
- Accessibility
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
Improve:
|
|
75
87
|
- Simplicity
|
|
76
|
-
- Understandability
|
|
77
88
|
- Refactoring/mantainability
|
|
78
89
|
- Reusability
|
|
79
90
|
|
|
80
91
|
Maintain:
|
|
81
92
|
- Acceptable performance
|
|
82
93
|
|
|
94
|
+
Remove:
|
|
95
|
+
- Lower-level procedural features
|
|
96
|
+
- Transactional optimizations/non-analytics features
|
|
97
|
+
|
|
83
98
|
## Hello World
|
|
84
99
|
|
|
85
100
|
Save the following code in a file named `hello.preql`
|
|
@@ -141,10 +156,7 @@ SELECT
|
|
|
141
156
|
sentences.text
|
|
142
157
|
;
|
|
143
158
|
|
|
144
|
-
|
|
145
|
-
|
|
146
159
|
```
|
|
147
|
-
|
|
148
160
|
Run the following from the directory the file is in.
|
|
149
161
|
|
|
150
162
|
```bash
|
|
@@ -157,11 +169,15 @@ trilogy run hello.trilogy duckdb
|
|
|
157
169
|
|
|
158
170
|
The current Trilogy implementation supports these backends:
|
|
159
171
|
|
|
172
|
+
### Core
|
|
160
173
|
- Bigquery
|
|
161
|
-
- SQL Server
|
|
162
174
|
- DuckDB
|
|
163
175
|
- Snowflake
|
|
164
176
|
|
|
177
|
+
### Experimental
|
|
178
|
+
- SQL Server
|
|
179
|
+
- Presto
|
|
180
|
+
|
|
165
181
|
## Basic Example - Python
|
|
166
182
|
|
|
167
183
|
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
@@ -170,7 +186,6 @@ A bigquery example, similar to bigquery [the quickstart](https://cloud.google.co
|
|
|
170
186
|
|
|
171
187
|
```python
|
|
172
188
|
|
|
173
|
-
|
|
174
189
|
from trilogy import Dialects, Environment
|
|
175
190
|
|
|
176
191
|
environment = Environment()
|
|
@@ -2,46 +2,61 @@
|
|
|
2
2
|
[](https://trilogydata.dev/)
|
|
3
3
|
[](https://discord.gg/Z4QSSuqGEd)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The Trilogy language is an experiment in better SQL for analytics - a streamlined SQL that replaces tables/joins with a lightweight semantic binding layer and provides easy reuse and composability. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[pytrilogy](https://github.com/trilogy-data/pytrilogy) is the reference implementation, written in Python.
|
|
8
|
+
|
|
9
|
+
Trilogy concretely solves these common problems in karge, SQL based analytics teams:
|
|
10
|
+
- decoupling consumption code from specific physical assets
|
|
11
|
+
- better testability and change management
|
|
12
|
+
- reduced boilerplate and opportunity for OLAP style optimization at scale
|
|
13
|
+
|
|
14
|
+
Trilogy can be especially powerful as a frontend consumption language, since the decoupling from the physical layout makes dynamic and interactive dashboards backed by SQL tables much easier to create.
|
|
8
15
|
|
|
9
16
|
> [!TIP]
|
|
10
|
-
>
|
|
17
|
+
> You can try Trilogy in a [open-source studio](https://trilogydata.dev/trilogy-studio-core/). More details on the language can be found on the [documentation](https://trilogydata.dev/).
|
|
11
18
|
|
|
12
|
-
|
|
19
|
+
We recommend the studio as the fastest way to explore Trilogy. For deeper work and integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
Installation: `pip install pytrilogy`
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
### Trilogy Looks Like SQL
|
|
17
24
|
|
|
18
|
-
Trilogy looks like SQL:
|
|
19
25
|
```sql
|
|
26
|
+
import names;
|
|
27
|
+
|
|
28
|
+
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
29
|
+
|
|
30
|
+
def initcap(word) -> upper(substring(word, 1, 1)) || substring(word, 2, len(word));
|
|
31
|
+
|
|
20
32
|
WHERE
|
|
21
|
-
name
|
|
33
|
+
@initcap(name) in top_names
|
|
22
34
|
SELECT
|
|
23
35
|
name,
|
|
24
|
-
|
|
36
|
+
sum(births) as name_count
|
|
25
37
|
ORDER BY
|
|
26
38
|
name_count desc
|
|
27
39
|
LIMIT 10;
|
|
28
40
|
```
|
|
29
41
|
## Goals
|
|
30
|
-
|
|
42
|
+
Versus SQL, Trilogy aims to:
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
Keep:
|
|
33
45
|
- Correctness
|
|
34
46
|
- Accessibility
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
Improve:
|
|
37
49
|
- Simplicity
|
|
38
|
-
- Understandability
|
|
39
50
|
- Refactoring/mantainability
|
|
40
51
|
- Reusability
|
|
41
52
|
|
|
42
53
|
Maintain:
|
|
43
54
|
- Acceptable performance
|
|
44
55
|
|
|
56
|
+
Remove:
|
|
57
|
+
- Lower-level procedural features
|
|
58
|
+
- Transactional optimizations/non-analytics features
|
|
59
|
+
|
|
45
60
|
## Hello World
|
|
46
61
|
|
|
47
62
|
Save the following code in a file named `hello.preql`
|
|
@@ -103,10 +118,7 @@ SELECT
|
|
|
103
118
|
sentences.text
|
|
104
119
|
;
|
|
105
120
|
|
|
106
|
-
|
|
107
|
-
|
|
108
121
|
```
|
|
109
|
-
|
|
110
122
|
Run the following from the directory the file is in.
|
|
111
123
|
|
|
112
124
|
```bash
|
|
@@ -119,11 +131,15 @@ trilogy run hello.trilogy duckdb
|
|
|
119
131
|
|
|
120
132
|
The current Trilogy implementation supports these backends:
|
|
121
133
|
|
|
134
|
+
### Core
|
|
122
135
|
- Bigquery
|
|
123
|
-
- SQL Server
|
|
124
136
|
- DuckDB
|
|
125
137
|
- Snowflake
|
|
126
138
|
|
|
139
|
+
### Experimental
|
|
140
|
+
- SQL Server
|
|
141
|
+
- Presto
|
|
142
|
+
|
|
127
143
|
## Basic Example - Python
|
|
128
144
|
|
|
129
145
|
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
@@ -132,7 +148,6 @@ A bigquery example, similar to bigquery [the quickstart](https://cloud.google.co
|
|
|
132
148
|
|
|
133
149
|
```python
|
|
134
150
|
|
|
135
|
-
|
|
136
151
|
from trilogy import Dialects, Environment
|
|
137
152
|
|
|
138
153
|
environment = Environment()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.70
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -40,46 +40,61 @@ Dynamic: summary
|
|
|
40
40
|
[](https://trilogydata.dev/)
|
|
41
41
|
[](https://discord.gg/Z4QSSuqGEd)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
The Trilogy language is an experiment in better SQL for analytics - a streamlined SQL that replaces tables/joins with a lightweight semantic binding layer and provides easy reuse and composability. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
[pytrilogy](https://github.com/trilogy-data/pytrilogy) is the reference implementation, written in Python.
|
|
46
|
+
|
|
47
|
+
Trilogy concretely solves these common problems in karge, SQL based analytics teams:
|
|
48
|
+
- decoupling consumption code from specific physical assets
|
|
49
|
+
- better testability and change management
|
|
50
|
+
- reduced boilerplate and opportunity for OLAP style optimization at scale
|
|
51
|
+
|
|
52
|
+
Trilogy can be especially powerful as a frontend consumption language, since the decoupling from the physical layout makes dynamic and interactive dashboards backed by SQL tables much easier to create.
|
|
46
53
|
|
|
47
54
|
> [!TIP]
|
|
48
|
-
>
|
|
55
|
+
> You can try Trilogy in a [open-source studio](https://trilogydata.dev/trilogy-studio-core/). More details on the language can be found on the [documentation](https://trilogydata.dev/).
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
We recommend the studio as the fastest way to explore Trilogy. For deeper work and integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
Installation: `pip install pytrilogy`
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
### Trilogy Looks Like SQL
|
|
55
62
|
|
|
56
|
-
Trilogy looks like SQL:
|
|
57
63
|
```sql
|
|
64
|
+
import names;
|
|
65
|
+
|
|
66
|
+
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
67
|
+
|
|
68
|
+
def initcap(word) -> upper(substring(word, 1, 1)) || substring(word, 2, len(word));
|
|
69
|
+
|
|
58
70
|
WHERE
|
|
59
|
-
name
|
|
71
|
+
@initcap(name) in top_names
|
|
60
72
|
SELECT
|
|
61
73
|
name,
|
|
62
|
-
|
|
74
|
+
sum(births) as name_count
|
|
63
75
|
ORDER BY
|
|
64
76
|
name_count desc
|
|
65
77
|
LIMIT 10;
|
|
66
78
|
```
|
|
67
79
|
## Goals
|
|
68
|
-
|
|
80
|
+
Versus SQL, Trilogy aims to:
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
Keep:
|
|
71
83
|
- Correctness
|
|
72
84
|
- Accessibility
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
Improve:
|
|
75
87
|
- Simplicity
|
|
76
|
-
- Understandability
|
|
77
88
|
- Refactoring/mantainability
|
|
78
89
|
- Reusability
|
|
79
90
|
|
|
80
91
|
Maintain:
|
|
81
92
|
- Acceptable performance
|
|
82
93
|
|
|
94
|
+
Remove:
|
|
95
|
+
- Lower-level procedural features
|
|
96
|
+
- Transactional optimizations/non-analytics features
|
|
97
|
+
|
|
83
98
|
## Hello World
|
|
84
99
|
|
|
85
100
|
Save the following code in a file named `hello.preql`
|
|
@@ -141,10 +156,7 @@ SELECT
|
|
|
141
156
|
sentences.text
|
|
142
157
|
;
|
|
143
158
|
|
|
144
|
-
|
|
145
|
-
|
|
146
159
|
```
|
|
147
|
-
|
|
148
160
|
Run the following from the directory the file is in.
|
|
149
161
|
|
|
150
162
|
```bash
|
|
@@ -157,11 +169,15 @@ trilogy run hello.trilogy duckdb
|
|
|
157
169
|
|
|
158
170
|
The current Trilogy implementation supports these backends:
|
|
159
171
|
|
|
172
|
+
### Core
|
|
160
173
|
- Bigquery
|
|
161
|
-
- SQL Server
|
|
162
174
|
- DuckDB
|
|
163
175
|
- Snowflake
|
|
164
176
|
|
|
177
|
+
### Experimental
|
|
178
|
+
- SQL Server
|
|
179
|
+
- Presto
|
|
180
|
+
|
|
165
181
|
## Basic Example - Python
|
|
166
182
|
|
|
167
183
|
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
@@ -170,7 +186,6 @@ A bigquery example, similar to bigquery [the quickstart](https://cloud.google.co
|
|
|
170
186
|
|
|
171
187
|
```python
|
|
172
188
|
|
|
173
|
-
|
|
174
189
|
from trilogy import Dialects, Environment
|
|
175
190
|
|
|
176
191
|
environment = Environment()
|
|
@@ -370,10 +370,10 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
370
370
|
arg_count=2,
|
|
371
371
|
),
|
|
372
372
|
FunctionType.REGEXP_EXTRACT: FunctionConfig(
|
|
373
|
-
valid_inputs={DataType.STRING},
|
|
373
|
+
valid_inputs=[{DataType.STRING}, {DataType.STRING}, {DataType.INTEGER}],
|
|
374
374
|
output_purpose=Purpose.PROPERTY,
|
|
375
375
|
output_type=DataType.STRING,
|
|
376
|
-
arg_count=
|
|
376
|
+
arg_count=3,
|
|
377
377
|
),
|
|
378
378
|
FunctionType.REGEXP_REPLACE: FunctionConfig(
|
|
379
379
|
valid_inputs={DataType.STRING},
|
|
@@ -2303,7 +2303,7 @@ class CustomFunctionFactory:
|
|
|
2303
2303
|
]
|
|
2304
2304
|
return self
|
|
2305
2305
|
|
|
2306
|
-
def __call__(self, *creation_args:
|
|
2306
|
+
def __call__(self, *creation_args: Expr):
|
|
2307
2307
|
nout = (
|
|
2308
2308
|
self.function.model_copy(deep=True)
|
|
2309
2309
|
if isinstance(self.function, BaseModel)
|
|
@@ -2389,7 +2389,6 @@ Expr = (
|
|
|
2389
2389
|
| int
|
|
2390
2390
|
| str
|
|
2391
2391
|
| float
|
|
2392
|
-
| list
|
|
2393
2392
|
| date
|
|
2394
2393
|
| datetime
|
|
2395
2394
|
| TupleWrapper
|
|
@@ -2430,7 +2429,6 @@ FuncArgs = (
|
|
|
2430
2429
|
| ListType
|
|
2431
2430
|
| MapType
|
|
2432
2431
|
| NumericType
|
|
2433
|
-
| list
|
|
2434
2432
|
| ListWrapper[Any]
|
|
2435
2433
|
| TupleWrapper[Any]
|
|
2436
2434
|
| Comparison
|
|
@@ -155,6 +155,7 @@ def concepts_to_build_grain_concepts(
|
|
|
155
155
|
pconcepts.append(c)
|
|
156
156
|
elif environment:
|
|
157
157
|
pconcepts.append(environment.concepts[c])
|
|
158
|
+
|
|
158
159
|
else:
|
|
159
160
|
raise ValueError(
|
|
160
161
|
f"Unable to resolve input {c} without environment provided to concepts_to_grain call"
|
|
@@ -248,7 +249,7 @@ def get_concept_arguments(expr) -> List["BuildConcept"]:
|
|
|
248
249
|
return output
|
|
249
250
|
|
|
250
251
|
|
|
251
|
-
class BuildParamaterizedConceptReference(BaseModel):
|
|
252
|
+
class BuildParamaterizedConceptReference(DataTyped, BaseModel):
|
|
252
253
|
concept: BuildConcept
|
|
253
254
|
|
|
254
255
|
def __str__(self):
|
|
@@ -258,6 +259,10 @@ class BuildParamaterizedConceptReference(BaseModel):
|
|
|
258
259
|
def safe_address(self) -> str:
|
|
259
260
|
return self.concept.safe_address
|
|
260
261
|
|
|
262
|
+
@property
|
|
263
|
+
def output_datatype(self) -> DataType:
|
|
264
|
+
return self.concept.output_datatype
|
|
265
|
+
|
|
261
266
|
|
|
262
267
|
class BuildGrain(BaseModel):
|
|
263
268
|
components: set[str] = Field(default_factory=set)
|
|
@@ -1810,8 +1815,8 @@ class Factory:
|
|
|
1810
1815
|
right_c, _ = self.instantiate_concept(base.right)
|
|
1811
1816
|
right = right_c
|
|
1812
1817
|
return BuildSubselectComparison.model_construct(
|
|
1813
|
-
left=self.build(base.left),
|
|
1814
|
-
right=self.build(right),
|
|
1818
|
+
left=self.handle_constant(self.build(base.left)),
|
|
1819
|
+
right=self.handle_constant(self.build(right)),
|
|
1815
1820
|
operator=base.operator,
|
|
1816
1821
|
)
|
|
1817
1822
|
|
|
@@ -1916,7 +1921,17 @@ class Factory:
|
|
|
1916
1921
|
where_factory = Factory(
|
|
1917
1922
|
grain=Grain(), environment=self.environment, local_concepts={}
|
|
1918
1923
|
)
|
|
1919
|
-
|
|
1924
|
+
where_clause = (
|
|
1925
|
+
where_factory.build(base.where_clause) if base.where_clause else None
|
|
1926
|
+
)
|
|
1927
|
+
# if the where clause derives new concepts
|
|
1928
|
+
# we need to ensure these are accessible from the general factory
|
|
1929
|
+
# post resolution
|
|
1930
|
+
for bk, bv in where_factory.local_concepts.items():
|
|
1931
|
+
# but do not override any local cahced grains
|
|
1932
|
+
if bk in materialized:
|
|
1933
|
+
continue
|
|
1934
|
+
materialized[bk] = bv
|
|
1920
1935
|
final: List[BuildConcept] = []
|
|
1921
1936
|
for original in base.selection:
|
|
1922
1937
|
new = original
|
|
@@ -1943,9 +1958,7 @@ class Factory:
|
|
|
1943
1958
|
factory.build(base.having_clause) if base.having_clause else None
|
|
1944
1959
|
),
|
|
1945
1960
|
# this uses a different grain factory
|
|
1946
|
-
where_clause=
|
|
1947
|
-
where_factory.build(base.where_clause) if base.where_clause else None
|
|
1948
|
-
),
|
|
1961
|
+
where_clause=where_clause,
|
|
1949
1962
|
)
|
|
1950
1963
|
|
|
1951
1964
|
@build.register
|
|
@@ -469,6 +469,7 @@ def generate_node(
|
|
|
469
469
|
Derivation.GROUP_TO: lambda: _generate_group_to_node(context),
|
|
470
470
|
Derivation.BASIC: lambda: _generate_basic_node(context),
|
|
471
471
|
Derivation.ROOT: lambda: RootNodeHandler(context).generate(),
|
|
472
|
+
Derivation.CONSTANT: lambda: RootNodeHandler(context).generate(),
|
|
472
473
|
}
|
|
473
474
|
|
|
474
475
|
handler = derivation_handlers.get(concept.derivation)
|
{pytrilogy-0.0.3.68 → pytrilogy-0.0.3.70}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
@@ -22,6 +22,24 @@ from trilogy.utility import unique
|
|
|
22
22
|
LOGGER_PREFIX = "[GEN_GROUP_NODE]"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
def get_aggregate_grain(
|
|
26
|
+
concept: BuildConcept, environment: BuildEnvironment
|
|
27
|
+
) -> BuildGrain:
|
|
28
|
+
parent_concepts: List[BuildConcept] = unique(
|
|
29
|
+
resolve_function_parent_concepts(concept, environment=environment), "address"
|
|
30
|
+
)
|
|
31
|
+
if (
|
|
32
|
+
concept.grain
|
|
33
|
+
and len(concept.grain.components) > 0
|
|
34
|
+
and not concept.grain.abstract
|
|
35
|
+
):
|
|
36
|
+
grain_components = [environment.concepts[c] for c in concept.grain.components]
|
|
37
|
+
parent_concepts += grain_components
|
|
38
|
+
return BuildGrain.from_concepts(parent_concepts)
|
|
39
|
+
else:
|
|
40
|
+
return BuildGrain.from_concepts(parent_concepts)
|
|
41
|
+
|
|
42
|
+
|
|
25
43
|
def gen_group_node(
|
|
26
44
|
concept: BuildConcept,
|
|
27
45
|
local_optional: List[BuildConcept],
|
|
@@ -51,7 +69,7 @@ def gen_group_node(
|
|
|
51
69
|
):
|
|
52
70
|
grain_components = [environment.concepts[c] for c in concept.grain.components]
|
|
53
71
|
parent_concepts += grain_components
|
|
54
|
-
build_grain_parents =
|
|
72
|
+
build_grain_parents = get_aggregate_grain(concept, environment)
|
|
55
73
|
output_concepts += grain_components
|
|
56
74
|
for possible_agg in local_optional:
|
|
57
75
|
|
|
@@ -70,6 +88,7 @@ def gen_group_node(
|
|
|
70
88
|
possible_agg,
|
|
71
89
|
environment=environment,
|
|
72
90
|
)
|
|
91
|
+
comp_grain = get_aggregate_grain(possible_agg, environment)
|
|
73
92
|
if set([x.address for x in agg_parents]).issubset(
|
|
74
93
|
set([x.address for x in parent_concepts])
|
|
75
94
|
):
|
|
@@ -77,7 +96,7 @@ def gen_group_node(
|
|
|
77
96
|
logger.info(
|
|
78
97
|
f"{padding(depth)}{LOGGER_PREFIX} found equivalent group by optional concept {possible_agg.address} for {concept.address}"
|
|
79
98
|
)
|
|
80
|
-
elif
|
|
99
|
+
elif comp_grain == build_grain_parents:
|
|
81
100
|
extra = [x for x in agg_parents if x.address not in parent_concepts]
|
|
82
101
|
parent_concepts += extra
|
|
83
102
|
output_concepts.append(possible_agg)
|
|
@@ -86,7 +105,7 @@ def gen_group_node(
|
|
|
86
105
|
)
|
|
87
106
|
else:
|
|
88
107
|
logger.info(
|
|
89
|
-
f"{padding(depth)}{LOGGER_PREFIX} cannot include optional agg {possible_agg.address}; mismatched parent grain {
|
|
108
|
+
f"{padding(depth)}{LOGGER_PREFIX} cannot include optional agg {possible_agg.address}; it has mismatched parent grain {comp_grain } vs local parent {build_grain_parents}"
|
|
90
109
|
)
|
|
91
110
|
if parent_concepts:
|
|
92
111
|
logger.info(
|
|
@@ -231,7 +231,10 @@ def create_pruned_concept_graph(
|
|
|
231
231
|
|
|
232
232
|
|
|
233
233
|
def resolve_subgraphs(
|
|
234
|
-
g: nx.DiGraph,
|
|
234
|
+
g: nx.DiGraph,
|
|
235
|
+
relevant: list[BuildConcept],
|
|
236
|
+
conditions: BuildWhereClause | None,
|
|
237
|
+
depth: int = 0,
|
|
235
238
|
) -> dict[str, list[str]]:
|
|
236
239
|
"""When we have multiple distinct subgraphs within our matched
|
|
237
240
|
nodes that can satisfy a query, resolve which one of those we should
|
|
@@ -241,7 +244,7 @@ def resolve_subgraphs(
|
|
|
241
244
|
discarding duplicates.
|
|
242
245
|
Duplicate subgraphs will be resolved based on which
|
|
243
246
|
ones are most 'optimal' to use, a hueristic
|
|
244
|
-
that can evolve in the future but is currently based on
|
|
247
|
+
that can evolve in the future but is currently based on datasource
|
|
245
248
|
cardinality."""
|
|
246
249
|
datasources = [n for n in g.nodes if n.startswith("ds~")]
|
|
247
250
|
subgraphs: dict[str, list[str]] = {
|
|
@@ -261,7 +264,7 @@ def resolve_subgraphs(
|
|
|
261
264
|
pruned_subgraphs = {}
|
|
262
265
|
|
|
263
266
|
def score_node(input: str):
|
|
264
|
-
logger.debug(f"scoring node {input}")
|
|
267
|
+
logger.debug(f"{padding(depth)}{LOGGER_PREFIX} scoring node {input}")
|
|
265
268
|
grain = grain_length[input]
|
|
266
269
|
# first - go for lowest grain
|
|
267
270
|
# but if the object we want is in the grain, treat that as "free"
|
|
@@ -275,7 +278,7 @@ def resolve_subgraphs(
|
|
|
275
278
|
len(subgraphs[input]),
|
|
276
279
|
input,
|
|
277
280
|
)
|
|
278
|
-
logger.debug(score)
|
|
281
|
+
logger.debug(f"{padding(depth)}{LOGGER_PREFIX} node {input} has score {score}")
|
|
279
282
|
return score
|
|
280
283
|
|
|
281
284
|
for key, nodes in subgraphs.items():
|
|
@@ -296,7 +299,7 @@ def resolve_subgraphs(
|
|
|
296
299
|
if len(value) < len(other_value):
|
|
297
300
|
is_subset = True
|
|
298
301
|
logger.debug(
|
|
299
|
-
f"Dropping subgraph {key} with {value} as it is a subset of {other_key} with {other_value}"
|
|
302
|
+
f"{padding(depth)}{LOGGER_PREFIX} Dropping subgraph {key} with {value} as it is a subset of {other_key} with {other_value}"
|
|
300
303
|
)
|
|
301
304
|
elif len(value) == len(other_value) and len(all_concepts) == len(
|
|
302
305
|
other_all_concepts
|
|
@@ -305,7 +308,9 @@ def resolve_subgraphs(
|
|
|
305
308
|
matches.add(key)
|
|
306
309
|
if matches and not is_subset:
|
|
307
310
|
min_node = min(matches, key=score_node)
|
|
308
|
-
logger.debug(
|
|
311
|
+
logger.debug(
|
|
312
|
+
f"{padding(depth)}{LOGGER_PREFIX} minimum source score is {min_node}"
|
|
313
|
+
)
|
|
309
314
|
is_subset = key is not min(matches, key=score_node)
|
|
310
315
|
if not is_subset:
|
|
311
316
|
pruned_subgraphs[key] = nodes
|
|
@@ -330,7 +335,9 @@ def resolve_subgraphs(
|
|
|
330
335
|
> 1
|
|
331
336
|
)
|
|
332
337
|
if not keep:
|
|
333
|
-
logger.debug(
|
|
338
|
+
logger.debug(
|
|
339
|
+
f"{padding(depth)}{LOGGER_PREFIX} Pruning node {node} as irrelevant after subgraph resolution"
|
|
340
|
+
)
|
|
334
341
|
pruned_subgraphs = {
|
|
335
342
|
k: [n for n in v if n != node] for k, v in pruned_subgraphs.items()
|
|
336
343
|
}
|
|
@@ -561,7 +568,7 @@ def gen_select_merge_node(
|
|
|
561
568
|
return None
|
|
562
569
|
|
|
563
570
|
sub_nodes = resolve_subgraphs(
|
|
564
|
-
pruned_concept_graph, relevant=non_constant, conditions=conditions
|
|
571
|
+
pruned_concept_graph, relevant=non_constant, conditions=conditions, depth=depth
|
|
565
572
|
)
|
|
566
573
|
|
|
567
574
|
logger.info(f"{padding(depth)}{LOGGER_PREFIX} fetching subgraphs {sub_nodes}")
|
|
@@ -395,14 +395,13 @@ def get_query_node(
|
|
|
395
395
|
if not statement.output_components:
|
|
396
396
|
raise ValueError(f"Statement has no output components {statement}")
|
|
397
397
|
history = history or History(base_environment=environment)
|
|
398
|
-
|
|
398
|
+
logger.info(
|
|
399
399
|
f"{LOGGER_PREFIX} building query node for {statement.output_components} grain {statement.grain}"
|
|
400
400
|
)
|
|
401
401
|
build_statement: BuildSelectLineage | BuildMultiSelectLineage = Factory(
|
|
402
402
|
environment=environment,
|
|
403
403
|
).build(statement)
|
|
404
404
|
|
|
405
|
-
# build_statement = statement
|
|
406
405
|
build_environment = environment.materialize_for_select(
|
|
407
406
|
build_statement.local_concepts
|
|
408
407
|
)
|