@@ -94,10 +94,42 @@ class IntegerEncoding:
9494
9595
9696@dataclass (frozen = True )
97- class ExpressionOrdering :
98- """Immutable object that holds information about the ordering of rows in a ArrayValue object."""
97+ class RowOrdering :
98+ """Immutable object that holds information about the ordering of rows in a ArrayValue object. May not be unambiguous. """
9999
100100 ordering_value_columns : typing .Tuple [OrderingExpression , ...] = ()
101+
102+ @property
103+ def all_ordering_columns (self ) -> Sequence [OrderingExpression ]:
104+ return list (self .ordering_value_columns )
105+
106+ @property
107+ def referenced_columns (self ) -> Set [str ]:
108+ return set (
109+ col
110+ for part in self .ordering_value_columns
111+ for col in part .scalar_expression .unbound_variables
112+ )
113+
114+ def with_reverse (self ) -> RowOrdering :
115+ """Reverses the ordering."""
116+ return RowOrdering (
117+ tuple ([col .with_reverse () for col in self .ordering_value_columns ]),
118+ )
119+
120+ def with_column_remap (self , mapping : typing .Mapping [str , str ]) -> RowOrdering :
121+ new_value_columns = [
122+ col .remap_names (mapping ) for col in self .all_ordering_columns
123+ ]
124+ return TotalOrdering (
125+ tuple (new_value_columns ),
126+ )
127+
128+
129+ @dataclass (frozen = True )
130+ class TotalOrdering (RowOrdering ):
131+ """Immutable object that holds information about the ordering of rows in a ArrayValue object. Guaranteed to be unambiguous."""
132+
101133 integer_encoding : IntegerEncoding = IntegerEncoding (False )
102134 string_encoding : StringEncoding = StringEncoding (False )
103135 # A table has a total ordering defined by the identities of a set of 1 or more columns.
@@ -106,8 +138,8 @@ class ExpressionOrdering:
106138 total_ordering_columns : frozenset [str ] = field (default_factory = frozenset )
107139
108140 @classmethod
109- def from_offset_col (cls , col : str ) -> ExpressionOrdering :
110- return ExpressionOrdering (
141+ def from_offset_col (cls , col : str ) -> TotalOrdering :
142+ return TotalOrdering (
111143 (ascending_over (col ),),
112144 integer_encoding = IntegerEncoding (True , is_sequential = True ),
113145 total_ordering_columns = frozenset ({col }),
@@ -119,7 +151,7 @@ def with_non_sequential(self):
119151 This is useful when filtering, but not sorting, an expression.
120152 """
121153 if self .integer_encoding .is_sequential :
122- return ExpressionOrdering (
154+ return TotalOrdering (
123155 self .ordering_value_columns ,
124156 integer_encoding = IntegerEncoding (
125157 self .integer_encoding .is_encoded , is_sequential = False
@@ -132,7 +164,7 @@ def with_non_sequential(self):
132164 def with_ordering_columns (
133165 self ,
134166 ordering_value_columns : Sequence [OrderingExpression ] = (),
135- ) -> ExpressionOrdering :
167+ ) -> TotalOrdering :
136168 """Creates a new ordering that reorders by the given columns.
137169
138170 Args:
@@ -147,7 +179,7 @@ def with_ordering_columns(
147179 new_ordering = self ._truncate_ordering (
148180 (* ordering_value_columns , * self .ordering_value_columns )
149181 )
150- return ExpressionOrdering (
182+ return TotalOrdering (
151183 new_ordering ,
152184 total_ordering_columns = self .total_ordering_columns ,
153185 )
@@ -173,7 +205,7 @@ def _truncate_ordering(
173205
174206 def with_reverse (self ):
175207 """Reverses the ordering."""
176- return ExpressionOrdering (
208+ return TotalOrdering (
177209 tuple ([col .with_reverse () for col in self .ordering_value_columns ]),
178210 total_ordering_columns = self .total_ordering_columns ,
179211 )
@@ -185,7 +217,7 @@ def with_column_remap(self, mapping: typing.Mapping[str, str]):
185217 new_total_order = frozenset (
186218 mapping .get (col_id , col_id ) for col_id in self .total_ordering_columns
187219 )
188- return ExpressionOrdering (
220+ return TotalOrdering (
189221 tuple (new_value_columns ),
190222 integer_encoding = self .integer_encoding ,
191223 string_encoding = self .string_encoding ,
@@ -211,18 +243,6 @@ def is_string_encoded(self) -> bool:
211243 def is_sequential (self ) -> bool :
212244 return self .integer_encoding .is_encoded and self .integer_encoding .is_sequential
213245
214- @property
215- def all_ordering_columns (self ) -> Sequence [OrderingExpression ]:
216- return list (self .ordering_value_columns )
217-
218- @property
219- def referenced_columns (self ) -> Set [str ]:
220- return set (
221- col
222- for part in self .ordering_value_columns
223- for col in part .scalar_expression .unbound_variables
224- )
225-
226246
227247def encode_order_string (
228248 order_id : ibis_types .IntegerColumn , length : int = DEFAULT_ORDERING_ID_LENGTH
@@ -257,3 +277,58 @@ def descending_over(id: str, nulls_last: bool = True) -> OrderingExpression:
257277 return OrderingExpression (
258278 expression .free_var (id ), direction = OrderingDirection .DESC , na_last = nulls_last
259279 )
280+
281+
282+ @typing .overload
283+ def join_orderings (
284+ left : TotalOrdering ,
285+ right : TotalOrdering ,
286+ left_id_mapping : Mapping [str , str ],
287+ right_id_mapping : Mapping [str , str ],
288+ left_order_dominates : bool = True ,
289+ ) -> TotalOrdering :
290+ ...
291+
292+
293+ @typing .overload
294+ def join_orderings (
295+ left : RowOrdering ,
296+ right : RowOrdering ,
297+ left_id_mapping : Mapping [str , str ],
298+ right_id_mapping : Mapping [str , str ],
299+ left_order_dominates : bool = True ,
300+ ) -> RowOrdering :
301+ ...
302+
303+
304+ def join_orderings (
305+ left : RowOrdering ,
306+ right : RowOrdering ,
307+ left_id_mapping : Mapping [str , str ],
308+ right_id_mapping : Mapping [str , str ],
309+ left_order_dominates : bool = True ,
310+ ) -> RowOrdering :
311+ left_ordering_refs = [
312+ ref .remap_names (left_id_mapping ) for ref in left .all_ordering_columns
313+ ]
314+ right_ordering_refs = [
315+ ref .remap_names (right_id_mapping ) for ref in right .all_ordering_columns
316+ ]
317+ if left_order_dominates :
318+ joined_refs = [* left_ordering_refs , * right_ordering_refs ]
319+ else :
320+ joined_refs = [* right_ordering_refs , * left_ordering_refs ]
321+
322+ if isinstance (left , TotalOrdering ) and isinstance (right , TotalOrdering ):
323+ left_total_order_cols = frozenset (
324+ [left_id_mapping [id ] for id in left .total_ordering_columns ]
325+ )
326+ right_total_order_cols = frozenset (
327+ [right_id_mapping [id ] for id in right .total_ordering_columns ]
328+ )
329+ return TotalOrdering (
330+ ordering_value_columns = tuple (joined_refs ),
331+ total_ordering_columns = left_total_order_cols | right_total_order_cols ,
332+ )
333+ else :
334+ return RowOrdering (tuple (joined_refs ))
0 commit comments