join fix and runner update to support multiple output files
parent
ab3d7ab971
commit
dd1431760a
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Alteryx example files
|
||||||
|
Alteryx_TestWorkflows/JoinTesting/Output/
|
||||||
|
!Alteryx_TestWorkflows/JoinTesting/Output/**/
|
||||||
|
|
||||||
# uv
|
# uv
|
||||||
uv.lock
|
uv.lock
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,226 +1,226 @@
|
||||||
Left_Store_ID,Product_ID,Left_Stock_On_Hand
|
Store_ID,Product_ID,Stock_On_Hand
|
||||||
15,31,4
|
15,31,4
|
||||||
15,32,16
|
15,32,16
|
||||||
15,33,8
|
15,33,8
|
||||||
15,34,7
|
15,34,7
|
||||||
16,31,14
|
16,31,14
|
||||||
16,32,7
|
16,32,7
|
||||||
16,33,6
|
16,33,6
|
||||||
16,34,2
|
16,34,2
|
||||||
16,35,6
|
16,35,6
|
||||||
17,31,20
|
17,31,20
|
||||||
17,32,15
|
17,32,15
|
||||||
17,33,27
|
17,33,27
|
||||||
17,34,11
|
17,34,11
|
||||||
18,31,4
|
18,31,4
|
||||||
18,32,9
|
18,32,9
|
||||||
18,33,9
|
18,33,9
|
||||||
18,34,8
|
18,34,8
|
||||||
18,35,10
|
18,35,10
|
||||||
19,31,4
|
19,31,4
|
||||||
19,32,5
|
19,32,5
|
||||||
19,33,0
|
19,33,0
|
||||||
19,34,15
|
19,34,15
|
||||||
19,35,14
|
19,35,14
|
||||||
20,31,10
|
20,31,10
|
||||||
20,32,9
|
20,32,9
|
||||||
20,33,28
|
20,33,28
|
||||||
20,34,19
|
20,34,19
|
||||||
21,31,19
|
21,31,19
|
||||||
21,32,3
|
21,32,3
|
||||||
21,33,16
|
21,33,16
|
||||||
21,34,16
|
21,34,16
|
||||||
22,31,34
|
22,31,34
|
||||||
22,32,38
|
22,32,38
|
||||||
22,33,8
|
22,33,8
|
||||||
22,34,6
|
22,34,6
|
||||||
22,35,2
|
22,35,2
|
||||||
23,31,19
|
23,31,19
|
||||||
23,32,11
|
23,32,11
|
||||||
23,33,6
|
23,33,6
|
||||||
23,34,18
|
23,34,18
|
||||||
23,35,4
|
23,35,4
|
||||||
24,31,10
|
24,31,10
|
||||||
24,32,10
|
24,32,10
|
||||||
24,33,4
|
24,33,4
|
||||||
24,34,17
|
24,34,17
|
||||||
24,35,19
|
24,35,19
|
||||||
25,31,0
|
25,31,0
|
||||||
25,32,10
|
25,32,10
|
||||||
25,33,4
|
25,33,4
|
||||||
25,34,23
|
25,34,23
|
||||||
26,31,4
|
26,31,4
|
||||||
26,32,2
|
26,32,2
|
||||||
26,33,2
|
26,33,2
|
||||||
26,34,17
|
26,34,17
|
||||||
26,35,8
|
26,35,8
|
||||||
27,31,13
|
27,31,13
|
||||||
27,32,6
|
27,32,6
|
||||||
27,33,7
|
27,33,7
|
||||||
27,34,9
|
27,34,9
|
||||||
28,31,18
|
28,31,18
|
||||||
28,32,3
|
28,32,3
|
||||||
28,33,9
|
28,33,9
|
||||||
28,34,19
|
28,34,19
|
||||||
29,31,3
|
42,31,11
|
||||||
29,32,7
|
42,32,4
|
||||||
29,33,6
|
42,33,18
|
||||||
29,34,16
|
42,34,34
|
||||||
30,31,20
|
42,35,13
|
||||||
30,32,13
|
43,31,18
|
||||||
30,33,10
|
43,32,38
|
||||||
30,34,18
|
43,33,5
|
||||||
31,31,39
|
43,34,7
|
||||||
31,32,12
|
44,31,8
|
||||||
31,33,20
|
44,32,29
|
||||||
31,34,20
|
44,33,0
|
||||||
32,31,4
|
44,34,22
|
||||||
32,32,8
|
45,31,6
|
||||||
32,33,13
|
45,32,6
|
||||||
32,34,20
|
45,33,7
|
||||||
33,31,7
|
45,34,3
|
||||||
33,32,15
|
46,31,13
|
||||||
33,33,9
|
46,32,8
|
||||||
33,34,14
|
46,33,11
|
||||||
33,35,18
|
46,34,24
|
||||||
34,31,30
|
47,31,48
|
||||||
34,32,19
|
47,32,6
|
||||||
34,33,9
|
47,33,13
|
||||||
34,34,17
|
47,34,3
|
||||||
34,35,20
|
48,31,41
|
||||||
35,31,74
|
48,32,7
|
||||||
35,32,20
|
48,33,0
|
||||||
35,33,14
|
48,34,39
|
||||||
35,34,9
|
48,35,3
|
||||||
36,31,6
|
49,31,51
|
||||||
36,32,7
|
49,32,11
|
||||||
36,33,21
|
49,33,15
|
||||||
36,34,2
|
49,34,2
|
||||||
36,35,12
|
49,35,19
|
||||||
37,31,14
|
50,31,18
|
||||||
37,32,0
|
50,32,9
|
||||||
37,33,10
|
50,33,1
|
||||||
37,34,13
|
50,34,17
|
||||||
37,35,14
|
50,35,8
|
||||||
38,31,17
|
29,31,3
|
||||||
38,32,20
|
29,32,7
|
||||||
38,33,9
|
29,33,6
|
||||||
38,34,18
|
29,34,16
|
||||||
38,35,2
|
30,31,20
|
||||||
39,31,15
|
30,32,13
|
||||||
39,32,5
|
30,33,10
|
||||||
39,33,14
|
30,34,18
|
||||||
39,34,4
|
31,31,39
|
||||||
40,31,5
|
31,32,12
|
||||||
40,32,7
|
31,33,20
|
||||||
40,33,16
|
31,34,20
|
||||||
40,34,5
|
32,31,4
|
||||||
41,31,18
|
32,32,8
|
||||||
41,32,29
|
32,33,13
|
||||||
41,33,13
|
32,34,20
|
||||||
41,34,15
|
33,31,7
|
||||||
41,35,10
|
33,32,15
|
||||||
1,31,7
|
33,33,9
|
||||||
1,32,4
|
33,34,14
|
||||||
1,33,2
|
33,35,18
|
||||||
1,34,0
|
34,31,30
|
||||||
1,35,12
|
34,32,19
|
||||||
2,31,18
|
34,33,9
|
||||||
2,32,10
|
34,34,17
|
||||||
2,33,11
|
34,35,20
|
||||||
2,34,18
|
35,31,74
|
||||||
3,31,29
|
35,32,20
|
||||||
3,32,4
|
35,33,14
|
||||||
3,33,4
|
35,34,9
|
||||||
3,34,7
|
36,31,6
|
||||||
4,31,35
|
36,32,7
|
||||||
4,32,6
|
36,33,21
|
||||||
4,33,2
|
36,34,2
|
||||||
4,34,0
|
36,35,12
|
||||||
4,35,4
|
37,31,14
|
||||||
5,31,31
|
37,32,0
|
||||||
5,32,10
|
37,33,10
|
||||||
5,33,17
|
37,34,13
|
||||||
5,34,10
|
37,35,14
|
||||||
6,31,17
|
38,31,17
|
||||||
6,32,7
|
38,32,20
|
||||||
6,33,7
|
38,33,9
|
||||||
6,34,8
|
38,34,18
|
||||||
6,35,3
|
38,35,2
|
||||||
7,31,15
|
39,31,15
|
||||||
7,32,3
|
39,32,5
|
||||||
7,33,18
|
39,33,14
|
||||||
7,34,2
|
39,34,4
|
||||||
7,35,17
|
40,31,5
|
||||||
8,31,27
|
40,32,7
|
||||||
8,32,7
|
40,33,16
|
||||||
8,33,17
|
40,34,5
|
||||||
8,34,18
|
41,31,18
|
||||||
8,35,8
|
41,32,29
|
||||||
9,31,6
|
41,33,13
|
||||||
9,32,3
|
41,34,15
|
||||||
9,33,9
|
41,35,10
|
||||||
9,34,5
|
1,31,7
|
||||||
9,35,4
|
1,32,4
|
||||||
10,31,7
|
1,33,2
|
||||||
10,32,13
|
1,34,0
|
||||||
10,33,12
|
1,35,12
|
||||||
10,34,16
|
2,31,18
|
||||||
10,35,2
|
2,32,10
|
||||||
11,31,20
|
2,33,11
|
||||||
11,32,4
|
2,34,18
|
||||||
11,33,6
|
3,31,29
|
||||||
11,34,9
|
3,32,4
|
||||||
12,31,13
|
3,33,4
|
||||||
12,32,9
|
3,34,7
|
||||||
12,33,5
|
4,31,35
|
||||||
12,34,9
|
4,32,6
|
||||||
12,35,9
|
4,33,2
|
||||||
13,31,24
|
4,34,0
|
||||||
13,32,7
|
4,35,4
|
||||||
13,33,3
|
5,31,31
|
||||||
13,34,3
|
5,32,10
|
||||||
14,31,5
|
5,33,17
|
||||||
14,32,2
|
5,34,10
|
||||||
14,33,2
|
6,31,17
|
||||||
14,34,8
|
6,32,7
|
||||||
42,31,11
|
6,33,7
|
||||||
42,32,4
|
6,34,8
|
||||||
42,33,18
|
6,35,3
|
||||||
42,34,34
|
7,31,15
|
||||||
42,35,13
|
7,32,3
|
||||||
43,31,18
|
7,33,18
|
||||||
43,32,38
|
7,34,2
|
||||||
43,33,5
|
7,35,17
|
||||||
43,34,7
|
8,31,27
|
||||||
44,31,8
|
8,32,7
|
||||||
44,32,29
|
8,33,17
|
||||||
44,33,0
|
8,34,18
|
||||||
44,34,22
|
8,35,8
|
||||||
45,31,6
|
9,31,6
|
||||||
45,32,6
|
9,32,3
|
||||||
45,33,7
|
9,33,9
|
||||||
45,34,3
|
9,34,5
|
||||||
46,31,13
|
9,35,4
|
||||||
46,32,8
|
10,31,7
|
||||||
46,33,11
|
10,32,13
|
||||||
46,34,24
|
10,33,12
|
||||||
47,31,48
|
10,34,16
|
||||||
47,32,6
|
10,35,2
|
||||||
47,33,13
|
11,31,20
|
||||||
47,34,3
|
11,32,4
|
||||||
48,31,41
|
11,33,6
|
||||||
48,32,7
|
11,34,9
|
||||||
48,33,0
|
12,31,13
|
||||||
48,34,39
|
12,32,9
|
||||||
48,35,3
|
12,33,5
|
||||||
49,31,51
|
12,34,9
|
||||||
49,32,11
|
12,35,9
|
||||||
49,33,15
|
13,31,24
|
||||||
49,34,2
|
13,32,7
|
||||||
49,35,19
|
13,33,3
|
||||||
50,31,18
|
13,34,3
|
||||||
50,32,9
|
14,31,5
|
||||||
50,33,1
|
14,32,2
|
||||||
50,34,17
|
14,33,2
|
||||||
50,35,8
|
14,34,8
|
||||||
|
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
Product_ID,Right_Product_Name,Right_Product_Category,Right_Product_Cost,Right_Product_Price
|
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
||||||
100,Non-product,NoCat,$1,$1
|
100,Non-product,NoCat,$1,$1
|
||||||
|
|
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
||||||
1,Action Figure,Toys,$9.99,$15.99
|
1,Action Figure,Toys,$9.99,$15.99
|
||||||
2,Animal Figures,Toys,$9.99,$12.99
|
2,Animal Figures,Toys,$9.99,$12.99
|
||||||
3,Barrel O' Slime,Art & Crafts,$1.99,$3.99
|
3,Barrel O' Slime,Art & Crafts,$1.99,$3.99
|
||||||
4,Chutes & Ladders,Games,$9.99,$12.99
|
4,Chutes & Ladders,Games,$9.99,$12.99
|
||||||
5,Classic Dominoes,Games,$7.99,$9.99
|
5,Classic Dominoes,Games,$7.99,$9.99
|
||||||
6,Colorbuds,Electronics,$6.99,$14.99
|
6,Colorbuds,Electronics,$6.99,$14.99
|
||||||
7,Dart Gun,Sports & Outdoors,$11.99,$15.99
|
7,Dart Gun,Sports & Outdoors,$11.99,$15.99
|
||||||
8,Deck Of Cards,Games,$3.99,$6.99
|
8,Deck Of Cards,Games,$3.99,$6.99
|
||||||
9,Dino Egg,Toys,$9.99,$10.99
|
9,Dino Egg,Toys,$9.99,$10.99
|
||||||
10,Dinosaur Figures,Toys,$10.99,$14.99
|
10,Dinosaur Figures,Toys,$10.99,$14.99
|
||||||
11,Etch A Sketch,Art & Crafts,$10.99,$20.99
|
11,Etch A Sketch,Art & Crafts,$10.99,$20.99
|
||||||
12,Foam Disk Launcher,Sports & Outdoors,$8.99,$11.99
|
12,Foam Disk Launcher,Sports & Outdoors,$8.99,$11.99
|
||||||
13,Gamer Headphones,Electronics,$14.99,$20.99
|
13,Gamer Headphones,Electronics,$14.99,$20.99
|
||||||
14,Glass Marbles,Games,$5.99,$10.99
|
14,Glass Marbles,Games,$5.99,$10.99
|
||||||
15,Hot Wheels 5-Pack,Toys,$3.99,$5.99
|
15,Hot Wheels 5-Pack,Toys,$3.99,$5.99
|
||||||
16,Jenga,Games,$2.99,$9.99
|
16,Jenga,Games,$2.99,$9.99
|
||||||
17,Kids Makeup Kit,Art & Crafts,$13.99,$19.99
|
17,Kids Makeup Kit,Art & Crafts,$13.99,$19.99
|
||||||
18,Lego Bricks,Toys,$34.99,$39.99
|
18,Lego Bricks,Toys,$34.99,$39.99
|
||||||
19,Magic Sand,Art & Crafts,$13.99,$15.99
|
19,Magic Sand,Art & Crafts,$13.99,$15.99
|
||||||
20,Mini Basketball Hoop,Sports & Outdoors,$8.99,$24.99
|
20,Mini Basketball Hoop,Sports & Outdoors,$8.99,$24.99
|
||||||
21,Mini Ping Pong Set,Sports & Outdoors,$6.99,$9.99
|
21,Mini Ping Pong Set,Sports & Outdoors,$6.99,$9.99
|
||||||
22,Monopoly,Games,$13.99,$19.99
|
22,Monopoly,Games,$13.99,$19.99
|
||||||
23,Mr. Potatohead,Toys,$4.99,$9.99
|
23,Mr. Potatohead,Toys,$4.99,$9.99
|
||||||
24,Nerf Gun,Sports & Outdoors,$14.99,$19.99
|
24,Nerf Gun,Sports & Outdoors,$14.99,$19.99
|
||||||
25,PlayDoh Can,Art & Crafts,$1.99,$2.99
|
25,PlayDoh Can,Art & Crafts,$1.99,$2.99
|
||||||
26,PlayDoh Playset,Art & Crafts,$20.99,$24.99
|
26,PlayDoh Playset,Art & Crafts,$20.99,$24.99
|
||||||
27,PlayDoh Toolkit,Art & Crafts,$3.99,$4.99
|
27,PlayDoh Toolkit,Art & Crafts,$3.99,$4.99
|
||||||
28,Playfoam,Art & Crafts,$3.99,$10.99
|
28,Playfoam,Art & Crafts,$3.99,$10.99
|
||||||
29,Plush Pony,Toys,$8.99,$19.99
|
29,Plush Pony,Toys,$8.99,$19.99
|
||||||
30,Rubik's Cube,Games,$17.99,$19.99
|
30,Rubik's Cube,Games,$17.99,$19.99
|
||||||
100,Non-product,NoCat,$1,$1
|
100,Non-product,NoCat,$1,$1
|
||||||
|
|
|
||||||
|
|
|
@ -74,7 +74,10 @@ class OutputDataTool(BaseTool):
|
||||||
or "True"
|
or "True"
|
||||||
)
|
)
|
||||||
header = header_val.lower() != "false"
|
header = header_val.lower() != "false"
|
||||||
df.write_csv(str(path), separator=delim, include_header=header)
|
line_end = (opts.findtext("LineEndStyle") or "LF").strip().upper()
|
||||||
|
eol = "\r\n" if line_end == "CRLF" else "\n"
|
||||||
|
df.write_csv(str(path), separator=delim, include_header=header,
|
||||||
|
line_terminator=eol)
|
||||||
elif fmt == 25: # Excel
|
elif fmt == 25: # Excel
|
||||||
df.write_excel(str(path))
|
df.write_excel(str(path))
|
||||||
elif fmt == 2: # Parquet
|
elif fmt == 2: # Parquet
|
||||||
|
|
|
||||||
|
|
@ -66,74 +66,80 @@ class JoinTool(BaseTool):
|
||||||
"""Apply field selection and renaming from SelectConfiguration."""
|
"""Apply field selection and renaming from SelectConfiguration."""
|
||||||
if df.is_empty() or self.config is None:
|
if df.is_empty() or self.config is None:
|
||||||
return df
|
return df
|
||||||
|
|
||||||
select_config = self.config.find("SelectConfiguration")
|
select_config = self.config.find("SelectConfiguration")
|
||||||
if select_config is None:
|
if select_config is None:
|
||||||
return df
|
return df
|
||||||
|
|
||||||
# Find the Configuration for this output connection
|
# Find the Configuration for this output connection
|
||||||
for cfg in select_config.findall("Configuration"):
|
for cfg in select_config.findall("Configuration"):
|
||||||
if cfg.attrib.get("outputConnection") == output_connection:
|
if cfg.attrib.get("outputConnection") == output_connection:
|
||||||
select_fields = cfg.find("SelectFields")
|
select_fields = cfg.find("SelectFields")
|
||||||
if select_fields is None:
|
if select_fields is None:
|
||||||
return df
|
return df
|
||||||
|
|
||||||
# Build column mapping
|
order_changed_el = cfg.find("OrderChanged")
|
||||||
# First, collect explicitly selected fields
|
order_changed = (
|
||||||
explicit_selections = [] # list of (src_col, output_name)
|
order_changed_el is not None
|
||||||
|
and order_changed_el.attrib.get("value", "False") == "True"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parse field rules
|
||||||
|
rename_map: dict[str, str] = {} # src_col → output_name
|
||||||
|
exclude_set: set[str] = set() # columns explicitly excluded
|
||||||
|
explicit_order: list[str] = [] # for OrderChanged=True
|
||||||
has_unknown = False
|
has_unknown = False
|
||||||
|
unknown_selected = True
|
||||||
|
|
||||||
for sf in select_fields.findall("SelectField"):
|
for sf in select_fields.findall("SelectField"):
|
||||||
field = sf.attrib.get("field", "")
|
field = sf.attrib.get("field", "")
|
||||||
selected = sf.attrib.get("selected", "False") == "True"
|
selected = sf.attrib.get("selected", "True") == "True"
|
||||||
rename = sf.attrib.get("rename", "")
|
rename = sf.attrib.get("rename", "")
|
||||||
input_prefix = sf.attrib.get("input", "")
|
|
||||||
|
|
||||||
if not selected:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if field == "*Unknown":
|
if field == "*Unknown":
|
||||||
has_unknown = True
|
has_unknown = True
|
||||||
|
unknown_selected = selected
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Resolve column name in the DataFrame
|
||||||
|
if field not in df.columns:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not selected:
|
||||||
|
exclude_set.add(field)
|
||||||
else:
|
else:
|
||||||
# Find the column with prefix
|
explicit_order.append(field)
|
||||||
src_col = f"{input_prefix}{field}" if input_prefix else field
|
if rename and rename != field:
|
||||||
if src_col in df.columns:
|
rename_map[field] = rename
|
||||||
output_name = rename if rename else field
|
|
||||||
explicit_selections.append((src_col, output_name))
|
|
||||||
elif field in df.columns:
|
|
||||||
output_name = rename if rename else field
|
|
||||||
explicit_selections.append((field, output_name))
|
|
||||||
|
|
||||||
# Build final column list
|
# Build final column list
|
||||||
selected_cols = []
|
mentioned = set(explicit_order) | exclude_set
|
||||||
rename_map = {}
|
|
||||||
|
if order_changed:
|
||||||
# Add explicitly selected columns
|
# Explicit selections first (in specified order), then *Unknown
|
||||||
for src, dst in explicit_selections:
|
final_cols = list(explicit_order)
|
||||||
selected_cols.append(src)
|
if has_unknown and unknown_selected:
|
||||||
if src != dst:
|
for col in df.columns:
|
||||||
rename_map[src] = dst
|
if col not in mentioned:
|
||||||
|
final_cols.append(col)
|
||||||
# Handle *Unknown: include all remaining columns, stripping prefixes
|
else:
|
||||||
if has_unknown:
|
# Preserve original DataFrame column order
|
||||||
explicit_srcs = {src for src, _ in explicit_selections}
|
final_cols = []
|
||||||
for col in df.columns:
|
for col in df.columns:
|
||||||
if col not in explicit_srcs:
|
if col in exclude_set:
|
||||||
# Strip Left_/Right_ prefix for output name
|
continue
|
||||||
output_name = col
|
if col in mentioned or (has_unknown and unknown_selected):
|
||||||
if col.startswith("Left_"):
|
final_cols.append(col)
|
||||||
output_name = col[5:]
|
elif not has_unknown and col not in mentioned:
|
||||||
elif col.startswith("Right_"):
|
# Default: include if not explicitly excluded
|
||||||
output_name = col[6:]
|
final_cols.append(col)
|
||||||
selected_cols.append(col)
|
|
||||||
if col != output_name:
|
if final_cols:
|
||||||
rename_map[col] = output_name
|
df = df.select(final_cols)
|
||||||
|
if rename_map:
|
||||||
# Apply selection and renaming
|
df = df.rename(
|
||||||
if selected_cols:
|
{k: v for k, v in rename_map.items() if k in df.columns}
|
||||||
df = df.select(selected_cols)
|
)
|
||||||
if rename_map:
|
|
||||||
df = df.rename(rename_map)
|
|
||||||
break
|
break
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
@ -145,45 +151,40 @@ class JoinTool(BaseTool):
|
||||||
) -> tuple[pl.DataFrame, pl.DataFrame, pl.DataFrame]:
|
) -> tuple[pl.DataFrame, pl.DataFrame, pl.DataFrame]:
|
||||||
con = self.ctx.duckdb_con
|
con = self.ctx.duckdb_con
|
||||||
|
|
||||||
# Disambiguate conflicting column names
|
|
||||||
key_l = {k[0] for k in join_keys}
|
key_l = {k[0] for k in join_keys}
|
||||||
key_r = {k[1] for k in join_keys}
|
key_r = {k[1] for k in join_keys}
|
||||||
l_non_key = [c for c in left.columns if c not in key_l]
|
l_non_key = [c for c in left.columns if c not in key_l]
|
||||||
r_non_key = [c for c in right.columns if c not in key_r]
|
r_non_key = [c for c in right.columns if c not in key_r]
|
||||||
|
# Only right non-key columns that clash with left columns need a prefix
|
||||||
conflicts = set(l_non_key) & set(r_non_key)
|
conflicts = set(l_non_key) & set(r_non_key)
|
||||||
|
|
||||||
# Prefix all non-key columns: Left_ for left, Right_ for right
|
# Register the original (un-prefixed) tables
|
||||||
# This matches Alteryx behavior where SelectConfiguration references
|
con.register("__join_left__", left.to_arrow())
|
||||||
# fields with these prefixes
|
con.register("__join_right__", right.to_arrow())
|
||||||
rename_l = {c: f"Left_{c}" for c in l_non_key}
|
|
||||||
rename_r = {c: f"Right_{c}" for c in r_non_key}
|
|
||||||
# But keep join keys without prefix (they come from left)
|
|
||||||
left_r = left.rename(rename_l) if rename_l else left
|
|
||||||
right_r = right.rename(rename_r) if rename_r else right
|
|
||||||
|
|
||||||
con.register("__join_left__", left_r.to_arrow())
|
|
||||||
con.register("__join_right__", right_r.to_arrow())
|
|
||||||
|
|
||||||
# Map renamed key column names
|
|
||||||
def lk(k: str) -> str:
|
|
||||||
return rename_l.get(k, k)
|
|
||||||
|
|
||||||
def rk(k: str) -> str:
|
|
||||||
return rename_r.get(k, k)
|
|
||||||
|
|
||||||
on_clause = " AND ".join(
|
on_clause = " AND ".join(
|
||||||
f'l."{lk(k[0])}" = r."{rk(k[1])}"' for k in join_keys
|
f'l."{k[0]}" = r."{k[1]}"' for k in join_keys
|
||||||
)
|
)
|
||||||
|
|
||||||
# Include right join keys with Right_ prefix for SelectConfiguration
|
# --- Inner join SELECT ------------------------------------------------
|
||||||
r_key_cols_sql = ", ".join(f'r."{rk(k[1])}" AS "Right_{k[1]}"' for k in join_keys)
|
# Left columns first (no prefix), then right join keys with Right_
|
||||||
r_cols_sql = ", ".join(f'r."{rk(c)}"' for c in r_non_key)
|
# prefix, then right non-key columns (Right_ prefix only on conflicts).
|
||||||
if r_key_cols_sql:
|
l_cols_sql = ", ".join(f'l."{c}"' for c in left.columns)
|
||||||
r_cols_sql = f"{r_key_cols_sql}, {r_cols_sql}"
|
r_key_cols_sql = ", ".join(
|
||||||
|
f'r."{k[1]}" AS "Right_{k[1]}"' for k in join_keys
|
||||||
r_key0 = rk(join_keys[0][1])
|
)
|
||||||
l_key0 = lk(join_keys[0][0])
|
r_non_key_sql = ", ".join(
|
||||||
j_sql = f"SELECT l.*, {r_cols_sql} FROM __join_left__ l INNER JOIN __join_right__ r ON {on_clause}"
|
f'r."{c}" AS "Right_{c}"' if c in conflicts else f'r."{c}"'
|
||||||
|
for c in r_non_key
|
||||||
|
)
|
||||||
|
j_parts = [p for p in (l_cols_sql, r_key_cols_sql, r_non_key_sql) if p]
|
||||||
|
j_select = ", ".join(j_parts)
|
||||||
|
|
||||||
|
l_key0 = join_keys[0][0]
|
||||||
|
r_key0 = join_keys[0][1]
|
||||||
|
|
||||||
|
j_sql = f"SELECT {j_select} FROM __join_left__ l INNER JOIN __join_right__ r ON {on_clause}"
|
||||||
|
# Left/right unmatched keep original column names (no prefixes)
|
||||||
l_sql = f'SELECT l.* FROM __join_left__ l LEFT JOIN __join_right__ r ON {on_clause} WHERE r."{r_key0}" IS NULL'
|
l_sql = f'SELECT l.* FROM __join_left__ l LEFT JOIN __join_right__ r ON {on_clause} WHERE r."{r_key0}" IS NULL'
|
||||||
r_sql = f'SELECT r.* FROM __join_right__ r LEFT JOIN __join_left__ l ON {on_clause} WHERE l."{l_key0}" IS NULL'
|
r_sql = f'SELECT r.* FROM __join_right__ r LEFT JOIN __join_left__ l ON {on_clause} WHERE l."{l_key0}" IS NULL'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue