pytrilogy 0.0.1.118__py3-none-any.whl → 0.0.2.1__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.

Files changed (45) hide show
  1. {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.1.dist-info}/METADATA +1 -1
  2. pytrilogy-0.0.2.1.dist-info/RECORD +82 -0
  3. {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.1.dist-info}/WHEEL +1 -1
  4. trilogy/__init__.py +1 -1
  5. trilogy/constants.py +6 -0
  6. trilogy/core/enums.py +7 -2
  7. trilogy/core/env_processor.py +43 -19
  8. trilogy/core/functions.py +1 -0
  9. trilogy/core/models.py +666 -146
  10. trilogy/core/optimization.py +31 -28
  11. trilogy/core/optimizations/inline_constant.py +4 -1
  12. trilogy/core/optimizations/inline_datasource.py +25 -4
  13. trilogy/core/optimizations/predicate_pushdown.py +94 -54
  14. trilogy/core/processing/concept_strategies_v3.py +69 -39
  15. trilogy/core/processing/graph_utils.py +3 -3
  16. trilogy/core/processing/node_generators/__init__.py +0 -2
  17. trilogy/core/processing/node_generators/basic_node.py +30 -17
  18. trilogy/core/processing/node_generators/filter_node.py +3 -1
  19. trilogy/core/processing/node_generators/node_merge_node.py +345 -96
  20. trilogy/core/processing/node_generators/rowset_node.py +18 -16
  21. trilogy/core/processing/node_generators/select_node.py +44 -83
  22. trilogy/core/processing/nodes/__init__.py +2 -0
  23. trilogy/core/processing/nodes/base_node.py +22 -5
  24. trilogy/core/processing/nodes/filter_node.py +3 -0
  25. trilogy/core/processing/nodes/group_node.py +20 -2
  26. trilogy/core/processing/nodes/merge_node.py +32 -18
  27. trilogy/core/processing/nodes/select_node_v2.py +17 -3
  28. trilogy/core/processing/utility.py +100 -8
  29. trilogy/core/query_processor.py +77 -24
  30. trilogy/dialect/base.py +11 -46
  31. trilogy/dialect/bigquery.py +1 -1
  32. trilogy/dialect/common.py +11 -0
  33. trilogy/dialect/duckdb.py +1 -1
  34. trilogy/dialect/presto.py +1 -0
  35. trilogy/hooks/graph_hook.py +50 -5
  36. trilogy/hooks/query_debugger.py +1 -0
  37. trilogy/parsing/common.py +8 -5
  38. trilogy/parsing/parse_engine.py +48 -27
  39. trilogy/parsing/render.py +13 -6
  40. trilogy/parsing/trilogy.lark +12 -7
  41. pytrilogy-0.0.1.118.dist-info/RECORD +0 -83
  42. trilogy/core/processing/node_generators/concept_merge_node.py +0 -214
  43. {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.1.dist-info}/LICENSE.md +0 -0
  44. {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.1.dist-info}/entry_points.txt +0 -0
  45. {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.1.dist-info}/top_level.txt +0 -0
@@ -32,6 +32,7 @@ from trilogy.core.enums import (
32
32
  WindowType,
33
33
  DatePart,
34
34
  ShowCategory,
35
+ SelectFiltering,
35
36
  )
36
37
  from trilogy.core.exceptions import InvalidSyntaxException, UndefinedConceptException
