use std::cmp::Ordering;
use super::ids::Ids;
use super::name::Name;
use super::ordered::Ordered;
use super::table::{Field, Table};
use super::values::Values;
use super::{Data, column::Column};
use crate::expr::Expr;
use crate::expr::cpu::Rows;
use crate::{Error, Result};
#[derive(Clone, PartialEq, Debug, serde::Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(description = "The rows of a table to read back, run through the expressions marks draw with.")]
pub struct Query {
of: Name,
#[serde(default)]
ids: Option<Ids>,
#[serde(default)]
filter: Option<Expr>,
#[serde(default)]
sort: Vec<(Expr, Order)>,
#[serde(default)]
offset: usize,
limit: Option<usize>,
#[serde(default)]
select: Ordered<Expr>,
at: Option<f64>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, serde::Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum Order {
#[default]
Ascending,
Descending,
}
impl Query {
pub fn of(table: impl Into<Name>) -> Query {
Query {
of: table.into(),
ids: None,
filter: None,
sort: Vec::new(),
offset: 0,
limit: None,
select: Ordered::default(),
at: None,
}
}
#[must_use]
pub fn ids(mut self, ids: impl Into<Ids>) -> Query {
self.ids = Some(ids.into());
self
}
#[must_use]
pub fn filter(mut self, keep: impl Into<Expr>) -> Query {
self.filter = Some(keep.into());
self
}
#[must_use]
pub fn sort(mut self, key: impl Into<Expr>, order: Order) -> Query {
self.sort.push((key.into(), order));
self
}
#[must_use]
pub fn offset(mut self, offset: usize) -> Query {
self.offset = offset;
self
}
#[must_use]
pub fn limit(mut self, limit: usize) -> Query {
self.limit = Some(limit);
self
}
#[must_use]
pub fn select(mut self, name: impl Into<Name>, value: impl Into<Expr>) -> Query {
self.select.0.push((name.into(), value.into()));
self
}
#[must_use]
pub fn at(mut self, at: f64) -> Query {
self.at = Some(at);
self
}
}
impl Data {
pub fn query(&self, query: &Query) -> Result<(Table, usize)> {
let table = self.table(&query.of).ok_or_else(|| Error::invalid("no such table").at(query.of.as_str()))?;
let at = query.at.unwrap_or(f64::INFINITY);
let all = std::slice::from_ref(self);
let rows = Rows::new(table, all).at(at);
let named: Vec<usize> = match &query.ids {
Some(ids) => (0..ids.len()).filter_map(|k| table.row(ids.key(k)?)).collect(),
None => (0..table.len()).collect(),
};
let mut picked: Vec<usize> = match &query.filter {
Some(keep) => {
let kept = rows.numbers(keep, |x| x > 0.5);
named.into_iter().filter(|&r| kept[r]).collect()
}
None => named,
};
let total = picked.len();
let end = query.limit.map_or(picked.len(), |limit| query.offset.saturating_add(limit).min(picked.len()));
if !query.sort.is_empty() {
let keys: Vec<(Vec<f64>, Order)> =
query.sort.iter().map(|(key, order)| (rows.numbers(key, |x| x), *order)).collect();
let compare = |a: &usize, b: &usize| {
keys.iter()
.map(|(values, order)| compare(values[*a], values[*b], *order))
.find(|o| o.is_ne())
.unwrap_or(Ordering::Equal)
.then(a.cmp(b))
};
if end < picked.len() {
picked.select_nth_unstable_by(end, compare);
picked.truncate(end);
}
picked.sort_unstable_by(compare);
}
let page = &picked[query.offset.min(end)..end];
let version = super::name::Version::next();
let mut out = Table {
name: table.name.clone(),
ids: table.ids.gather(page),
index: std::sync::Arc::default(),
fields: Vec::new(),
structure: version,
order: version,
};
if query.select.0.is_empty() {
for (name, field) in &table.fields {
out.insert(name.clone(), plain(field, at, page));
}
return Ok((out, total));
}
for (name, value) in &query.select.0 {
let field = if let Some(f) = value.field_name() {
let Some(field) = table.field_named(f) else {
return Err(Error::invalid(format!("no field {f}")).at(name.as_str()).at("select"));
};
plain(field, at, page)
} else {
let numbers = rows.numbers(value, |x| x.is_finite().then_some(x));
let values = Values::from(page.iter().map(|&r| numbers[r]).collect::<Vec<_>>());
Field::Values(Column::new(&values), super::name::Version::next())
};
out.insert(name.clone(), field);
}
Ok((out, total))
}
}
fn compare(a: f64, b: f64, order: Order) -> Ordering {
match (a.is_finite(), b.is_finite()) {
(true, true) if order == Order::Descending => b.total_cmp(&a),
(true, true) => a.total_cmp(&b),
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => Ordering::Equal,
}
}
fn plain(field: &Field, at: f64, rows: &[usize]) -> Field {
match field {
Field::Sampled(samples, _) => {
let frame = samples.frame_at(at);
let values: Vec<Option<f64>> = rows.iter().map(|&row| frame.and_then(|f| samples.value(row, f))).collect();
Field::Values(Column::new(&Values::from(values)), super::name::Version::next())
}
other => other.gather(rows),
}
}