pytrilogy 0.0.3.80__py3-none-any.whl → 0.0.3.82__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/RECORD +23 -22
- trilogy/__init__.py +1 -1
- trilogy/core/enums.py +3 -0
- trilogy/core/functions.py +28 -0
- trilogy/core/models/author.py +9 -4
- trilogy/core/processing/concept_strategies_v3.py +6 -5
- trilogy/core/processing/discovery_node_factory.py +17 -1
- trilogy/core/processing/discovery_utility.py +1 -1
- trilogy/core/processing/discovery_validation.py +0 -1
- trilogy/core/processing/node_generators/__init__.py +2 -0
- trilogy/core/processing/node_generators/basic_node.py +3 -0
- trilogy/core/processing/node_generators/constant_node.py +38 -0
- trilogy/core/processing/node_generators/node_merge_node.py +23 -8
- trilogy/core/processing/utility.py +5 -2
- trilogy/dialect/base.py +5 -0
- trilogy/dialect/duckdb.py +14 -0
- trilogy/parsing/parse_engine.py +16 -2
- trilogy/parsing/trilogy.lark +8 -2
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.80.dist-info → pytrilogy-0.0.3.82.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.82.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=bYiPbjWf72hFcdkZbNsSKQsD0uR8NWYSSfzF26YTpfM,303
|
|
3
3
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
trilogy/constants.py,sha256=eKb_EJvSqjN9tGbdVEViwdtwwh8fZ3-jpOEDqL71y70,1691
|
|
5
5
|
trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
|
|
@@ -11,19 +11,19 @@ trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
|
11
11
|
trilogy/authoring/__init__.py,sha256=e74k-Jep4DLYPQU_2m0aVsYlw5HKTOucAKtdTbd6f2g,2595
|
|
12
12
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
14
|
-
trilogy/core/enums.py,sha256=
|
|
14
|
+
trilogy/core/enums.py,sha256=kuwTeEur5TZ07DX8nO3fSiRQ32bLZ5jRK-vIjHbBUnc,8118
|
|
15
15
|
trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0,3170
|
|
16
16
|
trilogy/core/environment_helpers.py,sha256=VvPIiFemqaLLpIpLIqprfu63K7muZ1YzNg7UZIUph8w,8267
|
|
17
17
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
18
18
|
trilogy/core/exceptions.py,sha256=jYEduuMehcMkmCpf-OC_taELPZm7qNfeSNzIWkDYScs,707
|
|
19
|
-
trilogy/core/functions.py,sha256=
|
|
19
|
+
trilogy/core/functions.py,sha256=9Q-c7UGxOR0SVwzcHt9mHX_aVroW9jEWwJr7BtHigw8,31766
|
|
20
20
|
trilogy/core/graph_models.py,sha256=BYhJzHKSgnZHVLJs1CfsgrxTPHqKqPNeA64RlozGY0A,3498
|
|
21
21
|
trilogy/core/internal.py,sha256=wFx4e1I0mtx159YFShSXeUBSQ82NINtAbOI-92RX4i8,2151
|
|
22
22
|
trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,8860
|
|
23
23
|
trilogy/core/query_processor.py,sha256=5aFgv-2LVM1Uku9cR_tFuTRDwyLnxc95bCMAHeFy2AY,20332
|
|
24
24
|
trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
|
|
25
25
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
trilogy/core/models/author.py,sha256=
|
|
26
|
+
trilogy/core/models/author.py,sha256=Xt_TRVl4xHF6fe-KTKPSt4Z8Srmplnv2KQe2grZXX3w,78411
|
|
27
27
|
trilogy/core/models/build.py,sha256=0TRcL__hvajLcpFwYeLjCV35cow5oVPbJHXA3oO09XA,65931
|
|
28
28
|
trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
|
|
29
29
|
trilogy/core/models/core.py,sha256=q-8ExAaSB3PoSDX2XOqfZ01HBBvT1pZFs7jGg1vZizc,11096
|
|
@@ -35,21 +35,22 @@ trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnb
|
|
|
35
35
|
trilogy/core/optimizations/inline_datasource.py,sha256=2sWNRpoRInnTgo9wExVT_r9RfLAQHI57reEV5cGHUcg,4329
|
|
36
36
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
|
|
37
37
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256=
|
|
38
|
+
trilogy/core/processing/concept_strategies_v3.py,sha256=Izo7yfR6sGHTaD17lN7ZzGYCtXA5AkXAmIp_OBjHH58,23161
|
|
39
39
|
trilogy/core/processing/discovery_loop.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
-
trilogy/core/processing/discovery_node_factory.py,sha256=
|
|
41
|
-
trilogy/core/processing/discovery_utility.py,sha256=
|
|
42
|
-
trilogy/core/processing/discovery_validation.py,sha256=
|
|
40
|
+
trilogy/core/processing/discovery_node_factory.py,sha256=5QVYUsci_h6iYWhS0GCoDow2tSAipiBW1OyTRX-g_L8,15581
|
|
41
|
+
trilogy/core/processing/discovery_utility.py,sha256=eY4n7_r6_R-cx_Sm8FiouMXh78v2iO2SGhi0aI5jvDg,4549
|
|
42
|
+
trilogy/core/processing/discovery_validation.py,sha256=eZ4HfHMpqZLI8MGG2jez8arS8THs6ceuVrQFIY6gXrU,5364
|
|
43
43
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
44
|
-
trilogy/core/processing/utility.py,sha256=
|
|
45
|
-
trilogy/core/processing/node_generators/__init__.py,sha256=
|
|
46
|
-
trilogy/core/processing/node_generators/basic_node.py,sha256=
|
|
44
|
+
trilogy/core/processing/utility.py,sha256=VjKhDFeItB642riF8KHwyk4YEc58nVDiPLIydwdLPcQ,22716
|
|
45
|
+
trilogy/core/processing/node_generators/__init__.py,sha256=iVJ-crowPxYeut-hFjyEjfibKIDq7PfB4LEuDAUCjGY,943
|
|
46
|
+
trilogy/core/processing/node_generators/basic_node.py,sha256=TLZCv4WS196a-0g5xgKuJGthnGP8Ugm46iz85_3NIY4,5626
|
|
47
47
|
trilogy/core/processing/node_generators/common.py,sha256=PdysdroW9DUADP7f5Wv_GKPUyCTROZV1g3L45fawxi8,9443
|
|
48
|
+
trilogy/core/processing/node_generators/constant_node.py,sha256=LfpDq2WrBRZ3tGsLxw77LuigKfhbteWWh9L8BGdMGwk,1146
|
|
48
49
|
trilogy/core/processing/node_generators/filter_node.py,sha256=oRRq2-T3ufgn4D23uQsc58f20eFk-djs4QI3WKA75K8,10908
|
|
49
50
|
trilogy/core/processing/node_generators/group_node.py,sha256=1QJhRxsTklJ5xq8wHlAURZaN9gL9FPpeCa1OJ7IwXnY,6769
|
|
50
51
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
51
52
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
|
|
52
|
-
trilogy/core/processing/node_generators/node_merge_node.py,sha256=
|
|
53
|
+
trilogy/core/processing/node_generators/node_merge_node.py,sha256=bYhXGJqJDgT4JWlN4u761V3bkP96o6J25LFs6YBOiKQ,18304
|
|
53
54
|
trilogy/core/processing/node_generators/recursive_node.py,sha256=l5zdh0dURKwmAy8kK4OpMtZfyUEQRk6N-PwSWIyBpSM,2468
|
|
54
55
|
trilogy/core/processing/node_generators/rowset_node.py,sha256=5L5u6xz1In8EaHQdcYgR2si-tz9WB9YLXURo4AkUT9A,6630
|
|
55
56
|
trilogy/core/processing/node_generators/select_merge_node.py,sha256=Cv2GwNiYSmwewjuK8T3JB3pbgrLZFPsB75DCP153BMA,22818
|
|
@@ -76,12 +77,12 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
76
77
|
trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
|
|
77
78
|
trilogy/core/statements/execute.py,sha256=pfr1CZ_Cx1qQ-7LDyRI0JUfvtxBr_GGv-VeqiAjr43g,1406
|
|
78
79
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
-
trilogy/dialect/base.py,sha256=
|
|
80
|
+
trilogy/dialect/base.py,sha256=0nVDvsnDS8iMTIlCUsBs_ymoZMW6uh04U4MMZxOuHUw,47428
|
|
80
81
|
trilogy/dialect/bigquery.py,sha256=8xhEu0z_lKANjbvzvBbC7CeKrJf1iP8YyrHqNale-ug,4351
|
|
81
82
|
trilogy/dialect/common.py,sha256=tSthIZOXXRPQ4KeMKnDDsH7KlTmf2EVqigVtLyoc4zc,6071
|
|
82
83
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
83
84
|
trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
|
|
84
|
-
trilogy/dialect/duckdb.py,sha256=
|
|
85
|
+
trilogy/dialect/duckdb.py,sha256=uqnZEd1IH4tO8FSbbUXe3Jl9EjoyrDde406k0VDuMXw,5544
|
|
85
86
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
86
87
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
87
88
|
trilogy/dialect/presto.py,sha256=yzSF8SZ6o1dizj1UueAa7S3lR0qNYJdSXbF78EHyhY0,3668
|
|
@@ -97,9 +98,9 @@ trilogy/parsing/common.py,sha256=yV1AckK0h8u1OFeGQBTMu-wuW5m63c5CcZuPicsTH_w,306
|
|
|
97
98
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
98
99
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
99
100
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
100
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
101
|
+
trilogy/parsing/parse_engine.py,sha256=ll3uBwpJ2iAxT-KbbXy7OuCqUvKfLPzB1gHr8gApv_s,78591
|
|
101
102
|
trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
|
|
102
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
103
|
+
trilogy/parsing/trilogy.lark,sha256=Uhy6A00-GsC-L8fRCN0JZC6fL87ujOOd9_4LbQWApY4,15248
|
|
103
104
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
105
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
105
106
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -110,8 +111,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
110
111
|
trilogy/std/net.preql,sha256=7l7MqIjs6TDCpO6dBAoNJU81Ex255jZRK36kBgE1GDs,158
|
|
111
112
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
112
113
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
113
|
-
pytrilogy-0.0.3.
|
|
114
|
-
pytrilogy-0.0.3.
|
|
115
|
-
pytrilogy-0.0.3.
|
|
116
|
-
pytrilogy-0.0.3.
|
|
117
|
-
pytrilogy-0.0.3.
|
|
114
|
+
pytrilogy-0.0.3.82.dist-info/METADATA,sha256=eFCjlA6cbwL_BeJhwzGHYJEVqKLaWB49VJSkG71s0ls,9589
|
|
115
|
+
pytrilogy-0.0.3.82.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
116
|
+
pytrilogy-0.0.3.82.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
117
|
+
pytrilogy-0.0.3.82.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
118
|
+
pytrilogy-0.0.3.82.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/enums.py
CHANGED
|
@@ -171,6 +171,7 @@ class FunctionType(Enum):
|
|
|
171
171
|
RANDOM = "random"
|
|
172
172
|
FLOOR = "floor"
|
|
173
173
|
CEIL = "ceil"
|
|
174
|
+
LOG = "log"
|
|
174
175
|
|
|
175
176
|
# Aggregates
|
|
176
177
|
## group is not a real aggregate - it just means group by this + some other set of fields
|
|
@@ -193,6 +194,8 @@ class FunctionType(Enum):
|
|
|
193
194
|
SUBSTRING = "substring"
|
|
194
195
|
STRPOS = "strpos"
|
|
195
196
|
CONTAINS = "contains"
|
|
197
|
+
TRIM = "trim"
|
|
198
|
+
REPLACE = "replace"
|
|
196
199
|
|
|
197
200
|
# STRING REGEX
|
|
198
201
|
REGEXP_CONTAINS = "regexp_contains"
|
trilogy/core/functions.py
CHANGED
|
@@ -358,6 +358,16 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
358
358
|
output_type=DataType.INTEGER,
|
|
359
359
|
arg_count=2,
|
|
360
360
|
),
|
|
361
|
+
FunctionType.REPLACE: FunctionConfig(
|
|
362
|
+
valid_inputs=[
|
|
363
|
+
{DataType.STRING},
|
|
364
|
+
{DataType.STRING},
|
|
365
|
+
{DataType.STRING},
|
|
366
|
+
],
|
|
367
|
+
output_purpose=Purpose.PROPERTY,
|
|
368
|
+
output_type=DataType.STRING,
|
|
369
|
+
arg_count=3,
|
|
370
|
+
),
|
|
361
371
|
FunctionType.CONTAINS: FunctionConfig(
|
|
362
372
|
valid_inputs=[
|
|
363
373
|
{DataType.STRING},
|
|
@@ -367,6 +377,15 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
367
377
|
output_type=DataType.BOOL,
|
|
368
378
|
arg_count=2,
|
|
369
379
|
),
|
|
380
|
+
FunctionType.TRIM: FunctionConfig(
|
|
381
|
+
valid_inputs=[
|
|
382
|
+
{DataType.STRING},
|
|
383
|
+
{DataType.STRING},
|
|
384
|
+
],
|
|
385
|
+
output_purpose=Purpose.PROPERTY,
|
|
386
|
+
output_type=DataType.BOOL,
|
|
387
|
+
arg_count=2,
|
|
388
|
+
),
|
|
370
389
|
FunctionType.SUBSTRING: FunctionConfig(
|
|
371
390
|
valid_inputs=[{DataType.STRING}, {DataType.INTEGER}, {DataType.INTEGER}],
|
|
372
391
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -688,6 +707,15 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
688
707
|
output_type=DataType.INTEGER,
|
|
689
708
|
arg_count=1,
|
|
690
709
|
),
|
|
710
|
+
FunctionType.LOG: FunctionConfig(
|
|
711
|
+
valid_inputs=[
|
|
712
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
713
|
+
{DataType.INTEGER},
|
|
714
|
+
],
|
|
715
|
+
output_purpose=Purpose.PROPERTY,
|
|
716
|
+
output_type=DataType.FLOAT,
|
|
717
|
+
arg_count=2,
|
|
718
|
+
),
|
|
691
719
|
FunctionType.RANDOM: FunctionConfig(
|
|
692
720
|
valid_inputs=[],
|
|
693
721
|
output_purpose=Purpose.PROPERTY,
|
trilogy/core/models/author.py
CHANGED
|
@@ -1605,6 +1605,10 @@ def get_concept_arguments(expr) -> List["ConceptRef"]:
|
|
|
1605
1605
|
return output
|
|
1606
1606
|
|
|
1607
1607
|
|
|
1608
|
+
def args_to_pretty(input: set[DataType]) -> str:
|
|
1609
|
+
return ", ".join(sorted([f"'{x.value}'" for x in input if x != DataType.UNKNOWN]))
|
|
1610
|
+
|
|
1611
|
+
|
|
1608
1612
|
class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
1609
1613
|
operator: FunctionType
|
|
1610
1614
|
arg_count: int = Field(default=1)
|
|
@@ -1669,9 +1673,10 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1669
1673
|
and get_basic_type(arg.datatype.data_type) not in valid_inputs[idx]
|
|
1670
1674
|
):
|
|
1671
1675
|
if arg.datatype != DataType.UNKNOWN:
|
|
1676
|
+
|
|
1672
1677
|
raise TypeError(
|
|
1673
|
-
f"Invalid
|
|
1674
|
-
f"
|
|
1678
|
+
f"Invalid argument type '{arg.datatype.data_type.value}' passed into {operator_name} function in position {idx+1}"
|
|
1679
|
+
f" from concept: {arg.name}. Valid: {args_to_pretty(valid_inputs[idx])}."
|
|
1675
1680
|
)
|
|
1676
1681
|
if (
|
|
1677
1682
|
isinstance(arg, Function)
|
|
@@ -1679,8 +1684,8 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1679
1684
|
):
|
|
1680
1685
|
if arg.output_datatype != DataType.UNKNOWN:
|
|
1681
1686
|
raise TypeError(
|
|
1682
|
-
f"Invalid
|
|
1683
|
-
f" {operator_name} from function {arg.operator.name}
|
|
1687
|
+
f"Invalid argument type {arg.output_datatype}' passed into"
|
|
1688
|
+
f" {operator_name} function from function {arg.operator.name} in position {idx+1}. Valid: {args_to_pretty(valid_inputs[idx])}"
|
|
1684
1689
|
)
|
|
1685
1690
|
# check constants
|
|
1686
1691
|
comparisons: List[Tuple[Type, DataType]] = [
|
|
@@ -63,6 +63,11 @@ def generate_candidates_restrictive(
|
|
|
63
63
|
and x.address not in priority_concept.pseudonyms
|
|
64
64
|
and priority_concept.address not in x.pseudonyms
|
|
65
65
|
]
|
|
66
|
+
|
|
67
|
+
# if it's single row, joins are irrelevant. Fetch without keys.
|
|
68
|
+
if priority_concept.granularity == Granularity.SINGLE_ROW:
|
|
69
|
+
return [], conditions
|
|
70
|
+
|
|
66
71
|
if conditions and priority_concept.derivation in ROOT_DERIVATIONS:
|
|
67
72
|
logger.info(
|
|
68
73
|
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Injecting additional conditional row arguments as all remaining concepts are roots or constant"
|
|
@@ -72,9 +77,6 @@ def generate_candidates_restrictive(
|
|
|
72
77
|
unique(list(conditions.row_arguments) + local_candidates, "address"),
|
|
73
78
|
None,
|
|
74
79
|
)
|
|
75
|
-
# if it's single row, joins are irrelevant. Fetch without keys.
|
|
76
|
-
if priority_concept.granularity == Granularity.SINGLE_ROW:
|
|
77
|
-
return [], conditions
|
|
78
80
|
|
|
79
81
|
return local_candidates, conditions
|
|
80
82
|
|
|
@@ -324,7 +326,6 @@ def check_for_early_exit(
|
|
|
324
326
|
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Not complete (missing {missing}), continuing search"
|
|
325
327
|
)
|
|
326
328
|
# if we have attempted on root node, we've tried them all.
|
|
327
|
-
# inject in another search with filter concepts
|
|
328
329
|
if priority_concept.derivation == Derivation.ROOT:
|
|
329
330
|
logger.info(
|
|
330
331
|
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Breaking as attempted root with no results"
|
|
@@ -364,7 +365,7 @@ def generate_loop_completion(context: LoopContext, virtual: set[str]) -> Strateg
|
|
|
364
365
|
for x in context.stack
|
|
365
366
|
}
|
|
366
367
|
logger.info(
|
|
367
|
-
f"Condition {context.conditions} not required, parents included filtering! {parent_map
|
|
368
|
+
f"Condition {context.conditions} not required, parents included filtering! {parent_map}"
|
|
368
369
|
)
|
|
369
370
|
if len(context.stack) == 1:
|
|
370
371
|
output: StrategyNode = context.stack[0]
|
|
@@ -12,6 +12,7 @@ from trilogy.core.models.build_environment import BuildEnvironment
|
|
|
12
12
|
from trilogy.core.processing.discovery_utility import LOGGER_PREFIX, depth_to_prefix
|
|
13
13
|
from trilogy.core.processing.node_generators import (
|
|
14
14
|
gen_basic_node,
|
|
15
|
+
gen_constant_node,
|
|
15
16
|
gen_filter_node,
|
|
16
17
|
gen_group_node,
|
|
17
18
|
gen_group_to_node,
|
|
@@ -252,6 +253,21 @@ def _generate_basic_node(ctx: NodeGenerationContext) -> StrategyNode | None:
|
|
|
252
253
|
)
|
|
253
254
|
|
|
254
255
|
|
|
256
|
+
def _generate_constant_node(ctx: NodeGenerationContext) -> StrategyNode | None:
|
|
257
|
+
ctx.log_generation("constant")
|
|
258
|
+
return gen_constant_node(
|
|
259
|
+
ctx.concept,
|
|
260
|
+
ctx.local_optional,
|
|
261
|
+
history=ctx.history,
|
|
262
|
+
environment=ctx.environment,
|
|
263
|
+
g=ctx.g,
|
|
264
|
+
depth=ctx.next_depth,
|
|
265
|
+
source_concepts=ctx.source_concepts,
|
|
266
|
+
conditions=ctx.conditions,
|
|
267
|
+
accept_partial=ctx.accept_partial,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
255
271
|
class RootNodeHandler:
|
|
256
272
|
"""Handles complex root node generation logic."""
|
|
257
273
|
|
|
@@ -469,7 +485,7 @@ def generate_node(
|
|
|
469
485
|
Derivation.GROUP_TO: lambda: _generate_group_to_node(context),
|
|
470
486
|
Derivation.BASIC: lambda: _generate_basic_node(context),
|
|
471
487
|
Derivation.ROOT: lambda: RootNodeHandler(context).generate(),
|
|
472
|
-
Derivation.CONSTANT: lambda:
|
|
488
|
+
Derivation.CONSTANT: lambda: _generate_constant_node(context),
|
|
473
489
|
}
|
|
474
490
|
|
|
475
491
|
handler = derivation_handlers.get(concept.derivation)
|
|
@@ -73,11 +73,11 @@ def get_priority_concept(
|
|
|
73
73
|
+ [c for c in remaining_concept if c.derivation == Derivation.RECURSIVE]
|
|
74
74
|
+ [c for c in remaining_concept if c.derivation == Derivation.BASIC]
|
|
75
75
|
+ [c for c in remaining_concept if c.derivation == Derivation.GROUP_TO]
|
|
76
|
+
+ [c for c in remaining_concept if c.derivation == Derivation.CONSTANT]
|
|
76
77
|
# finally our plain selects
|
|
77
78
|
+ [
|
|
78
79
|
c for c in remaining_concept if c.derivation == Derivation.ROOT
|
|
79
80
|
] # and any non-single row constants
|
|
80
|
-
+ [c for c in remaining_concept if c.derivation == Derivation.CONSTANT]
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
priority += [
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .basic_node import gen_basic_node
|
|
2
|
+
from .constant_node import gen_constant_node
|
|
2
3
|
from .filter_node import gen_filter_node
|
|
3
4
|
from .group_node import gen_group_node
|
|
4
5
|
from .group_to_node import gen_group_to_node
|
|
@@ -26,4 +27,5 @@ __all__ = [
|
|
|
26
27
|
"gen_multiselect_node",
|
|
27
28
|
"gen_synonym_node",
|
|
28
29
|
"gen_recursive_node",
|
|
30
|
+
"gen_constant_node",
|
|
29
31
|
]
|
|
@@ -97,6 +97,9 @@ def gen_basic_node(
|
|
|
97
97
|
and not any(x.address in y.pseudonyms for y in equivalent_optional)
|
|
98
98
|
and x.address not in ignored_optional
|
|
99
99
|
]
|
|
100
|
+
logger.info(
|
|
101
|
+
f"{depth_prefix}{LOGGER_PREFIX} basic node for {concept} has non-equivalent optional {[x.address for x in non_equivalent_optional]}"
|
|
102
|
+
)
|
|
100
103
|
all_parents: list[BuildConcept] = unique(
|
|
101
104
|
parent_concepts + non_equivalent_optional, "address"
|
|
102
105
|
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from trilogy.core.models.build import BuildConcept, BuildWhereClause
|
|
4
|
+
from trilogy.core.models.build_environment import BuildEnvironment
|
|
5
|
+
from trilogy.core.processing.nodes import History, StrategyNode
|
|
6
|
+
|
|
7
|
+
LOGGER_PREFIX = "[GEN_CONSTANT_NODE]"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def gen_constant_node(
|
|
11
|
+
concept: BuildConcept,
|
|
12
|
+
local_optional: List[BuildConcept],
|
|
13
|
+
environment: BuildEnvironment,
|
|
14
|
+
g,
|
|
15
|
+
depth: int,
|
|
16
|
+
source_concepts,
|
|
17
|
+
history: History | None = None,
|
|
18
|
+
conditions: BuildWhereClause | None = None,
|
|
19
|
+
accept_partial: bool = False,
|
|
20
|
+
):
|
|
21
|
+
"""our only goal here is to generate a row if conditions exist, or none if they do not"""
|
|
22
|
+
|
|
23
|
+
targets = [concept] + local_optional
|
|
24
|
+
if conditions:
|
|
25
|
+
targets += conditions.row_arguments
|
|
26
|
+
parent_node: StrategyNode | None = source_concepts(
|
|
27
|
+
mandatory_list=targets,
|
|
28
|
+
environment=environment,
|
|
29
|
+
g=g,
|
|
30
|
+
depth=depth + 1,
|
|
31
|
+
history=history,
|
|
32
|
+
conditions=conditions,
|
|
33
|
+
accept_partial=accept_partial,
|
|
34
|
+
)
|
|
35
|
+
if not parent_node:
|
|
36
|
+
return None
|
|
37
|
+
parent_node.set_output_concepts([concept] + local_optional)
|
|
38
|
+
return parent_node
|
|
@@ -94,31 +94,45 @@ def determine_induced_minimal_nodes(
|
|
|
94
94
|
# inclusion of aggregates can create ambiguous node relation chains
|
|
95
95
|
# there may be a better way to handle this
|
|
96
96
|
# can be revisited if we need to connect a derived synonym based on an aggregate
|
|
97
|
-
if lookup.derivation in (
|
|
97
|
+
if lookup.derivation in (
|
|
98
|
+
Derivation.CONSTANT,
|
|
99
|
+
Derivation.AGGREGATE,
|
|
100
|
+
Derivation.FILTER,
|
|
101
|
+
):
|
|
98
102
|
nodes_to_remove.append(node)
|
|
99
103
|
# purge a node if we're already looking for all it's parents
|
|
100
104
|
if filter_downstream and lookup.derivation not in (Derivation.ROOT,):
|
|
101
105
|
nodes_to_remove.append(node)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if nodes_to_remove:
|
|
107
|
+
logger.debug(f"Removing nodes {nodes_to_remove} from graph")
|
|
108
|
+
H.remove_nodes_from(nodes_to_remove)
|
|
109
|
+
isolates = list(nx.isolates(H))
|
|
110
|
+
if isolates:
|
|
111
|
+
logger.debug(f"Removing isolates {isolates} from graph")
|
|
112
|
+
H.remove_nodes_from(isolates)
|
|
106
113
|
|
|
107
114
|
zero_out = list(x for x in H.nodes if G.out_degree(x) == 0 and x not in nodelist)
|
|
108
115
|
while zero_out:
|
|
116
|
+
logger.debug(f"Removing zero out nodes {zero_out} from graph")
|
|
109
117
|
H.remove_nodes_from(zero_out)
|
|
110
118
|
zero_out = list(
|
|
111
119
|
x for x in H.nodes if G.out_degree(x) == 0 and x not in nodelist
|
|
112
120
|
)
|
|
113
|
-
|
|
114
121
|
try:
|
|
115
122
|
paths = nx.multi_source_dijkstra_path(H, nodelist)
|
|
116
123
|
# logger.debug(f"Paths found for {nodelist}")
|
|
117
124
|
except nx.exception.NodeNotFound:
|
|
118
125
|
# logger.debug(f"Unable to find paths for {nodelist}- {str(e)}")
|
|
119
126
|
return None
|
|
120
|
-
|
|
127
|
+
path_removals = list(x for x in H.nodes if x not in paths)
|
|
128
|
+
if path_removals:
|
|
129
|
+
logger.debug(f"Removing paths {path_removals} from graph")
|
|
130
|
+
H.remove_nodes_from(path_removals)
|
|
131
|
+
logger.debug(f"Graph after path removal {H.nodes}")
|
|
121
132
|
sG: nx.Graph = ax.steinertree.steiner_tree(H, nodelist).copy()
|
|
133
|
+
if not sG.nodes:
|
|
134
|
+
logger.debug(f"No Steiner tree found for nodes {nodelist}")
|
|
135
|
+
return None
|
|
122
136
|
# logger.debug(f"Steiner tree found for nodes {nodelist} {sG.nodes}")
|
|
123
137
|
final: nx.DiGraph = nx.subgraph(G, sG.nodes).copy()
|
|
124
138
|
|
|
@@ -293,7 +307,8 @@ def resolve_weak_components(
|
|
|
293
307
|
if not nx.is_weakly_connected(g):
|
|
294
308
|
break_flag = True
|
|
295
309
|
continue
|
|
296
|
-
|
|
310
|
+
# from trilogy.hooks.graph_hook import GraphHook
|
|
311
|
+
# GraphHook().query_graph_built(g, highlight_nodes=[concept_to_node(c.with_default_grain()) for c in all_concepts if "__preql_internal" not in c.address])
|
|
297
312
|
all_graph_concepts = [
|
|
298
313
|
extract_concept(extract_address(node), environment)
|
|
299
314
|
for node in g.nodes
|
|
@@ -10,6 +10,7 @@ from trilogy.constants import MagicConstants
|
|
|
10
10
|
from trilogy.core.enums import (
|
|
11
11
|
BooleanOperator,
|
|
12
12
|
DatePart,
|
|
13
|
+
Derivation,
|
|
13
14
|
FunctionClass,
|
|
14
15
|
Granularity,
|
|
15
16
|
JoinType,
|
|
@@ -262,16 +263,19 @@ def calculate_graph_relevance(
|
|
|
262
263
|
"""
|
|
263
264
|
relevance = 0
|
|
264
265
|
for node in g.nodes:
|
|
266
|
+
|
|
265
267
|
if node not in subset_nodes:
|
|
266
268
|
continue
|
|
267
269
|
if not g.nodes[node]["type"] == NodeType.CONCEPT:
|
|
268
270
|
continue
|
|
269
271
|
concept = [x for x in concepts if x.address == node].pop()
|
|
270
|
-
|
|
272
|
+
# debug granularity and derivation
|
|
271
273
|
# a single row concept can always be crossjoined
|
|
272
274
|
# therefore a graph with only single row concepts is always relevant
|
|
273
275
|
if concept.granularity == Granularity.SINGLE_ROW:
|
|
274
276
|
continue
|
|
277
|
+
if concept.derivation == Derivation.CONSTANT:
|
|
278
|
+
continue
|
|
275
279
|
# if it's an aggregate up to an arbitrary grain, it can be joined in later
|
|
276
280
|
# and can be ignored in subgraph
|
|
277
281
|
if concept.purpose == Purpose.METRIC:
|
|
@@ -284,7 +288,6 @@ def calculate_graph_relevance(
|
|
|
284
288
|
continue
|
|
285
289
|
# Added 2023-10-18 since we seemed to be strangely dropping things
|
|
286
290
|
relevance += 1
|
|
287
|
-
|
|
288
291
|
return relevance
|
|
289
292
|
|
|
290
293
|
|
trilogy/dialect/base.py
CHANGED
|
@@ -205,6 +205,9 @@ FUNCTION_MAP = {
|
|
|
205
205
|
FunctionType.MOD: lambda x: f"({x[0]} % {x[1]})",
|
|
206
206
|
FunctionType.SQRT: lambda x: f"sqrt({x[0]})",
|
|
207
207
|
FunctionType.RANDOM: lambda x: "random()",
|
|
208
|
+
FunctionType.LOG: lambda x: (
|
|
209
|
+
f"log({x[0]})" if x[1] == 10 else f"log({x[0]}, {x[1]})"
|
|
210
|
+
),
|
|
208
211
|
# aggregate types
|
|
209
212
|
FunctionType.COUNT_DISTINCT: lambda x: f"count(distinct {x[0]})",
|
|
210
213
|
FunctionType.COUNT: lambda x: f"count({x[0]})",
|
|
@@ -224,6 +227,8 @@ FUNCTION_MAP = {
|
|
|
224
227
|
FunctionType.REGEXP_CONTAINS: lambda x: f"REGEXP_CONTAINS({x[0]},{x[1]})",
|
|
225
228
|
FunctionType.REGEXP_EXTRACT: lambda x: f"REGEXP_EXTRACT({x[0]},{x[1]})",
|
|
226
229
|
FunctionType.REGEXP_REPLACE: lambda x: f"REGEXP_REPLACE({x[0]},{x[1]}, {x[2]})",
|
|
230
|
+
FunctionType.TRIM: lambda x: f"TRIM({x[0]})",
|
|
231
|
+
FunctionType.REPLACE: lambda x: f"REPLACE({x[0]},{x[1]},{x[2]})",
|
|
227
232
|
# FunctionType.NOT_LIKE: lambda x: f" CASE WHEN {x[0]} like {x[1]} THEN 0 ELSE 1 END",
|
|
228
233
|
# date types
|
|
229
234
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc({x[0]},{x[1]})",
|
trilogy/dialect/duckdb.py
CHANGED
|
@@ -33,11 +33,25 @@ def render_sort(args):
|
|
|
33
33
|
return f"list_sort({args[0]}, '{order[0]}', '{order[1]}')"
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def render_log(args):
|
|
37
|
+
if len(args) == 1:
|
|
38
|
+
return f"log({args[0]})"
|
|
39
|
+
elif len(args) == 2:
|
|
40
|
+
if int(args[1]) == 10:
|
|
41
|
+
return f"log({args[0]})"
|
|
42
|
+
else:
|
|
43
|
+
# change of base formula
|
|
44
|
+
return f"log({args[0]})/log({args[1]})"
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError("log function requires 1 or 2 arguments")
|
|
47
|
+
|
|
48
|
+
|
|
36
49
|
FUNCTION_MAP = {
|
|
37
50
|
FunctionType.COUNT: lambda args: f"count({args[0]})",
|
|
38
51
|
FunctionType.SUM: lambda args: f"sum({args[0]})",
|
|
39
52
|
FunctionType.AVG: lambda args: f"avg({args[0]})",
|
|
40
53
|
FunctionType.LENGTH: lambda args: f"length({args[0]})",
|
|
54
|
+
FunctionType.LOG: lambda args: render_log(args),
|
|
41
55
|
FunctionType.LIKE: lambda args: (
|
|
42
56
|
f" CASE WHEN {args[0]} like {args[1]} THEN True ELSE False END"
|
|
43
57
|
),
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -1793,10 +1793,18 @@ class ParseToObjects(Transformer):
|
|
|
1793
1793
|
def fstrpos(self, meta, args):
|
|
1794
1794
|
return self.function_factory.create_function(args, FunctionType.STRPOS, meta)
|
|
1795
1795
|
|
|
1796
|
+
@v_args(meta=True)
|
|
1797
|
+
def freplace(self, meta, args):
|
|
1798
|
+
return self.function_factory.create_function(args, FunctionType.REPLACE, meta)
|
|
1799
|
+
|
|
1796
1800
|
@v_args(meta=True)
|
|
1797
1801
|
def fcontains(self, meta, args):
|
|
1798
1802
|
return self.function_factory.create_function(args, FunctionType.CONTAINS, meta)
|
|
1799
1803
|
|
|
1804
|
+
@v_args(meta=True)
|
|
1805
|
+
def ftrim(self, meta, args):
|
|
1806
|
+
return self.function_factory.create_function(args, FunctionType.TRIM, meta)
|
|
1807
|
+
|
|
1800
1808
|
@v_args(meta=True)
|
|
1801
1809
|
def fsubstring(self, meta, args):
|
|
1802
1810
|
return self.function_factory.create_function(args, FunctionType.SUBSTRING, meta)
|
|
@@ -1971,6 +1979,12 @@ class ParseToObjects(Transformer):
|
|
|
1971
1979
|
args.append(0)
|
|
1972
1980
|
return self.function_factory.create_function(args, FunctionType.ROUND, meta)
|
|
1973
1981
|
|
|
1982
|
+
@v_args(meta=True)
|
|
1983
|
+
def flog(self, meta, args) -> Function:
|
|
1984
|
+
if len(args) == 1:
|
|
1985
|
+
args.append(10)
|
|
1986
|
+
return self.function_factory.create_function(args, FunctionType.LOG, meta)
|
|
1987
|
+
|
|
1974
1988
|
@v_args(meta=True)
|
|
1975
1989
|
def ffloor(self, meta, args) -> Function:
|
|
1976
1990
|
return self.function_factory.create_function(args, FunctionType.FLOOR, meta)
|
|
@@ -2074,9 +2088,9 @@ def unpack_visit_error(e: VisitError, text: str | None = None):
|
|
|
2074
2088
|
extract = text[e.obj.meta.start_pos - 5 : e.obj.meta.end_pos + 5]
|
|
2075
2089
|
raise InvalidSyntaxException(
|
|
2076
2090
|
str(e.orig_exc)
|
|
2077
|
-
+ "
|
|
2091
|
+
+ " Raised when parsing rule: "
|
|
2078
2092
|
+ str(e.rule)
|
|
2079
|
-
+ f
|
|
2093
|
+
+ f' Line: {e.obj.meta.line} "...{extract}..."'
|
|
2080
2094
|
)
|
|
2081
2095
|
InvalidSyntaxException(
|
|
2082
2096
|
str(e.orig_exc) + " in " + str(e.rule) + f" Line: {e.obj.meta.line}"
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -222,6 +222,8 @@
|
|
|
222
222
|
fmul: ("multiply"i "(" expr "," expr ")" )
|
|
223
223
|
fdiv: ( "divide"i "(" expr "," expr ")")
|
|
224
224
|
fmod: ( "mod"i "(" expr "," (int_lit | concept_lit ) ")")
|
|
225
|
+
_LOG.1: "log"i "("
|
|
226
|
+
flog: _LOG expr ("," expr)? ")"
|
|
225
227
|
_ROUND.1: "round"i "("
|
|
226
228
|
fround: _ROUND expr ("," expr)? ")"
|
|
227
229
|
_FLOOR.1: "floor"i "("
|
|
@@ -234,7 +236,7 @@
|
|
|
234
236
|
_RANDOM.1: "random("i
|
|
235
237
|
frandom: _RANDOM expr ")"
|
|
236
238
|
|
|
237
|
-
_math_functions: fmul | fdiv | fadd | fsub | fround | ffloor | fceil | fmod | fabs | fsqrt | frandom
|
|
239
|
+
_math_functions: fmul | fdiv | fadd | fsub | fround | ffloor | fceil | fmod | flog | fabs | fsqrt | frandom
|
|
238
240
|
|
|
239
241
|
//generic
|
|
240
242
|
_fcast_primary: "cast"i "(" expr "as"i data_type ")"
|
|
@@ -278,6 +280,10 @@
|
|
|
278
280
|
fstrpos: _STRPOS expr "," expr ")"
|
|
279
281
|
_CONTAINS.1: "contains("i
|
|
280
282
|
fcontains: _CONTAINS expr "," expr ")"
|
|
283
|
+
_TRIM.1: "trim("i
|
|
284
|
+
ftrim: _TRIM expr ")"
|
|
285
|
+
_REPLACE.1: "replace("i
|
|
286
|
+
freplace: _REPLACE expr "," expr "," expr ")"
|
|
281
287
|
_SUBSTRING.1: "substring("i
|
|
282
288
|
fsubstring: _SUBSTRING expr "," expr "," expr ")"
|
|
283
289
|
_REGEXP_EXTRACT.1: "regexp_extract("
|
|
@@ -287,7 +293,7 @@
|
|
|
287
293
|
_REGEXP_REPLACE.1: "regexp_replace("
|
|
288
294
|
fregexp_replace: _REGEXP_REPLACE expr "," expr "," expr ")"
|
|
289
295
|
|
|
290
|
-
_string_functions: like | ilike | upper | flower | fsplit | fstrpos | fsubstring | fcontains | fregexp_extract | fregexp_contains | fregexp_replace
|
|
296
|
+
_string_functions: like | ilike | upper | flower | fsplit | fstrpos | fsubstring | fcontains | ftrim | freplace | fregexp_extract | fregexp_contains | fregexp_replace
|
|
291
297
|
|
|
292
298
|
//array_functions
|
|
293
299
|
_ARRAY_SUM.1: "array_sum("i
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|