1use arrow::array::PrimitiveArray;
2use polars_core::chunked_array::ops::row_encode::{
3 encode_rows_unordered, encode_rows_vertical_par_unordered_broadcast_nulls,
4};
5use polars_core::series::BitRepr;
6use polars_core::utils::split;
7use polars_core::with_match_physical_float_polars_type;
8use polars_utils::aliases::PlRandomState;
9use polars_utils::hashing::DirtyHash;
10use polars_utils::nulls::IsNull;
11use polars_utils::total_ord::{ToTotalOrd, TotalEq, TotalHash};
12
13use super::*;
14use crate::series::SeriesSealed;
15
16pub trait SeriesJoin: SeriesSealed + Sized {
17 #[doc(hidden)]
18 fn hash_join_left(
19 &self,
20 other: &Series,
21 validate: JoinValidation,
22 nulls_equal: bool,
23 ) -> PolarsResult<LeftJoinIds> {
24 let s_self = self.as_series();
25 let (lhs, rhs) = (s_self.to_physical_repr(), other.to_physical_repr());
26 validate.validate_probe(&lhs, &rhs, false, nulls_equal)?;
27
28 let lhs_dtype = lhs.dtype();
29 let rhs_dtype = rhs.dtype();
30
31 use DataType as T;
32 match lhs_dtype {
33 T::String | T::Binary => {
34 let lhs = lhs.cast(&T::Binary).unwrap();
35 let rhs = rhs.cast(&T::Binary).unwrap();
36 let lhs = lhs.binary().unwrap();
37 let rhs = rhs.binary().unwrap();
38 let (lhs, rhs, _, _) = prepare_binary::<BinaryType>(lhs, rhs, false);
39 let lhs = lhs.iter().map(|v| v.as_slice()).collect::<Vec<_>>();
40 let rhs = rhs.iter().map(|v| v.as_slice()).collect::<Vec<_>>();
41 let build_null_count = other.null_count();
42 hash_join_tuples_left(
43 lhs,
44 rhs,
45 None,
46 None,
47 validate,
48 nulls_equal,
49 build_null_count,
50 )
51 },
52 T::BinaryOffset => {
53 let lhs = lhs.binary_offset().unwrap();
54 let rhs = rhs.binary_offset().unwrap();
55 let (lhs, rhs, _, _) = prepare_binary::<BinaryOffsetType>(lhs, rhs, false);
56 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
58 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
59 let build_null_count = other.null_count();
60 hash_join_tuples_left(
61 lhs,
62 rhs,
63 None,
64 None,
65 validate,
66 nulls_equal,
67 build_null_count,
68 )
69 },
70 T::List(_) => {
71 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
72 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
73 lhs.hash_join_left(rhs, validate, nulls_equal)
74 },
75 #[cfg(feature = "dtype-array")]
76 T::Array(_, _) => {
77 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
78 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
79 lhs.hash_join_left(rhs, validate, nulls_equal)
80 },
81 #[cfg(feature = "dtype-struct")]
82 T::Struct(_) => {
83 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
84 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
85 lhs.hash_join_left(rhs, validate, nulls_equal)
86 },
87 x if x.is_float() => {
88 with_match_physical_float_polars_type!(lhs.dtype(), |$T| {
89 let lhs: &ChunkedArray<$T> = lhs.as_ref().as_ref().as_ref();
90 let rhs: &ChunkedArray<$T> = rhs.as_ref().as_ref().as_ref();
91 num_group_join_left(lhs, rhs, validate, nulls_equal)
92 })
93 },
94 _ => {
95 let lhs = s_self.bit_repr();
96 let rhs = other.bit_repr();
97
98 let (Some(lhs), Some(rhs)) = (lhs, rhs) else {
99 polars_bail!(nyi = "Hash Left Join between {lhs_dtype} and {rhs_dtype}");
100 };
101
102 use BitRepr as B;
103 match (lhs, rhs) {
104 (B::U8(lhs), B::U8(rhs)) => {
105 num_group_join_left(&lhs, &rhs, validate, nulls_equal)
106 },
107 (B::U16(lhs), B::U16(rhs)) => {
108 num_group_join_left(&lhs, &rhs, validate, nulls_equal)
109 },
110 (B::U32(lhs), B::U32(rhs)) => {
111 num_group_join_left(&lhs, &rhs, validate, nulls_equal)
112 },
113 (B::U64(lhs), B::U64(rhs)) => {
114 num_group_join_left(&lhs, &rhs, validate, nulls_equal)
115 },
116 #[cfg(feature = "dtype-u128")]
117 (B::U128(lhs), B::U128(rhs)) => {
118 num_group_join_left(&lhs, &rhs, validate, nulls_equal)
119 },
120 _ => {
121 polars_bail!(
122 nyi = "Mismatch bit repr Hash Left Join between {lhs_dtype} and {rhs_dtype}",
123 );
124 },
125 }
126 },
127 }
128 }
129
130 #[cfg(feature = "semi_anti_join")]
131 fn hash_join_semi_anti(
132 &self,
133 other: &Series,
134 anti: bool,
135 nulls_equal: bool,
136 ) -> PolarsResult<Vec<IdxSize>> {
137 let s_self = self.as_series();
138 let (lhs, rhs) = (s_self.to_physical_repr(), other.to_physical_repr());
139
140 let lhs_dtype = lhs.dtype();
141 let rhs_dtype = rhs.dtype();
142
143 use DataType as T;
144 Ok(match lhs_dtype {
145 T::String | T::Binary => {
146 let lhs = lhs.cast(&T::Binary).unwrap();
147 let rhs = rhs.cast(&T::Binary).unwrap();
148 let lhs = lhs.binary().unwrap();
149 let rhs = rhs.binary().unwrap();
150 let (lhs, rhs, _, _) = prepare_binary::<BinaryType>(lhs, rhs, false);
151 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
153 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
154 if anti {
155 hash_join_tuples_left_anti(lhs, rhs, nulls_equal)
156 } else {
157 hash_join_tuples_left_semi(lhs, rhs, nulls_equal)
158 }
159 },
160 T::BinaryOffset => {
161 let lhs = lhs.binary_offset().unwrap();
162 let rhs = rhs.binary_offset().unwrap();
163 let (lhs, rhs, _, _) = prepare_binary::<BinaryOffsetType>(lhs, rhs, false);
164 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
166 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
167 if anti {
168 hash_join_tuples_left_anti(lhs, rhs, nulls_equal)
169 } else {
170 hash_join_tuples_left_semi(lhs, rhs, nulls_equal)
171 }
172 },
173 T::List(_) => {
174 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
175 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
176 lhs.hash_join_semi_anti(rhs, anti, nulls_equal)?
177 },
178 #[cfg(feature = "dtype-array")]
179 T::Array(_, _) => {
180 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
181 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
182 lhs.hash_join_semi_anti(rhs, anti, nulls_equal)?
183 },
184 #[cfg(feature = "dtype-struct")]
185 T::Struct(_) => {
186 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
187 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
188 lhs.hash_join_semi_anti(rhs, anti, nulls_equal)?
189 },
190 x if x.is_float() => {
191 with_match_physical_float_polars_type!(lhs.dtype(), |$T| {
192 let lhs: &ChunkedArray<$T> = lhs.as_ref().as_ref().as_ref();
193 let rhs: &ChunkedArray<$T> = rhs.as_ref().as_ref().as_ref();
194 num_group_join_anti_semi(lhs, rhs, anti, nulls_equal)
195 })
196 },
197 _ => {
198 let lhs = s_self.bit_repr();
199 let rhs = other.bit_repr();
200
201 let (Some(lhs), Some(rhs)) = (lhs, rhs) else {
202 polars_bail!(nyi = "Hash Semi-Anti Join between {lhs_dtype} and {rhs_dtype}");
203 };
204
205 use BitRepr as B;
206 match (lhs, rhs) {
207 (B::U8(lhs), B::U8(rhs)) => {
208 num_group_join_anti_semi(&lhs, &rhs, anti, nulls_equal)
209 },
210 (B::U16(lhs), B::U16(rhs)) => {
211 num_group_join_anti_semi(&lhs, &rhs, anti, nulls_equal)
212 },
213 (B::U32(lhs), B::U32(rhs)) => {
214 num_group_join_anti_semi(&lhs, &rhs, anti, nulls_equal)
215 },
216 (B::U64(lhs), B::U64(rhs)) => {
217 num_group_join_anti_semi(&lhs, &rhs, anti, nulls_equal)
218 },
219 #[cfg(feature = "dtype-u128")]
220 (B::U128(lhs), B::U128(rhs)) => {
221 num_group_join_anti_semi(&lhs, &rhs, anti, nulls_equal)
222 },
223 _ => {
224 polars_bail!(
225 nyi = "Mismatch bit repr Hash Semi-Anti Join between {lhs_dtype} and {rhs_dtype}",
226 );
227 },
228 }
229 },
230 })
231 }
232
233 fn hash_join_inner(
235 &self,
236 other: &Series,
237 validate: JoinValidation,
238 nulls_equal: bool,
239 ) -> PolarsResult<(InnerJoinIds, bool)> {
240 let s_self = self.as_series();
241 let (lhs, rhs) = (s_self.to_physical_repr(), other.to_physical_repr());
242 validate.validate_probe(&lhs, &rhs, true, nulls_equal)?;
243
244 let lhs_dtype = lhs.dtype();
245 let rhs_dtype = rhs.dtype();
246
247 use DataType as T;
248 match lhs_dtype {
249 T::String | T::Binary => {
250 let lhs = lhs.cast(&T::Binary).unwrap();
251 let rhs = rhs.cast(&T::Binary).unwrap();
252 let lhs = lhs.binary().unwrap();
253 let rhs = rhs.binary().unwrap();
254 let (lhs, rhs, swapped, _) = prepare_binary::<BinaryType>(lhs, rhs, true);
255 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
257 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
258 let build_null_count = if swapped {
259 s_self.null_count()
260 } else {
261 other.null_count()
262 };
263 Ok((
264 hash_join_tuples_inner(
265 lhs,
266 rhs,
267 swapped,
268 validate,
269 nulls_equal,
270 build_null_count,
271 )?,
272 !swapped,
273 ))
274 },
275 T::BinaryOffset => {
276 let lhs = lhs.binary_offset().unwrap();
277 let rhs = rhs.binary_offset()?;
278 let (lhs, rhs, swapped, _) = prepare_binary::<BinaryOffsetType>(lhs, rhs, true);
279 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
281 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
282 let build_null_count = if swapped {
283 s_self.null_count()
284 } else {
285 other.null_count()
286 };
287 Ok((
288 hash_join_tuples_inner(
289 lhs,
290 rhs,
291 swapped,
292 validate,
293 nulls_equal,
294 build_null_count,
295 )?,
296 !swapped,
297 ))
298 },
299 T::List(_) => {
300 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
301 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
302 lhs.hash_join_inner(rhs, validate, nulls_equal)
303 },
304 #[cfg(feature = "dtype-array")]
305 T::Array(_, _) => {
306 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
307 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
308 lhs.hash_join_inner(rhs, validate, nulls_equal)
309 },
310 #[cfg(feature = "dtype-struct")]
311 T::Struct(_) => {
312 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
313 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
314 lhs.hash_join_inner(rhs, validate, nulls_equal)
315 },
316 x if x.is_float() => {
317 with_match_physical_float_polars_type!(lhs.dtype(), |$T| {
318 let lhs: &ChunkedArray<$T> = lhs.as_ref().as_ref().as_ref();
319 let rhs: &ChunkedArray<$T> = rhs.as_ref().as_ref().as_ref();
320 group_join_inner::<$T>(lhs, rhs, validate, nulls_equal)
321 })
322 },
323 _ => {
324 let lhs = s_self.bit_repr();
325 let rhs = other.bit_repr();
326
327 let (Some(lhs), Some(rhs)) = (lhs, rhs) else {
328 polars_bail!(nyi = "Hash Inner Join between {lhs_dtype} and {rhs_dtype}");
329 };
330
331 use BitRepr as B;
332 match (lhs, rhs) {
333 (B::U8(lhs), B::U8(rhs)) => {
334 group_join_inner::<UInt8Type>(&lhs, &rhs, validate, nulls_equal)
335 },
336 (B::U16(lhs), B::U16(rhs)) => {
337 group_join_inner::<UInt16Type>(&lhs, &rhs, validate, nulls_equal)
338 },
339 (B::U32(lhs), B::U32(rhs)) => {
340 group_join_inner::<UInt32Type>(&lhs, &rhs, validate, nulls_equal)
341 },
342 (B::U64(lhs), BitRepr::U64(rhs)) => {
343 group_join_inner::<UInt64Type>(&lhs, &rhs, validate, nulls_equal)
344 },
345 #[cfg(feature = "dtype-u128")]
346 (B::U128(lhs), BitRepr::U128(rhs)) => {
347 group_join_inner::<UInt128Type>(&lhs, &rhs, validate, nulls_equal)
348 },
349 _ => {
350 polars_bail!(
351 nyi = "Mismatch bit repr Hash Inner Join between {lhs_dtype} and {rhs_dtype}"
352 );
353 },
354 }
355 },
356 }
357 }
358
359 fn hash_join_outer(
360 &self,
361 other: &Series,
362 validate: JoinValidation,
363 nulls_equal: bool,
364 ) -> PolarsResult<(PrimitiveArray<IdxSize>, PrimitiveArray<IdxSize>)> {
365 let s_self = self.as_series();
366 let (lhs, rhs) = (s_self.to_physical_repr(), other.to_physical_repr());
367 validate.validate_probe(&lhs, &rhs, true, nulls_equal)?;
368
369 let lhs_dtype = lhs.dtype();
370 let rhs_dtype = rhs.dtype();
371
372 use DataType as T;
373 match lhs_dtype {
374 T::String | T::Binary => {
375 let lhs = lhs.cast(&T::Binary).unwrap();
376 let rhs = rhs.cast(&T::Binary).unwrap();
377 let lhs = lhs.binary().unwrap();
378 let rhs = rhs.binary().unwrap();
379 let (lhs, rhs, swapped, _) = prepare_binary::<BinaryType>(lhs, rhs, true);
380 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
382 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
383 hash_join_tuples_outer(lhs, rhs, swapped, validate, nulls_equal)
384 },
385 T::BinaryOffset => {
386 let lhs = lhs.binary_offset().unwrap();
387 let rhs = rhs.binary_offset()?;
388 let (lhs, rhs, swapped, _) = prepare_binary::<BinaryOffsetType>(lhs, rhs, true);
389 let lhs = lhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
391 let rhs = rhs.iter().map(|k| k.as_slice()).collect::<Vec<_>>();
392 hash_join_tuples_outer(lhs, rhs, swapped, validate, nulls_equal)
393 },
394 T::List(_) => {
395 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
396 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
397 lhs.hash_join_outer(rhs, validate, nulls_equal)
398 },
399 #[cfg(feature = "dtype-array")]
400 T::Array(_, _) => {
401 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
402 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
403 lhs.hash_join_outer(rhs, validate, nulls_equal)
404 },
405 #[cfg(feature = "dtype-struct")]
406 T::Struct(_) => {
407 let lhs = &encode_join_nested_key(lhs.into_owned(), nulls_equal)?;
408 let rhs = &encode_join_nested_key(rhs.into_owned(), nulls_equal)?;
409 lhs.hash_join_outer(rhs, validate, nulls_equal)
410 },
411 x if x.is_float() => {
412 with_match_physical_float_polars_type!(lhs.dtype(), |$T| {
413 let lhs: &ChunkedArray<$T> = lhs.as_ref().as_ref().as_ref();
414 let rhs: &ChunkedArray<$T> = rhs.as_ref().as_ref().as_ref();
415 hash_join_outer(lhs, rhs, validate, nulls_equal)
416 })
417 },
418 _ => {
419 let (Some(lhs), Some(rhs)) = (s_self.bit_repr(), other.bit_repr()) else {
420 polars_bail!(nyi = "Hash Join Outer between {lhs_dtype} and {rhs_dtype}");
421 };
422
423 use BitRepr as B;
424 match (lhs, rhs) {
425 (B::U8(lhs), B::U8(rhs)) => hash_join_outer(&lhs, &rhs, validate, nulls_equal),
426 (B::U16(lhs), B::U16(rhs)) => {
427 hash_join_outer(&lhs, &rhs, validate, nulls_equal)
428 },
429 (B::U32(lhs), B::U32(rhs)) => {
430 hash_join_outer(&lhs, &rhs, validate, nulls_equal)
431 },
432 (B::U64(lhs), B::U64(rhs)) => {
433 hash_join_outer(&lhs, &rhs, validate, nulls_equal)
434 },
435 #[cfg(feature = "dtype-u128")]
436 (B::U128(lhs), B::U128(rhs)) => {
437 hash_join_outer(&lhs, &rhs, validate, nulls_equal)
438 },
439 _ => {
440 polars_bail!(
441 nyi = "Mismatch bit repr Hash Join Outer between {lhs_dtype} and {rhs_dtype}"
442 );
443 },
444 }
445 },
446 }
447 }
448}
449
450impl SeriesJoin for Series {}
451
452fn chunks_as_slices<T>(splitted: &[ChunkedArray<T>]) -> Vec<&[T::Native]>
453where
454 T: PolarsNumericType,
455{
456 splitted
457 .iter()
458 .flat_map(|ca| ca.downcast_iter().map(|arr| arr.values().as_slice()))
459 .collect()
460}
461
462fn encode_join_nested_key(s: Series, nulls_equal: bool) -> PolarsResult<Series> {
463 let by = [s.into_column()];
464 let encoded = if nulls_equal {
465 encode_rows_unordered(&by)?
466 } else {
467 encode_rows_vertical_par_unordered_broadcast_nulls(&by)?
468 };
469 Ok(encoded.into_series())
470}
471
472fn get_arrays<T: PolarsDataType>(cas: &[ChunkedArray<T>]) -> Vec<&T::Array> {
473 cas.iter().flat_map(|arr| arr.downcast_iter()).collect()
474}
475
476fn group_join_inner<T>(
477 left: &ChunkedArray<T>,
478 right: &ChunkedArray<T>,
479 validate: JoinValidation,
480 nulls_equal: bool,
481) -> PolarsResult<(InnerJoinIds, bool)>
482where
483 T: PolarsDataType,
484 for<'a> &'a T::Array: IntoIterator<Item = Option<&'a T::Physical<'a>>>,
485 for<'a> T::Physical<'a>:
486 Send + Sync + Copy + TotalHash + TotalEq + DirtyHash + IsNull + ToTotalOrd,
487 for<'a> <T::Physical<'a> as ToTotalOrd>::TotalOrdItem:
488 Send + Sync + Copy + Hash + Eq + DirtyHash + IsNull,
489{
490 let n_threads = RAYON.current_num_threads();
491 let (a, b, swapped) = det_hash_prone_order!(left, right);
492 let splitted_a = split(a, n_threads);
493 let splitted_b = split(b, n_threads);
494 let splitted_a = get_arrays(&splitted_a);
495 let splitted_b = get_arrays(&splitted_b);
496
497 match (left.null_count(), right.null_count()) {
498 (0, 0) => {
499 let first = &splitted_a[0];
500 if first.as_slice().is_some() {
501 let splitted_a = splitted_a
502 .iter()
503 .map(|arr| arr.as_slice().unwrap())
504 .collect::<Vec<_>>();
505 let splitted_b = splitted_b
506 .iter()
507 .map(|arr| arr.as_slice().unwrap())
508 .collect::<Vec<_>>();
509 Ok((
510 hash_join_tuples_inner(
511 splitted_a,
512 splitted_b,
513 swapped,
514 validate,
515 nulls_equal,
516 0,
517 )?,
518 !swapped,
519 ))
520 } else {
521 Ok((
522 hash_join_tuples_inner(
523 splitted_a,
524 splitted_b,
525 swapped,
526 validate,
527 nulls_equal,
528 0,
529 )?,
530 !swapped,
531 ))
532 }
533 },
534 _ => {
535 let build_null_count = if swapped {
536 left.null_count()
537 } else {
538 right.null_count()
539 };
540 Ok((
541 hash_join_tuples_inner(
542 splitted_a,
543 splitted_b,
544 swapped,
545 validate,
546 nulls_equal,
547 build_null_count,
548 )?,
549 !swapped,
550 ))
551 },
552 }
553}
554
555#[cfg(feature = "chunked_ids")]
556fn create_mappings(
557 chunks_left: &[ArrayRef],
558 chunks_right: &[ArrayRef],
559 left_len: usize,
560 right_len: usize,
561) -> (Option<Vec<ChunkId>>, Option<Vec<ChunkId>>) {
562 let mapping_left = || {
563 if chunks_left.len() > 1 {
564 Some(create_chunked_index_mapping(chunks_left, left_len))
565 } else {
566 None
567 }
568 };
569
570 let mapping_right = || {
571 if chunks_right.len() > 1 {
572 Some(create_chunked_index_mapping(chunks_right, right_len))
573 } else {
574 None
575 }
576 };
577
578 RAYON.join(mapping_left, mapping_right)
579}
580
581#[cfg(not(feature = "chunked_ids"))]
582fn create_mappings(
583 _chunks_left: &[ArrayRef],
584 _chunks_right: &[ArrayRef],
585 _left_len: usize,
586 _right_len: usize,
587) -> (Option<Vec<ChunkId>>, Option<Vec<ChunkId>>) {
588 (None, None)
589}
590
591fn num_group_join_left<T>(
592 left: &ChunkedArray<T>,
593 right: &ChunkedArray<T>,
594 validate: JoinValidation,
595 nulls_equal: bool,
596) -> PolarsResult<LeftJoinIds>
597where
598 T: PolarsNumericType,
599 T::Native: TotalHash + TotalEq + DirtyHash + IsNull + ToTotalOrd,
600 <T::Native as ToTotalOrd>::TotalOrdItem: Send + Sync + Copy + Hash + Eq + DirtyHash + IsNull,
601 T::Native: DirtyHash + Copy + ToTotalOrd,
602 <Option<T::Native> as ToTotalOrd>::TotalOrdItem: Send + Sync + DirtyHash,
603{
604 let n_threads = RAYON.current_num_threads();
605 let splitted_a = split(left, n_threads);
606 let splitted_b = split(right, n_threads);
607 match (
608 left.null_count(),
609 right.null_count(),
610 left.chunks().len(),
611 right.chunks().len(),
612 ) {
613 (0, 0, 1, 1) => {
614 let keys_a = chunks_as_slices(&splitted_a);
615 let keys_b = chunks_as_slices(&splitted_b);
616 hash_join_tuples_left(keys_a, keys_b, None, None, validate, nulls_equal, 0)
617 },
618 (0, 0, _, _) => {
619 let keys_a = chunks_as_slices(&splitted_a);
620 let keys_b = chunks_as_slices(&splitted_b);
621
622 let (mapping_left, mapping_right) =
623 create_mappings(left.chunks(), right.chunks(), left.len(), right.len());
624 hash_join_tuples_left(
625 keys_a,
626 keys_b,
627 mapping_left.as_deref(),
628 mapping_right.as_deref(),
629 validate,
630 nulls_equal,
631 0,
632 )
633 },
634 _ => {
635 let keys_a = get_arrays(&splitted_a);
636 let keys_b = get_arrays(&splitted_b);
637 let (mapping_left, mapping_right) =
638 create_mappings(left.chunks(), right.chunks(), left.len(), right.len());
639 let build_null_count = right.null_count();
640 hash_join_tuples_left(
641 keys_a,
642 keys_b,
643 mapping_left.as_deref(),
644 mapping_right.as_deref(),
645 validate,
646 nulls_equal,
647 build_null_count,
648 )
649 },
650 }
651}
652
653fn hash_join_outer<T>(
654 ca_in: &ChunkedArray<T>,
655 other: &ChunkedArray<T>,
656 validate: JoinValidation,
657 nulls_equal: bool,
658) -> PolarsResult<(PrimitiveArray<IdxSize>, PrimitiveArray<IdxSize>)>
659where
660 T: PolarsNumericType,
661 T::Native: TotalHash + TotalEq + ToTotalOrd,
662 <T::Native as ToTotalOrd>::TotalOrdItem: Send + Sync + Copy + Hash + Eq + IsNull,
663{
664 let (a, b, swapped) = det_hash_prone_order!(ca_in, other);
665
666 let n_partitions = _set_partition_size();
667 let splitted_a = split(a, n_partitions);
668 let splitted_b = split(b, n_partitions);
669
670 match (a.null_count(), b.null_count()) {
671 (0, 0) => {
672 let iters_a = splitted_a
673 .iter()
674 .flat_map(|ca| ca.downcast_iter().map(|arr| arr.values().as_slice()))
675 .collect::<Vec<_>>();
676 let iters_b = splitted_b
677 .iter()
678 .flat_map(|ca| ca.downcast_iter().map(|arr| arr.values().as_slice()))
679 .collect::<Vec<_>>();
680 hash_join_tuples_outer(iters_a, iters_b, swapped, validate, nulls_equal)
681 },
682 _ => {
683 let iters_a = splitted_a
684 .iter()
685 .flat_map(|ca| ca.downcast_iter().map(|arr| arr.iter()))
686 .collect::<Vec<_>>();
687 let iters_b = splitted_b
688 .iter()
689 .flat_map(|ca| ca.downcast_iter().map(|arr| arr.iter()))
690 .collect::<Vec<_>>();
691 hash_join_tuples_outer(iters_a, iters_b, swapped, validate, nulls_equal)
692 },
693 }
694}
695
696pub(crate) fn prepare_binary<'a, T>(
697 ca: &'a ChunkedArray<T>,
698 other: &'a ChunkedArray<T>,
699 build_shortest_table: bool,
702) -> (
703 Vec<Vec<BytesHash<'a>>>,
704 Vec<Vec<BytesHash<'a>>>,
705 bool,
706 PlRandomState,
707)
708where
709 T: PolarsDataType,
710 for<'b> <T::Array as StaticArray>::ValueT<'b>: AsRef<[u8]>,
711{
712 let (a, b, swapped) = if build_shortest_table {
713 det_hash_prone_order!(ca, other)
714 } else {
715 (ca, other, false)
716 };
717 let hb = PlRandomState::default();
718 let bh_a = a.to_bytes_hashes(true, hb.clone());
719 let bh_b = b.to_bytes_hashes(true, hb.clone());
720
721 (bh_a, bh_b, swapped, hb)
722}
723
724#[cfg(feature = "semi_anti_join")]
725fn num_group_join_anti_semi<T>(
726 left: &ChunkedArray<T>,
727 right: &ChunkedArray<T>,
728 anti: bool,
729 nulls_equal: bool,
730) -> Vec<IdxSize>
731where
732 T: PolarsNumericType,
733 T::Native: TotalHash + TotalEq + DirtyHash + ToTotalOrd,
734 <T::Native as ToTotalOrd>::TotalOrdItem: Send + Sync + Copy + Hash + Eq + DirtyHash + IsNull,
735 <Option<T::Native> as ToTotalOrd>::TotalOrdItem: Send + Sync + DirtyHash + IsNull,
736{
737 let n_threads = RAYON.current_num_threads();
738 let splitted_a = split(left, n_threads);
739 let splitted_b = split(right, n_threads);
740 match (
741 left.null_count(),
742 right.null_count(),
743 left.chunks().len(),
744 right.chunks().len(),
745 ) {
746 (0, 0, 1, 1) => {
747 let keys_a = chunks_as_slices(&splitted_a);
748 let keys_b = chunks_as_slices(&splitted_b);
749 if anti {
750 hash_join_tuples_left_anti(keys_a, keys_b, nulls_equal)
751 } else {
752 hash_join_tuples_left_semi(keys_a, keys_b, nulls_equal)
753 }
754 },
755 (0, 0, _, _) => {
756 let keys_a = chunks_as_slices(&splitted_a);
757 let keys_b = chunks_as_slices(&splitted_b);
758 if anti {
759 hash_join_tuples_left_anti(keys_a, keys_b, nulls_equal)
760 } else {
761 hash_join_tuples_left_semi(keys_a, keys_b, nulls_equal)
762 }
763 },
764 _ => {
765 let keys_a = get_arrays(&splitted_a);
766 let keys_b = get_arrays(&splitted_b);
767 if anti {
768 hash_join_tuples_left_anti(keys_a, keys_b, nulls_equal)
769 } else {
770 hash_join_tuples_left_semi(keys_a, keys_b, nulls_equal)
771 }
772 },
773 }
774}