37
38
  from trilogy.core.functions import (
@@ -68,7 +69,7 @@ from trilogy.core.models import (
68
69
  ConceptTransform,
69
70
  Conditional,
70
71
  Datasource,
71
- MergeStatement,
72
+ MergeStatementV2,
72
73
  Environment,
73
74
  FilterItem,
74
75
  Function,
@@ -400,7 +401,7 @@ class ParseToObjects(Transformer):
400
401
  @v_args(meta=True)
401
402
  def concept_property_declaration(self, meta: Meta, args) -> Concept:
402
403
 
403
- metadata = None
404
+ metadata = Metadata()
404
405
  modifiers = []
405
406
  for arg in args:
406
407
  if isinstance(arg, Metadata):
@@ -439,7 +440,7 @@ class ParseToObjects(Transformer):
439
440
 
440
441
  @v_args(meta=True)
441
442
  def concept_declaration(self, meta: Meta, args) -> ConceptDeclarationStatement:
442
- metadata = None
443
+ metadata = Metadata()
443
444
  modifiers = []
444
445
  for arg in args:
445
446
  if isinstance(arg, Metadata):
@@ -447,9 +448,7 @@ class ParseToObjects(Transformer):
447
448
  if isinstance(arg, Modifier):
448
449
  modifiers.append(arg)
449
450
  name = args[1]
450
- lookup, namespace, name, parent = parse_concept_reference(
451
- name, self.environment
452
- )
451
+ _, namespace, name, _ = parse_concept_reference(name, self.environment)
453
452
  concept = Concept(
454
453
  name=name,
455
454
  datatype=args[2],
@@ -754,19 +753,21 @@ class ParseToObjects(Transformer):
754
753
  return [x for x in args]
755
754
 
756
755
  @v_args(meta=True)
757
- def merge_statement(self, meta: Meta, args) -> MergeStatement:
758
- parsed = [self.environment.concepts[x] for x in args]
759
- datatypes = {x.datatype for x in parsed}
760
- if not len(datatypes) == 1 and self.environment.concepts.fail_on_missing:
761
- type_dict = {x.address: x.datatype for x in parsed}
762
- raise SyntaxError(
763
- f"Cannot merge concepts with different datatype"
764
- f"line: {meta.line} concepts: {type_dict}"
765
- )
766
- merge = MergeStatement(concepts=parsed, datatype=datatypes.pop())
767
- new = merge.merge_concept
768
- self.environment.add_concept(new, meta=meta)
769
- return merge
756
+ def merge_statement_v2(self, meta: Meta, args) -> MergeStatementV2:
757
+ modifiers = []
758
+ concepts = []
759
+ for arg in args:
760
+ if isinstance(arg, Modifier):
761
+ modifiers.append(arg)
762
+ else:
763
+ concepts.append(self.environment.concepts[arg])
764
+ new = MergeStatementV2(
765
+ source=concepts[0], target=concepts[1], modifiers=modifiers
766
+ )
767
+
768
+ self.environment.merge_concept(new.source, new.target, modifiers)
769
+
770
+ return new
770
771
 
771
772
  @v_args(meta=True)
772
773
  def rawsql_statement(self, meta: Meta, args) -> RawSQLStatement:
@@ -892,7 +893,7 @@ class ParseToObjects(Transformer):
892
893
 
893
894
  @v_args(meta=True)
894
895
  def select_statement(self, meta: Meta, args) -> SelectStatement:
895
- select_items = None
896
+ select_items: List[SelectItem] | None = None
896
897
  limit = None
897
898
  order_by = None
898
899
  where = None
@@ -919,16 +920,24 @@ class ParseToObjects(Transformer):
919
920
  # so rebuild at this point in the tree
920
921
  # TODO: simplify
921
922
  if isinstance(item.content, ConceptTransform):
922
- new_concept = item.content.output.with_select_grain(output.grain)
923
+ new_concept = item.content.output.with_select_context(
924
+ output.grain,
925
+ conditional=(
926
+ output.where_clause.conditional
927
+ if output.where_clause
928
+ and output.where_clause_category == SelectFiltering.IMPLICIT
929
+ else None
930
+ ),
931
+ )
923
932
  self.environment.add_concept(new_concept, meta=meta)
924
933
  item.content.output = new_concept
925
934
  if order_by:
926
- for item in order_by.items:
935
+ for orderitem in order_by.items:
927
936
  if (
928
- isinstance(item.expr, Concept)
929
- and item.expr.purpose == Purpose.METRIC
937
+ isinstance(orderitem.expr, Concept)
938
+ and orderitem.expr.purpose == Purpose.METRIC
930
939
  ):
931
- item.expr = item.expr.with_grain(output.grain)
940
+ orderitem.expr = orderitem.expr.with_grain(output.grain)
932
941
  return output
933
942
 
934
943
  @v_args(meta=True)
@@ -1045,8 +1054,20 @@ class ParseToObjects(Transformer):
1045
1054
  def parenthetical(self, args):
1046
1055
  return Parenthetical(content=args[0])
1047
1056
 
1057
+ def condition_parenthetical(self, args):
1058
+ return Parenthetical(content=args[0])
1059
+
1048
1060
  def conditional(self, args):
1049
- return Conditional(left=args[0], right=args[2], operator=args[1])
1061
+ def munch_args(args):
1062
+ while args:
1063
+ if len(args) == 1:
1064
+ return args[0]
1065
+ else:
1066
+ return Conditional(
1067
+ left=args[0], operator=args[1], right=munch_args(args[2:])
1068
+ )
1069
+
1070
+ return munch_args(args)
1050
1071
 
1051
1072
  def window_order(self, args):
1052
1073
  return WindowOrder(args[0])
@@ -1723,7 +1744,7 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
1723
1744
  | None
1724
1745
  ],
1725
1746
  ]:
1726
- environment = environment or Environment(datasources={})
1747
+ environment = environment or Environment()
1727
1748
  parser = ParseToObjects(visit_tokens=True, text=text, environment=environment)
1728
1749
 
1729
1750
  try:
trilogy/parsing/render.py CHANGED
@@ -33,13 +33,13 @@ from trilogy.core.models import (
33
33
  PersistStatement,
34
34
  ListWrapper,
35
35
  RowsetDerivationStatement,
36
- MergeStatement,
37
36
  MultiSelectStatement,
38
37
  OrderBy,
39
38
  AlignClause,
40
39
  AlignItem,
41
40
  RawSQLStatement,
42
41
  NumericType,
42
+ MergeStatementV2,
43
43
  )
44
44
  from trilogy.core.enums import Modifier
45
45
 
@@ -142,11 +142,6 @@ class Renderer:
142
142
  components = ",".join(self.to_string(x) for x in arg.components)
143
143
  return f"grain ({components})"
144
144
 
145
- @to_string.register
146
- def _(self, arg: MergeStatement):
147
- components = ", ".join(self.to_string(x) for x in arg.concepts)
148
- return f"merge {components};"
149
-
150
145
  @to_string.register
151
146
  def _(self, arg: "Query"):
152
147
  return f"""query {arg.text}"""
@@ -341,6 +336,18 @@ class Renderer:
341
336
  return f"{self.to_string(arg.function)} by {self.to_string(arg.by)}"
342
337
  return f"{self.to_string(arg.function)}"
343
338
 
339
+ @to_string.register
340
+ def _(self, arg: MergeStatementV2):
341
+ return f"MERGE {self.to_string(arg.source)} into {''.join([self.to_string(modifier) for modifier in arg.modifiers])}{self.to_string(arg.target)};"
342
+
343
+ @to_string.register
344
+ def _(self, arg: Modifier):
345
+ if arg == Modifier.PARTIAL:
346
+ return "~"
347
+ if arg == Modifier.HIDDEN:
348
+ return "--"
349
+ return arg.value
350
+
344
351
  @to_string.register
345
352
  def _(self, arg: int):
346
353
  return f"{arg}"
@@ -8,7 +8,8 @@
8
8
  | persist_statement
9
9
  | rowset_derivation_statement
10
10
  | import_statement
11
- | merge_statement
11
+
12
+ | merge_statement_v2
12
13
  | rawsql_statement
13
14
 
14
15
  _TERMINATOR: ";"i /\s*/
@@ -69,8 +70,8 @@
69
70
 
70
71
  align_clause: align_item ("AND"i align_item)* "AND"i?
71
72
 
72
- // merge statemment
73
- merge_statement: "merge" IDENTIFIER ("," IDENTIFIER)* ","?
73
+ // merge_v2
74
+ merge_statement_v2: "merge"i IDENTIFIER "into"i SHORTHAND_MODIFIER? IDENTIFIER
74
75
 
75
76
  // raw sql statement
76
77
  rawsql_statement: "raw_sql"i "(" MULTILINE_STRING ")"
@@ -125,9 +126,13 @@
125
126
 
126
127
  !logical_operator: "and"i | "or"i
127
128
 
128
- conditional: expr logical_operator expr
129
+ condition_parenthetical: "(" conditional ")"
130
+
131
+ _condition_unit: (expr | condition_parenthetical)
132
+
133
+ conditional: _condition_unit (logical_operator _condition_unit)*
129
134
 
130
- where: "WHERE"i expr+
135
+ where: "WHERE"i conditional
131
136
 
132
137
  !array_comparison: ( ("NOT"i "IN"i) | "IN"i)
133
138
 
@@ -150,7 +155,7 @@
150
155
  index_access: expr "[" int_lit "]"
151
156
  attr_access: expr "[" _string_lit "]"
152
157
 
153
- expr: _constant_functions | window_item | filter_item | subselect_comparison | between_comparison | fgroup | aggregate_functions | unnest | _static_functions | literal | concept_lit | index_access | attr_access | parenthetical | expr_tuple | comparison | conditional | alt_like
158
+ expr: _constant_functions | window_item | filter_item | subselect_comparison | between_comparison | fgroup | aggregate_functions | unnest | _static_functions | literal | concept_lit | index_access | attr_access | parenthetical | expr_tuple | comparison | alt_like
154
159
 
155
160
  // functions
156
161
 
@@ -168,7 +173,7 @@
168
173
  fcast: "cast"i "(" expr "as"i data_type ")"
169
174
  concat: ("concat"i "(" (expr ",")* expr ")") | (expr "||" expr)
170
175
  fcoalesce: "coalesce"i "(" (expr ",")* expr ")"
171
- fcase_when: "WHEN"i (expr | conditional) "THEN"i expr
176
+ fcase_when: "WHEN"i conditional "THEN"i expr
172
177
  fcase_else: "ELSE"i expr
173
178
  fcase: "CASE"i (fcase_when)* (fcase_else)? "END"i
174
179
  len: "len"i "(" expr ")"
@@ -1,83 +0,0 @@
1
- trilogy/__init__.py,sha256=fYN8J2EkHXTX5cKVWpbBMQHilscbkhmZF-pMLoMqHFc,292
2
- trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- trilogy/constants.py,sha256=u2dNxhwy0v-6HrvG1GcpDVvuhzdTH5fuyYNCxDPlr2E,770
4
- trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
5
- trilogy/executor.py,sha256=5cRbU4Rj7p1pNV76rfp1pz704Hx_0q8_O8HFURjgXxQ,11016
6
- trilogy/parser.py,sha256=UtuqSiGiCjpMAYgo1bvNq-b7NSzCA5hzbUW31RXaMII,281
7
- trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
9
- trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- trilogy/core/constants.py,sha256=LL8NLvxb3HRnAjvofyLRXqQJijLcYiXAQYQzGarVD-g,128
11
- trilogy/core/enums.py,sha256=9IzCv0Hzzr3OTJQWzg9yHV_b46Jvk47RNoZjdBi2Its,5678
12
- trilogy/core/env_processor.py,sha256=SU-jpaGfoWLe9sGTeQYG1qjVnwGQ7TwctmnJRlfzluc,1459
13
- trilogy/core/environment_helpers.py,sha256=mzBDHhdF9ssZ_-LY8CcaM_ddfJavkpRYrFImUd3cjXI,5972
14
- trilogy/core/ergonomics.py,sha256=w3gwXdgrxNHCuaRdyKg73t6F36tj-wIjQf47WZkHmJk,1465
15
- trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,561
16
- trilogy/core/functions.py,sha256=fWuuPOeWRAiukTkXvTEu_NCSbu6eUmogtLGpC4ey2FI,9465
17
- trilogy/core/graph_models.py,sha256=oJUMSpmYhqXlavckHLpR07GJxuQ8dZ1VbB1fB0KaS8c,2036
18
- trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
19
- trilogy/core/models.py,sha256=Mp6MB8Gl2CZcPDWkofPX8Mym1ZvT0OUS0dKAUWCj7K8,116419
20
- trilogy/core/optimization.py,sha256=oM3Ry7UpbpTSm2xNkmWx70OHd2V2vWRjM72sZpsZfb8,4116
21
- trilogy/core/query_processor.py,sha256=clIRJ6IcsqIVBPKFsxt8bqCLsLyajvAu02MUIcKQhTo,15713
22
- trilogy/core/optimizations/__init__.py,sha256=pxRzNzd2g8oRMy4f_ub5va6bNS2pd4hnyp9JBzTKc1E,300
23
- trilogy/core/optimizations/base_optimization.py,sha256=tWWT-xnTbnEU-mNi_isMNbywm8B9WTRsNFwGpeh3rqE,468
24
- trilogy/core/optimizations/inline_constant.py,sha256=neZOFjX7M2pzQ-8m-f8nApy_MfJuowX6SzcGwGFt5w4,927
25
- trilogy/core/optimizations/inline_datasource.py,sha256=BSp54fwF4RRwInd-09pggemC7JuXj-uqGzi32ufeqYo,2171
26
- trilogy/core/optimizations/predicate_pushdown.py,sha256=sIojWvoYp6k_ANCyVqxCpEyLY_GLmzsG-Sghj0cbk3k,4135
27
- trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- trilogy/core/processing/concept_strategies_v3.py,sha256=MYrpNMidqvPOg123RekOcqVTjcj03i_538gBo0MzoWE,23432
29
- trilogy/core/processing/graph_utils.py,sha256=ulCJ4hYAISbUxLD6VM2fah9RBPGIXSEHEPeRBSFl0Rs,1197
30
- trilogy/core/processing/utility.py,sha256=acxH5448-j8JXqxMRibyAxjz1Wqu7QudbR0PfMuucww,9902
31
- trilogy/core/processing/node_generators/__init__.py,sha256=LIs6uBEum8LDc-26zjyAwjxa-ay2ok9tKtPjDNvbVkE,757
32
- trilogy/core/processing/node_generators/basic_node.py,sha256=HJnIhZLgkUdorKYcofe-QnKSM3Lf_3QO91cbSJhsqf4,2242
33
- trilogy/core/processing/node_generators/common.py,sha256=liZDth7mvhkF_sUFXK7JitJsiaKD132w3ySLbF7l-nE,8956
34
- trilogy/core/processing/node_generators/concept_merge_node.py,sha256=x4M8VVZZmBcqHDY1uq7M9KGKCBwjU6mcE_x2BOEk2Mg,7328
35
- trilogy/core/processing/node_generators/filter_node.py,sha256=y_tqYe2So18vWHASMwVPLzDO-PnyQCO-MAlI4B-rY3Y,4526
36
- trilogy/core/processing/node_generators/group_node.py,sha256=xWI1xNIXEOj6jlRGD9hcv2_vVNvY6lpzJl6pQ8HuFBE,2988
37
- trilogy/core/processing/node_generators/group_to_node.py,sha256=BzPdYwzoo8gRMH7BDffTTXq4z-mjfCEzvfB5I-P0_nw,2941
38
- trilogy/core/processing/node_generators/multiselect_node.py,sha256=vP84dnLQy6dtypi6mUbt9sMAcmmrTgQ1Oz4GI6X1IEo,6421
39
- trilogy/core/processing/node_generators/node_merge_node.py,sha256=wNDHAbRrKSjsns-EROM_G12mRyOMjbcWpYav2uefXOE,6045
40
- trilogy/core/processing/node_generators/rowset_node.py,sha256=eNG6rfLifUKraoRGxE8pesQMy5cKT6R5XNIaa3Wuiwk,6081
41
- trilogy/core/processing/node_generators/select_node.py,sha256=Qb00Kizsv-877UMkGfusl5jXKXMZtZTtLks5pxU07SU,20698
42
- trilogy/core/processing/node_generators/unnest_node.py,sha256=6CH66eGwpadNX7TzUhWZ8aqIisOtQeHINbLV6X3QBUk,1779
43
- trilogy/core/processing/node_generators/window_node.py,sha256=9nXUXUgQrNczU1gaOqhOZPNzCUxw-lkxt0R7HORI6ss,2582
44
- trilogy/core/processing/nodes/__init__.py,sha256=baODkJfvUoWEEbu843GEd7snubwLeOG5FQ8l-CwIaC8,3928
45
- trilogy/core/processing/nodes/base_node.py,sha256=yhjmsAUmhHDqgbQjz_9YdfP-M5pj4xbrPRDF6Y4XVuw,10498
46
- trilogy/core/processing/nodes/filter_node.py,sha256=rDw4vfE6tqWxuKT0arihVmIOoOWDDCyzRA-2yONX_Ek,1860
47
- trilogy/core/processing/nodes/group_node.py,sha256=vzeU9J4xMhRrPj4-KPJTgNbH-KFu2ZS8b57SOynsdw0,4448
48
- trilogy/core/processing/nodes/merge_node.py,sha256=FvSiTWKOzaUsXBkf6wJD8QQqQxp_aphS_I5VzNRw8Yo,13600
49
- trilogy/core/processing/nodes/select_node_v2.py,sha256=ERCflBFzKpD5SzweMevnJLyQnxmF_-IQ6VRu5yVeiBg,6552
50
- trilogy/core/processing/nodes/unnest_node.py,sha256=JFtm90IVM-46aCYkTNIaJah6v9ApAfonjVhcVM1HmDE,1903
51
- trilogy/core/processing/nodes/window_node.py,sha256=X7qxLUKd3tekjUUsmH_4vz5b-U89gMnGd04VBxuu2Ns,1280
52
- trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- trilogy/dialect/base.py,sha256=ycsxbUL68DsodOsHEgEoNLKMn5vgRN3sDIRfiH9fQDs,31719
54
- trilogy/dialect/bigquery.py,sha256=9vxQn2BMv_oTGQSWQpoN5ho_OgqMWaHH9e-5vQVf44c,2906
55
- trilogy/dialect/common.py,sha256=zWrYmvevlXznocw9uGHmY5Ws1rp_kICm9zA_ulTe4eg,2165
56
- trilogy/dialect/config.py,sha256=tLVEMctaTDhUgARKXUNfHUcIolGaALkQ0RavUvXAY4w,2994
57
- trilogy/dialect/duckdb.py,sha256=Ddyt68sr8IL2HnZMenyytoD65FXwY_O2pz1McyS0bis,3075
58
- trilogy/dialect/enums.py,sha256=4NdpsydBpDn6jnh0JzFz5VvQEtnShErWtWHVyT6TNpw,3948
59
- trilogy/dialect/postgres.py,sha256=r47xbCA7nfEYENofiVfLZ-SnReNfDmUmW4OSHVkkP4E,3206
60
- trilogy/dialect/presto.py,sha256=UxBodRiV3szpFcQlcjoJaGXEwAhZJf_OT7dHczYvO80,3092
61
- trilogy/dialect/snowflake.py,sha256=N3HknYgN-fjD7BLX1Ucj-ss_ku2Ox8DgLsF3BIHutHo,2941
62
- trilogy/dialect/sql_server.py,sha256=HX68vNTrcDaTnOxe6Zbx_PBgrO42e2VuThxO6CYQ2cY,3026
63
- trilogy/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- trilogy/hooks/base_hook.py,sha256=Xkb-A2qCHozYjum0A36zOy5PwTVwrP3NLDF0U2GpgHo,1100
65
- trilogy/hooks/graph_hook.py,sha256=i-Tv9sxZU0sMc-God8bLLz-nAg4-wYafogZtHaU8LXw,801
66
- trilogy/hooks/query_debugger.py,sha256=D2VJUcyvQrVJ8sT6FCMKR3NKTfVrgZQ7gly91avHHpw,4222
67
- trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- trilogy/parsing/common.py,sha256=iR3fiiZ7w8VJuUGrQ0v06XGDXov81f4z1ZlFnj6y40E,5804
70
- trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
71
- trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
72
- trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
73
- trilogy/parsing/parse_engine.py,sha256=F1ok96qT6EhKRKV1Q_YzfHxMFtNV8qAXopK8NaePgU4,57080
74
- trilogy/parsing/render.py,sha256=TnLf5fg4wimpd9EvhLU-FMDwpyW9pesoedBZ0RrmWD4,11810
75
- trilogy/parsing/trilogy.lark,sha256=1AZbQGpNmpm4KamAXA5IWcuOr2B8Gb8kUJcAOmKf_zY,10862
76
- trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
78
- pytrilogy-0.0.1.118.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
79
- pytrilogy-0.0.1.118.dist-info/METADATA,sha256=tZ1kQcOTJ5JTYb5tqlfdo7g-o94TC0tKmcdqLtJ35bI,7878
80
- pytrilogy-0.0.1.118.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
81
- pytrilogy-0.0.1.118.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
82
- pytrilogy-0.0.1.118.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
83
- pytrilogy-0.0.1.118.dist-info/RECORD,,
@@ -1,214 +0,0 @@
1
- from trilogy.core.models import (
2
- Concept,
3
- Environment,
4
- MergeStatement,
5
- )
6
- from trilogy.core.processing.nodes import MergeNode, NodeJoin, History
7
- from trilogy.core.processing.nodes.base_node import concept_list_to_grain, StrategyNode
8
- from typing import List
9
-
10
- from trilogy.core.enums import JoinType, PurposeLineage
11
- from trilogy.constants import logger
12
- from trilogy.core.processing.utility import padding
13
- from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
14
- from itertools import combinations
15
- from trilogy.core.processing.node_generators.common import resolve_join_order
16
- from trilogy.utility import unique
17
-
18
-
19
- LOGGER_PREFIX = "[GEN_CONCEPT_MERGE_NODE]"
20
-
21
-
22
- def merge_joins(
23
- parents: List[StrategyNode], merge_concepts: List[Concept]
24
- ) -> List[NodeJoin]:
25
- output = []
26
- for left, right in combinations(parents, 2):
27
- output.append(
28
- NodeJoin(
29
- left_node=left,
30
- right_node=right,
31
- concepts=merge_concepts,
32
- join_type=JoinType.FULL,
33
- filter_to_mutual=True,
34
- )
35
- )
36
- return resolve_join_order(output)
37
-
38
-
39
- def gen_concept_merge_node(
40
- concept: Concept,
41
- local_optional: List[Concept],
42
- environment: Environment,
43
- g,
44
- depth: int,
45
- source_concepts,
46
- history: History | None = None,
47
- ) -> MergeNode | None:
48
- if not isinstance(concept.lineage, MergeStatement):
49
- logger.info(
50
- f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge node for {concept}"
51
- )
52
- return None
53
- lineage: MergeStatement = concept.lineage
54
-
55
- base_parents: List[StrategyNode] = []
56
-
57
- # get additional concepts that should be merged across the environments
58
- additional_merge: List[Concept] = [*lineage.concepts]
59
- target_namespaces = set(x.namespace for x in [concept] + local_optional)
60
- for x in local_optional:
61
- if x.address in environment.merged_concepts:
62
- ms = environment.merged_concepts[x.address].lineage
63
- assert isinstance(ms, MergeStatement)
64
- additional_merge += ms.concepts
65
-
66
- for select in lineage.concepts:
67
- # if it's a merge concept, filter it out of the optional
68
- if select.namespace not in target_namespaces:
69
- continue
70
- sub_optional = [
71
- x
72
- for x in local_optional
73
- if x.address not in environment.merged_concepts
74
- and x.namespace == select.namespace
75
- ]
76
-
77
- sub_additional_merge = [
78
- x for x in additional_merge if x.namespace == select.namespace
79
- ]
80
- sub_optional += sub_additional_merge
81
- final: List[Concept] = unique([select] + sub_optional, "address")
82
- logger.info(
83
- f"{padding(depth)}{LOGGER_PREFIX} generating concept merge parent node with {[x.address for x in final]}"
84
- )
85
- snode: StrategyNode = source_concepts(
86
- mandatory_list=final,
87
- environment=environment,
88
- g=g,
89
- depth=depth + 1,
90
- history=history,
91
- )
92
-
93
- if not snode:
94
- logger.info(
95
- f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge node for {concept}"
96
- )
97
- return None
98
- for x in sub_additional_merge:
99
- snode.add_output_concept(environment.merged_concepts[x.address])
100
- base_parents.append(snode)
101
-
102
- node_joins = merge_joins(
103
- base_parents,
104
- unique(
105
- [environment.merged_concepts[x.address] for x in additional_merge],
106
- "address",
107
- ),
108
- )
109
-
110
- enrichment = set([x.address for x in local_optional])
111
- outputs = [
112
- x
113
- for y in base_parents
114
- for x in y.output_concepts
115
- if x.derivation != PurposeLineage.MERGE
116
- ]
117
-
118
- additional_relevant = [x for x in outputs if x.address in enrichment]
119
- final_outputs = outputs + additional_relevant + [concept]
120
- virtual_outputs = [x for x in final_outputs if x.derivation == PurposeLineage.MERGE]
121
- node = MergeNode(
122
- input_concepts=[x for y in base_parents for x in y.output_concepts],
123
- output_concepts=[
124
- x for x in final_outputs if x.derivation != PurposeLineage.MERGE
125
- ],
126
- environment=environment,
127
- g=g,
128
- depth=depth,
129
- parents=base_parents,
130
- node_joins=node_joins,
131
- virtual_output_concepts=virtual_outputs,
132
- )
133
-
134
- qds = node.rebuild_cache()
135
-
136
- # assume grain to be outoput of select
137
- # but don't include anything aggregate at this point
138
- qds.grain = concept_list_to_grain(
139
- node.output_concepts, parent_sources=qds.datasources
140
- )
141
- possible_joins = concept_to_relevant_joins(additional_relevant)
142
- if not local_optional:
143
- logger.info(
144
- f"{padding(depth)}{LOGGER_PREFIX} no enriched required for merge concept node; exiting early"
145
- )
146
- return node
147
- if not possible_joins:
148
- logger.info(
149
- f"{padding(depth)}{LOGGER_PREFIX} no possible joins for merge concept node; exiting early"
150
- )
151
- return node
152
- if all(
153
- [x.address in [y.address for y in node.output_concepts] for x in local_optional]
154
- ):
155
- logger.info(
156
- f"{padding(depth)}{LOGGER_PREFIX} all enriched concepts returned from base merge concept node; exiting early"
157
- )
158
- return node
159
- missing = [
160
- x
161
- for x in local_optional
162
- if x.address not in [y.address for y in node.output_concepts]
163
- ]
164
- logger.info(
165
- f"{padding(depth)}{LOGGER_PREFIX} generating merge concept enrichment node for missing {[x.address for x in missing]}"
166
- )
167
- enrich_node: MergeNode = source_concepts( # this fetches the parent + join keys
168
- # to then connect to the rest of the query
169
- mandatory_list=additional_relevant + missing,
170
- environment=environment,
171
- g=g,
172
- depth=depth + 1,
173
- history=history,
174
- )
175
- if not enrich_node:
176
- logger.info(
177
- f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge concept enrichment node for {concept.address} with optional {[x.address for x in local_optional]}, returning just merge concept"
178
- )
179
- return node
180
-
181
- # we still need the hidden concepts to be returned to the search
182
- # since they must be on the final node
183
- # to avoid further recursion
184
- # TODO: let the downstream search know they were found
185
- return MergeNode(
186
- input_concepts=enrich_node.output_concepts + node.output_concepts,
187
- # also filter out the
188
- output_concepts=[
189
- x
190
- for x in node.output_concepts + local_optional
191
- if x.derivation != PurposeLineage.MERGE
192
- ],
193
- hidden_concepts=[],
194
- environment=environment,
195
- g=g,
196
- depth=depth,
197
- parents=[
198
- # this node gets the window
199
- node,
200
- # this node gets enrichment
201
- enrich_node,
202
- ],
203
- node_joins=[
204
- NodeJoin(
205
- left_node=enrich_node,
206
- right_node=node,
207
- concepts=possible_joins,
208
- filter_to_mutual=False,
209
- join_type=JoinType.LEFT_OUTER,
210
- )
211
- ],
212
- partial_concepts=node.partial_concepts,
213
- virtual_output_concepts=virtual_outputs,
214
- )