use rudb_common::{Error, Memory, Reservation, Result, Value};
use rudb_plan::{Plan, Slice, SortKey};
use rudb_vector::Chunk;
use crate::expr::evaluate_all;
use crate::operator::Operator;
use crate::rows;
use crate::schema::Schema;
use crate::sort::compare;
#[derive(Debug)]
pub(crate) struct TopN<'a> {
input: Box<dyn Operator + 'a>,
plan: &'a Plan,
keys: Vec<SortKey>,
schema: Schema,
count: usize,
offset: usize,
bound: usize,
built: bool,
chunks: Vec<Chunk>,
at: usize,
memory: Memory,
held: Reservation,
}
impl<'a> TopN<'a> {
pub(crate) fn new(
plan: &'a Plan,
input: Box<dyn Operator + 'a>,
keys: Slice,
count: u64,
offset: u64,
memory: &Memory,
) -> Self {
let schema = input.schema().clone();
let count = usize::try_from(count).unwrap_or(usize::MAX);
let offset = usize::try_from(offset).unwrap_or(usize::MAX);
Self {
input,
plan,
keys: plan.sort_key_list(keys).to_vec(),
schema,
count,
offset,
bound: count.saturating_add(offset),
built: false,
chunks: Vec::new(),
at: 0,
memory: memory.clone(),
held: memory.reservation(),
}
}
fn build(&mut self) -> Result<()> {
let exprs: Vec<_> = self.keys.iter().map(|key| key.expr).collect();
let mut scratch = self.memory.reservation();
let mut kept: Vec<(Vec<Value>, Vec<Value>)> = Vec::new();
let mut failure: Option<Error> = None;
let ceiling = self.bound.saturating_mul(2);
while let Some(chunk) = self.input.next()? {
let keys = evaluate_all(self.plan, &exprs, &self.schema, &chunk)?;
let mut taken = 0;
for row in 0..chunk.len() {
let key: Vec<Value> = keys.iter().map(|column| column.value_at(row)).collect();
let values: Vec<Value> = chunk.row(row).collect();
taken += rows::footprint(&key) + rows::footprint(&values);
kept.push((key, values));
}
scratch.grow(taken)?;
if kept.len() > ceiling {
trim(&self.keys, &mut kept, self.bound, &mut failure);
recharge(&kept, &mut scratch)?;
}
}
trim(&self.keys, &mut kept, self.bound, &mut failure);
if let Some(error) = failure {
return Err(error);
}
let wanted = kept.into_iter().skip(self.offset).take(self.count);
let ordered: Vec<Vec<Value>> = wanted.map(|(_, row)| row).collect();
self.chunks = rows::chunks(&self.schema.types(), &ordered, &mut self.held)?;
Ok(())
}
}
fn trim(
keys: &[SortKey],
kept: &mut Vec<(Vec<Value>, Vec<Value>)>,
bound: usize,
failure: &mut Option<Error>,
) {
kept.sort_by(|left, right| compare(keys, &left.0, &right.0, failure));
kept.truncate(bound);
}
fn recharge(kept: &[(Vec<Value>, Vec<Value>)], scratch: &mut Reservation) -> Result<()> {
let footprint =
kept.iter().map(|(key, values)| rows::footprint(key) + rows::footprint(values)).sum();
scratch.release();
scratch.grow(footprint)
}
impl Operator for TopN<'_> {
fn schema(&self) -> &Schema {
&self.schema
}
fn next(&mut self) -> Result<Option<Chunk>> {
if !self.built {
self.build()?;
self.built = true;
}
if self.at >= self.chunks.len() {
return Ok(None);
}
let chunk = self.chunks[self.at].clone();
self.at += 1;
Ok(Some(chunk))
}
}