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