1use std::cell::RefCell;
15use std::collections::BTreeMap;
16use std::rc::Rc;
17
18use differential_dataflow::AsCollection;
19use differential_dataflow::hashable::Hashable;
20use differential_dataflow::lattice::Lattice;
21use differential_dataflow::operators::arrange::{Arranged, TraceAgent};
22use differential_dataflow::operators::iterate::Variable as SemigroupVariable;
23use differential_dataflow::trace::cursor::{BatchCursor, BatchValOwn};
24use differential_dataflow::trace::{Builder, Cursor, Navigable, Trace};
25use differential_dataflow::{Data, VecCollection};
26use mz_compute_types::dyncfgs::{ENABLE_COMPUTE_TEMPORAL_BUCKETING, TEMPORAL_BUCKETING_SUMMARY};
27use mz_compute_types::plan::ArrangementStrategy;
28use mz_compute_types::plan::scalar::LirScalarExpr;
29use mz_compute_types::plan::top_k::{
30 BasicTopKPlan, MonotonicTop1Plan, MonotonicTopKPlan, TopKPlan,
31};
32use mz_expr::func::CastUint64ToInt64;
33use mz_expr::{BinaryFunc, Columns, Eval, EvalError, UnaryFunc, func, permutation_for_arrangement};
34use mz_ore::cast::CastFrom;
35use mz_ore::soft_assert_or_log;
36use mz_repr::fixed_length::ExtendDatums;
37use mz_repr::{Datum, DatumVec, Diff, ReprScalarType, Row, SharedRow};
38use mz_timely_util::columnation::ColumnationChunker;
39use mz_timely_util::operator::CollectionExt;
40use timely::Container;
41use timely::container::{CapacityContainerBuilder, PushInto};
42use timely::dataflow::channels::pact::Pipeline;
43use timely::dataflow::operators::Operator;
44
45use crate::extensions::arrange::{ArrangementSize, KeyCollection, MzArrange};
46use crate::extensions::reduce::{ClearContainer, MzReduce};
47use crate::render::Pairer;
48use crate::render::context::{ArrangementFlavor, CollectionBundle, Context};
49use crate::render::errors::DataflowErrorSer;
50use crate::render::errors::MaybeValidatingRow;
51use crate::typedefs::{ErrBatcher, ErrBuilder, KeyBatcher, MzTimestamp, RowRowSpine, RowSpine};
52use mz_row_spine::{
53 DatumContainer, DatumSeq, RowBatcher, RowBuilder, RowRowBatcher, RowRowBuilder, RowValBuilder,
54 RowValSpine,
55};
56
57impl<'scope, T: crate::render::RenderTimestamp + crate::render::MaybeBucketByTime>
59 Context<'scope, T>
60{
61 pub(crate) fn render_topk(
62 &self,
63 input: CollectionBundle<'scope, T>,
64 top_k_plan: TopKPlan,
65 temporal_bucketing_strategy: ArrangementStrategy,
66 ) -> CollectionBundle<'scope, T> {
67 let (ok_input, err_input) = input.as_specific_collection(None, &self.config_set);
68
69 if matches!(
87 temporal_bucketing_strategy,
88 ArrangementStrategy::TemporalBucketing
89 ) {
90 let must_consolidate = match &top_k_plan {
91 TopKPlan::MonotonicTop1(p) => p.must_consolidate,
92 TopKPlan::MonotonicTopK(p) => p.must_consolidate,
93 TopKPlan::Basic(_) => true,
94 };
95 soft_assert_or_log!(
96 must_consolidate,
97 "TopK with `TemporalBucketing` should not have `must_consolidate = false`; \
98 `RelaxMustConsolidate` only runs on single-time dataflows where \
99 `mz_now()` has been const-folded and no temporal bucketing is set",
100 );
101 }
102 let ok_input = if matches!(
103 temporal_bucketing_strategy,
104 ArrangementStrategy::TemporalBucketing
105 ) && ENABLE_COMPUTE_TEMPORAL_BUCKETING.get(&self.config_set)
106 {
107 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
108 .get(&self.config_set)
109 .try_into()
110 .expect("must fit");
111 T::maybe_apply_temporal_bucketing(ok_input.inner, self.as_of_frontier.clone(), summary)
112 } else {
113 ok_input
114 };
115
116 let outer_scope = ok_input.scope();
118 let bundle = outer_scope.clone().region_named("TopK", |inner| {
119 let ok_input = ok_input.enter_region(inner);
120 let mut err_collection = err_input.enter_region(inner);
121
122 match top_k_plan.limit().map(|l| (l.as_literal(), l)) {
130 None => {}
131 Some((Some(Ok(literal)), _))
132 if literal == Datum::Null || literal.unwrap_int64() >= 0 => {}
133 Some((_, expr)) => {
134 let expr = expr.clone();
140 let mut datum_vec = mz_repr::DatumVec::new();
141 let errors = ok_input.clone().flat_map(move |row| {
142 let temp_storage = mz_repr::RowArena::new();
143 let datums = datum_vec.borrow_with(&row);
144 match expr.eval(&datums[..], &temp_storage) {
145 Ok(l) if l != Datum::Null && l.unwrap_int64() < 0 => {
146 Some(EvalError::NegLimit.into())
147 }
148 Ok(_) => None,
149 Err(e) => Some(e.into()),
150 }
151 });
152 err_collection = err_collection.concat(errors);
153 }
154 }
155
156 let bundle = match top_k_plan {
157 TopKPlan::MonotonicTop1(MonotonicTop1Plan {
158 group_key,
159 order_key,
160 arity,
161 must_consolidate,
162 }) => {
163 let (arrangement, errs) = self.render_top1_monotonic(
164 ok_input,
165 group_key.clone(),
166 order_key,
167 arity,
168 must_consolidate,
169 );
170 err_collection = err_collection.concat(errs);
171
172 let errs: KeyCollection<_, _, _> = err_collection.clone().into();
178 let err_arrangement = errs
179 .mz_arrange::<ColumnationChunker<_>, ErrBatcher<_, _>, ErrBuilder<_, _>, _>(
180 "Arrange bundle err",
181 );
182 CollectionBundle::from_columns(
183 group_key.iter().copied(),
184 ArrangementFlavor::Local(arrangement, err_arrangement),
185 )
186 }
187 TopKPlan::MonotonicTopK(MonotonicTopKPlan {
188 order_key,
189 group_key,
190 arity,
191 mut limit,
192 must_consolidate,
193 }) => {
194 if let Some(expr) = limit.as_mut() {
196 let mut map = BTreeMap::new();
197 for (index, column) in group_key.iter().enumerate() {
198 map.insert(*column, index);
199 }
200 expr.permute_map(&map);
201 }
202
203 let mut datum_vec = mz_repr::DatumVec::new();
205 let ok_scope = ok_input.scope();
206 let collection = ok_input
207 .map(move |row| {
208 let group_row = {
209 let datums = datum_vec.borrow_with(&row);
210 SharedRow::pack(group_key.iter().map(|i| datums[*i]))
211 };
212 (group_row, row)
213 })
214 .consolidate_named_if::<KeyBatcher<_, _, _>>(
215 must_consolidate,
216 "Consolidated MonotonicTopK input",
217 );
218
219 let error_logger = self.error_logger();
221 let (collection, errs) = collection.ensure_monotonic(move |data, diff| {
222 error_logger.log(
223 "Non-monotonic input to MonotonicTopK",
224 &format!("data={data:?}, diff={diff}"),
225 );
226 let m = "tried to build monotonic top-k on non-monotonic input".into();
227 (DataflowErrorSer::from(EvalError::Internal(m)), Diff::ONE)
228 });
229 err_collection = err_collection.concat(errs);
230
231 let collection = if let Some(limit) = limit.clone() {
237 render_intra_ts_thinning(collection, order_key.clone(), limit)
238 } else {
239 collection
240 };
241
242 let pairer = Pairer::new(1);
243 let collection = collection.map(move |(group_row, row)| {
244 let hash = row.hashed();
245 let hash_key = pairer.merge(std::iter::once(Datum::from(hash)), &group_row);
246 (hash_key, row)
247 });
248
249 let delay = std::time::Duration::from_secs(10);
258 let (retractions_var, retractions) = SemigroupVariable::new(
259 ok_scope,
260 <T as crate::render::RenderTimestamp>::system_delay(
261 delay.try_into().expect("must fit"),
262 ),
263 );
264 let thinned = collection.clone().concat(retractions.negate());
265
266 let (result, errs) =
272 self.build_topk_stage(thinned, order_key, 1u64, 0, limit, arity, false);
273 let result = CollectionExt::consolidate_named::<KeyBatcher<_, _, _>>(
275 result,
276 "Monotonic TopK final consolidate",
277 );
278 retractions_var.set(collection.concat(result.clone().negate()));
279 soft_assert_or_log!(
280 errs.is_none(),
281 "requested no validation, but received error collection"
282 );
283
284 CollectionBundle::from_collections(
285 result.map(|(_key_hash, row)| row),
286 err_collection,
287 )
288 }
289 TopKPlan::Basic(BasicTopKPlan {
290 group_key,
291 order_key,
292 offset,
293 mut limit,
294 arity,
295 buckets,
296 }) => {
297 if let Some(expr) = limit.as_mut() {
299 let mut map = BTreeMap::new();
300 for (index, column) in group_key.iter().enumerate() {
301 map.insert(*column, index);
302 }
303 expr.permute_map(&map);
304 }
305
306 let (oks, errs) = self.build_topk(
307 ok_input, group_key, order_key, offset, limit, arity, buckets,
308 );
309 err_collection = err_collection.concat(errs);
310 CollectionBundle::from_collections(oks, err_collection)
311 }
312 };
313
314 bundle.leave_region(outer_scope)
316 });
317
318 bundle
319 }
320
321 fn build_topk<'s>(
323 &self,
324 collection: VecCollection<'s, T, Row, Diff>,
325 group_key: Vec<usize>,
326 order_key: Vec<mz_expr::ColumnOrder>,
327 offset: usize,
328 limit: Option<LirScalarExpr>,
329 arity: usize,
330 buckets: Vec<u64>,
331 ) -> (
332 VecCollection<'s, T, Row, Diff>,
333 VecCollection<'s, T, DataflowErrorSer, Diff>,
334 ) {
335 let pairer = Pairer::new(1);
336 let mut datum_vec = mz_repr::DatumVec::new();
337 let mut collection = collection.map({
338 move |row| {
339 let group_row = {
340 let row_hash = row.hashed();
341 let datums = datum_vec.borrow_with(&row);
342 let iterator = group_key.iter().map(|i| datums[*i]);
343 pairer.merge(std::iter::once(Datum::from(row_hash)), iterator)
344 };
345 (group_row, row)
346 }
347 });
348
349 let mut validating = true;
350 let mut err_collection: Option<VecCollection<'s, T, _, _>> = None;
351
352 if let Some(mut limit) = limit.clone() {
353 if offset > 0 {
356 let new_limit = (|| {
357 let limit = limit.as_literal_int64()?;
358 let offset = i64::try_from(offset).ok()?;
359 limit.checked_add(offset)
360 })();
361
362 if let Some(new_limit) = new_limit {
363 limit =
364 LirScalarExpr::literal_ok(Datum::Int64(new_limit), ReprScalarType::Int64);
365 } else {
366 limit = limit.call_binary(
367 LirScalarExpr::literal_ok(
368 Datum::UInt64(u64::cast_from(offset)),
369 ReprScalarType::UInt64,
370 )
371 .call_unary(UnaryFunc::CastUint64ToInt64(CastUint64ToInt64)),
372 BinaryFunc::AddInt64(func::AddInt64),
373 );
374 }
375 }
376
377 for bucket in buckets.into_iter() {
381 let (oks, errs) = self.build_topk_stage(
385 collection,
386 order_key.clone(),
387 bucket,
388 0,
389 Some(limit.clone()),
390 arity,
391 validating,
392 );
393 collection = oks;
394 if validating {
395 err_collection = errs;
396 validating = false;
397 }
398 }
399 }
400
401 let (oks, errs) = self.build_topk_stage(
405 collection, order_key, 1u64, offset, limit, arity, validating,
406 );
407 let oks =
409 CollectionExt::consolidate_named::<KeyBatcher<_, _, _>>(oks, "TopK final consolidate");
410 collection = oks;
411 if validating {
412 err_collection = errs;
413 }
414 (
415 collection.map(|(_key_hash, row)| row),
416 err_collection.expect("at least one stage validated its inputs"),
417 )
418 }
419
420 fn build_topk_stage<'s>(
453 &self,
454 collection: VecCollection<'s, T, (Row, Row), Diff>,
455 order_key: Vec<mz_expr::ColumnOrder>,
456 modulus: u64,
457 offset: usize,
458 limit: Option<LirScalarExpr>,
459 arity: usize,
460 validating: bool,
461 ) -> (
462 VecCollection<'s, T, (Row, Row), Diff>,
463 Option<VecCollection<'s, T, DataflowErrorSer, Diff>>,
464 ) {
465 let input = collection.map(move |(hash_key, row)| {
468 let mut hash_key_iter = hash_key.iter();
469 let hash = hash_key_iter.next().unwrap().unwrap_uint64() % modulus;
470 let hash_key = SharedRow::pack(std::iter::once(hash.into()).chain(hash_key_iter));
471 (hash_key, row)
472 });
473
474 let (input, oks, errs) = if validating {
476 let (input, stage) = build_topk_negated_stage::<
478 T,
479 RowValBuilder<_, _, _>,
480 RowValSpine<Result<Row, Row>, _, _>,
481 >(&input, order_key, offset, limit, arity);
482 let stage = stage.as_collection(|k, v| (k.to_row(), v.clone()));
483
484 let error_logger = self.error_logger();
486 type CB<C> = CapacityContainerBuilder<C>;
487 let (oks, errs) = stage.map_fallible::<CB<_>, CB<_>, _, _, _>(
488 "Demuxing Errors",
489 move |(hk, result)| match result {
490 Err(v) => {
491 let mut hk_iter = hk.iter();
492 let h = hk_iter.next().unwrap().unwrap_uint64();
493 let k = SharedRow::pack(hk_iter);
494 let message = "Negative multiplicities in TopK";
495 error_logger.log(message, &format!("k={k:?}, h={h}, v={v:?}"));
496 Err(EvalError::Internal(message.into()).into())
497 }
498 Ok(t) => Ok((hk, t)),
499 },
500 );
501 (input, oks, Some(errs))
502 } else {
503 let (input, stage) =
505 build_topk_negated_stage::<T, RowRowBuilder<_, _>, RowRowSpine<_, _>>(
506 &input, order_key, offset, limit, arity,
507 );
508 let stage = stage.as_collection(|k, v| (k.to_row(), v.to_row()));
510
511 (input, stage, None)
512 };
513 let input = input.as_collection(|k, v| (k.to_row(), v.to_row()));
514 (oks.concat(input), errs)
515 }
516
517 fn render_top1_monotonic<'s>(
518 &self,
519 collection: VecCollection<'s, T, Row, Diff>,
520 group_key: Vec<usize>,
521 order_key: Vec<mz_expr::ColumnOrder>,
522 arity: usize,
523 must_consolidate: bool,
524 ) -> (
525 Arranged<'s, TraceAgent<RowRowSpine<T, Diff>>>,
526 VecCollection<'s, T, DataflowErrorSer, Diff>,
527 ) {
528 let key: Vec<LirScalarExpr> = group_key
535 .iter()
536 .map(|c| LirScalarExpr::column(*c))
537 .collect();
538 let (_permutation, thinning) = permutation_for_arrangement(&key, arity);
539
540 let collection = collection
545 .map({
546 let mut datum_vec = mz_repr::DatumVec::new();
547 move |row| {
548 let group_key = {
550 let datums = datum_vec.borrow_with(&row);
551 SharedRow::pack(group_key.iter().map(|i| datums[*i]))
552 };
553 (group_key, row)
554 }
555 })
556 .consolidate_named_if::<KeyBatcher<_, _, _>>(
557 must_consolidate,
558 "Consolidated MonotonicTop1 input",
559 );
560
561 let error_logger = self.error_logger();
563 let (partial, errs) = collection.ensure_monotonic(move |data, diff| {
564 error_logger.log(
565 "Non-monotonic input to MonotonicTop1",
566 &format!("data={data:?}, diff={diff}"),
567 );
568 let m = "tried to build monotonic top-1 on non-monotonic input".into();
569 (EvalError::Internal(m).into(), Diff::ONE)
570 });
571 let partial: KeyCollection<_, _, _> = partial
572 .explode_one(move |(group_key, row)| {
573 (
574 group_key,
575 monoids::Top1Monoid {
576 row,
577 order_key: order_key.clone(),
578 },
579 )
580 })
581 .into();
582 let result = partial
583 .mz_arrange::<
584 ColumnationChunker<_>,
585 RowBatcher<_, _>,
586 RowBuilder<_, _>,
587 RowSpine<_, _>,
588 >(
589 "Arranged MonotonicTop1 partial [val: empty]",
590 )
591 .mz_reduce_abelian::<_, RowRowBuilder<_, _>, RowRowSpine<_, _>, _>(
592 "MonotonicTop1",
593 {
594 let mut datum_vec = mz_repr::DatumVec::new();
595 move |_key, input, output| {
596 let accum: &monoids::Top1Monoid = &input[0].1;
597 let datums = datum_vec.borrow_with(&accum.row);
598 let value = SharedRow::pack(thinning.iter().map(|i| datums[*i]));
599 output.push((value, Diff::ONE));
600 }
601 },
602 );
603 (result, errs)
604 }
605}
606
607fn build_topk_negated_stage<'s, T, Bu, Tr>(
615 input: &VecCollection<'s, T, (Row, Row), Diff>,
616 order_key: Vec<mz_expr::ColumnOrder>,
617 offset: usize,
618 limit: Option<LirScalarExpr>,
619 arity: usize,
620) -> (
621 Arranged<'s, TraceAgent<RowRowSpine<T, Diff>>>,
622 Arranged<'s, TraceAgent<Tr>>,
623)
624where
625 T: MzTimestamp,
626 Bu: Builder<
627 Time = T,
628 Input: Container + ClearContainer + PushInto<((Row, BatchValOwn<Tr>), T, Diff)>,
629 Output = Tr::Batch,
630 > + 'static,
631 Tr: Trace<Batch: Navigable, Time = T> + 'static,
632 for<'a> BatchCursor<Tr>: Cursor<
633 Key<'a> = DatumSeq<'a>,
634 KeyContainer = DatumContainer,
635 ValOwn: Data + MaybeValidatingRow<Row, Row>,
636 Time = T,
637 Diff = Diff,
638 >,
639 Arranged<'s, TraceAgent<Tr>>: ArrangementSize,
640{
641 let mut datum_vec = mz_repr::DatumVec::new();
642
643 let arranged = input
648 .clone()
649 .mz_arrange::<
650 ColumnationChunker<_>,
651 RowRowBatcher<_, _>,
652 RowRowBuilder<_, _>,
653 RowRowSpine<_, _>,
654 >(
655 "Arranged TopK input",
656 );
657
658 let limit = limit.map(|l| match l.as_literal() {
660 Some(Ok(Datum::Null)) => Ok(Diff::MAX),
661 Some(Ok(d)) => Ok(Diff::from(d.unwrap_int64())),
662 _ => Err(l),
663 });
664
665 let reduced = arranged
666 .clone()
667 .mz_reduce_abelian::<_, Bu, Tr, _>("Reduced TopK input", {
668 move |hash_key, source, target: &mut Vec<(BatchValOwn<Tr>, Diff)>| {
669 let limit = match &limit {
671 Some(Ok(lit)) => Some(*lit),
672 Some(Err(expr)) => {
673 let temp_storage = mz_repr::RowArena::new();
676 let mut key_datums = datum_vec.borrow();
677 hash_key.extend_datums(&temp_storage, &mut key_datums, None);
678 let datum_limit = expr
680 .eval(&key_datums[1..], &temp_storage)
681 .unwrap_or(Datum::Int64(0));
682 Some(match datum_limit {
683 Datum::Null => Diff::MAX,
684 d => Diff::from(d.unwrap_int64()),
685 })
686 }
687 None => None,
688 };
689
690 if let Some(err) = BatchValOwn::<Tr>::into_error() {
691 for (datums, diff) in source.iter() {
692 if diff.is_positive() {
693 continue;
694 }
695 target.push((err((*datums).to_row()), Diff::ONE));
696 return;
697 }
698 }
699
700 let must_shrink = offset > 0
702 || limit
703 .map(|l| source.iter().map(|(_, d)| *d).sum::<Diff>() > l)
704 .unwrap_or(false);
705 if !must_shrink {
706 return;
707 }
708
709 target.reserve(source.len());
713 for (datums, diff) in source.iter() {
714 target.push((BatchValOwn::<Tr>::ok((*datums).to_row()), -diff));
715 }
716 let mut offset = offset;
718 let mut limit = limit;
719
720 let mut indexes = (0..source.len()).collect::<Vec<_>>();
722 let temp_storage = mz_repr::RowArena::new();
725 let mut buffer = datum_vec.borrow();
726 for (index, (datums, _)) in source.iter().enumerate() {
727 datums.extend_datums(&temp_storage, &mut buffer, None);
728 assert_eq!(buffer.len(), arity * (index + 1));
729 }
730 let width = buffer.len() / source.len();
731
732 indexes.sort_by(|left, right| {
734 let left = &buffer[left * width..][..width];
735 let right = &buffer[right * width..][..width];
736 mz_expr::compare_columns(&order_key, left, right, || left.cmp(right))
739 });
740
741 for index in indexes.into_iter() {
744 let (datums, mut diff) = source[index];
745 if !diff.is_positive() {
746 continue;
747 }
748 if offset > 0 {
750 let to_skip =
751 std::cmp::min(offset, usize::try_from(diff.into_inner()).unwrap());
752 offset -= to_skip;
753 diff -= Diff::try_from(to_skip).unwrap();
754 }
755 if let Some(limit) = &mut limit {
757 diff = std::cmp::min(diff, Diff::from(*limit));
758 *limit -= diff;
759 }
760 if diff.is_positive() {
762 target.push((BatchValOwn::<Tr>::ok(datums.to_row()), diff));
765 }
766 }
767 }
768 });
769 (arranged, reduced)
770}
771
772fn render_intra_ts_thinning<'s, T>(
773 collection: VecCollection<'s, T, (Row, Row), Diff>,
774 order_key: Vec<mz_expr::ColumnOrder>,
775 limit: LirScalarExpr,
776) -> VecCollection<'s, T, (Row, Row), Diff>
777where
778 T: timely::progress::Timestamp + Lattice,
779{
780 let mut datum_vec = mz_repr::DatumVec::new();
781
782 let mut aggregates = BTreeMap::new();
783 let shared = Rc::new(RefCell::new(monoids::Top1MonoidShared {
784 order_key,
785 left: DatumVec::new(),
786 right: DatumVec::new(),
787 }));
788 collection
789 .inner
790 .unary_notify(
791 Pipeline,
792 "TopKIntraTimeThinning",
793 [],
794 move |input, output, notificator| {
795 input.for_each_time(|time, data| {
796 let agg_time = aggregates
797 .entry(time.time().clone())
798 .or_insert_with(BTreeMap::new);
799 for ((grp_row, row), record_time, diff) in data.flat_map(|data| data.drain(..))
800 {
801 let monoid = monoids::Top1MonoidLocal {
802 row,
803 shared: Rc::clone(&shared),
804 };
805
806 let limit = if let Some(l) = limit.as_literal_int64() {
808 l
809 } else {
810 let temp_storage = mz_repr::RowArena::new();
811 let key_datums = datum_vec.borrow_with(&grp_row);
812 let datum_limit = limit
815 .eval(&key_datums, &temp_storage)
816 .unwrap_or(mz_repr::Datum::Int64(0));
817 if datum_limit == Datum::Null {
818 i64::MAX
819 } else {
820 datum_limit.unwrap_int64()
821 }
822 };
823
824 let topk = agg_time
825 .entry((grp_row, record_time))
826 .or_insert_with(move || topk_agg::TopKBatch::new(limit));
827 topk.update(monoid, diff.into_inner());
828 }
829 notificator.notify_at(time.retain(0));
830 });
831
832 notificator.for_each(|time, _, _| {
833 if let Some(aggs) = aggregates.remove(time.time()) {
834 let mut session = output.session(&time);
835 for ((grp_row, record_time), topk) in aggs {
836 session.give_iterator(topk.into_iter().map(|(monoid, diff)| {
837 (
838 (grp_row.clone(), monoid.into_row()),
839 record_time.clone(),
840 diff.into(),
841 )
842 }))
843 }
844 }
845 });
846 },
847 )
848 .as_collection()
849}
850
851pub mod topk_agg {
853 use differential_dataflow::consolidation;
854 use smallvec::SmallVec;
855
856 pub struct TopKBatch<T> {
863 updates: SmallVec<[(T, i64); 16]>,
864 clean: usize,
865 limit: i64,
866 }
867
868 impl<T: Ord> TopKBatch<T> {
869 pub fn new(limit: i64) -> Self {
870 Self {
871 updates: SmallVec::new(),
872 clean: 0,
873 limit,
874 }
875 }
876
877 #[inline]
884 pub fn update(&mut self, item: T, value: i64) {
885 self.updates.push((item, value));
886 self.maintain_bounds();
887 }
888
889 #[inline]
895 pub fn compact(&mut self) {
896 if self.clean < self.updates.len() && self.updates.len() > 1 {
897 let len = consolidation::consolidate_slice(&mut self.updates);
898 self.updates.truncate(len);
899
900 let mut limit = self.limit;
902 self.updates.retain(|x| {
903 if limit > 0 {
904 limit -= x.1;
905 true
906 } else {
907 false
908 }
909 });
910 if limit < 0 {
918 if let Some(item) = self.updates.last_mut() {
919 item.1 -= -limit;
922 }
923 }
924 }
925 self.clean = self.updates.len();
926 }
927
928 fn maintain_bounds(&mut self) {
931 if self.updates.len() > 32 && self.updates.len() >> 1 >= self.clean {
933 self.compact()
934 }
935 }
936 }
937
938 impl<T: Ord> IntoIterator for TopKBatch<T> {
939 type Item = (T, i64);
940 type IntoIter = smallvec::IntoIter<[(T, i64); 16]>;
941
942 fn into_iter(mut self) -> Self::IntoIter {
943 self.compact();
944 self.updates.into_iter()
945 }
946 }
947}
948
949pub mod monoids {
951 use std::cell::RefCell;
952 use std::cmp::Ordering;
953 use std::hash::{Hash, Hasher};
954 use std::rc::Rc;
955
956 use columnation::{Columnation, Region};
957 use differential_dataflow::difference::{IsZero, Multiply, Semigroup};
958 use mz_expr::ColumnOrder;
959 use mz_repr::{DatumVec, Diff, Row};
960 use serde::{Deserialize, Serialize};
961
962 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Hash, Default)]
964 pub struct Top1Monoid {
965 pub row: Row,
966 pub order_key: Vec<ColumnOrder>,
967 }
968
969 impl Clone for Top1Monoid {
970 #[inline]
971 fn clone(&self) -> Self {
972 Self {
973 row: self.row.clone(),
974 order_key: self.order_key.clone(),
975 }
976 }
977
978 #[inline]
979 fn clone_from(&mut self, source: &Self) {
980 self.row.clone_from(&source.row);
981 self.order_key.clone_from(&source.order_key);
982 }
983 }
984
985 impl Multiply<Diff> for Top1Monoid {
986 type Output = Self;
987
988 fn multiply(self, factor: &Diff) -> Self {
989 assert!(factor.is_positive());
994 self
995 }
996 }
997
998 impl Ord for Top1Monoid {
999 fn cmp(&self, other: &Self) -> Ordering {
1000 debug_assert_eq!(self.order_key, other.order_key);
1001
1002 let left: Vec<_> = self.row.unpack();
1005 let right: Vec<_> = other.row.unpack();
1006 mz_expr::compare_columns(&self.order_key, &left, &right, || left.cmp(&right))
1007 }
1008 }
1009 impl PartialOrd for Top1Monoid {
1010 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1011 Some(self.cmp(other))
1012 }
1013 }
1014
1015 impl Semigroup for Top1Monoid {
1016 fn plus_equals(&mut self, rhs: &Self) {
1017 let cmp = (*self).cmp(rhs);
1018 if cmp == Ordering::Greater {
1020 self.clone_from(rhs);
1021 }
1022 }
1023 }
1024
1025 impl IsZero for Top1Monoid {
1026 fn is_zero(&self) -> bool {
1027 false
1028 }
1029 }
1030
1031 impl Columnation for Top1Monoid {
1032 type InnerRegion = Top1MonoidRegion;
1033 }
1034
1035 #[derive(Default)]
1036 pub struct Top1MonoidRegion {
1037 row_region: <Row as Columnation>::InnerRegion,
1038 order_key_region: <Vec<ColumnOrder> as Columnation>::InnerRegion,
1039 }
1040
1041 impl Region for Top1MonoidRegion {
1042 type Item = Top1Monoid;
1043
1044 unsafe fn copy(&mut self, item: &Self::Item) -> Self::Item {
1045 let row = unsafe { self.row_region.copy(&item.row) };
1046 let order_key = unsafe { self.order_key_region.copy(&item.order_key) };
1047 Self::Item { row, order_key }
1048 }
1049
1050 fn clear(&mut self) {
1051 self.row_region.clear();
1052 self.order_key_region.clear();
1053 }
1054
1055 fn reserve_items<'a, I>(&mut self, items1: I)
1056 where
1057 Self: 'a,
1058 I: Iterator<Item = &'a Self::Item> + Clone,
1059 {
1060 let items2 = items1.clone();
1061 self.row_region
1062 .reserve_items(items1.into_iter().map(|s| &s.row));
1063 self.order_key_region
1064 .reserve_items(items2.into_iter().map(|s| &s.order_key));
1065 }
1066
1067 fn reserve_regions<'a, I>(&mut self, regions1: I)
1068 where
1069 Self: 'a,
1070 I: Iterator<Item = &'a Self> + Clone,
1071 {
1072 let regions2 = regions1.clone();
1073 self.row_region
1074 .reserve_regions(regions1.into_iter().map(|s| &s.row_region));
1075 self.order_key_region
1076 .reserve_regions(regions2.into_iter().map(|s| &s.order_key_region));
1077 }
1078
1079 fn heap_size(&self, mut callback: impl FnMut(usize, usize)) {
1080 self.row_region.heap_size(&mut callback);
1081 self.order_key_region.heap_size(callback);
1082 }
1083 }
1084
1085 #[derive(Debug)]
1087 pub struct Top1MonoidShared {
1088 pub order_key: Vec<ColumnOrder>,
1089 pub left: DatumVec,
1090 pub right: DatumVec,
1091 }
1092
1093 #[derive(Debug, Clone)]
1096 pub struct Top1MonoidLocal {
1097 pub row: Row,
1098 pub shared: Rc<RefCell<Top1MonoidShared>>,
1099 }
1100
1101 impl Top1MonoidLocal {
1102 pub fn into_row(self) -> Row {
1103 self.row
1104 }
1105 }
1106
1107 impl PartialEq for Top1MonoidLocal {
1108 fn eq(&self, other: &Self) -> bool {
1109 self.row.eq(&other.row)
1110 }
1111 }
1112
1113 impl Eq for Top1MonoidLocal {}
1114
1115 impl Hash for Top1MonoidLocal {
1116 fn hash<H: Hasher>(&self, state: &mut H) {
1117 self.row.hash(state);
1118 }
1119 }
1120
1121 impl Ord for Top1MonoidLocal {
1122 fn cmp(&self, other: &Self) -> Ordering {
1123 debug_assert!(Rc::ptr_eq(&self.shared, &other.shared));
1124 let Top1MonoidShared {
1125 left,
1126 right,
1127 order_key,
1128 } = &mut *self.shared.borrow_mut();
1129
1130 let left = left.borrow_with(&self.row);
1131 let right = right.borrow_with(&other.row);
1132 mz_expr::compare_columns(order_key, &left, &right, || left.cmp(&right))
1133 }
1134 }
1135
1136 impl PartialOrd for Top1MonoidLocal {
1137 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1138 Some(self.cmp(other))
1139 }
1140 }
1141
1142 impl Semigroup for Top1MonoidLocal {
1143 fn plus_equals(&mut self, rhs: &Self) {
1144 let cmp = (*self).cmp(rhs);
1145 if cmp == Ordering::Greater {
1147 self.clone_from(rhs);
1148 }
1149 }
1150 }
1151
1152 impl IsZero for Top1MonoidLocal {
1153 fn is_zero(&self) -> bool {
1154 false
1155 }
1156 }
1157}