use crate::segment::Segment;
use crate::value::IndexValue;
use kevy_text::SortOrder;
pub use crate::view_materialized::{MaterializedSet, Membership};
pub use crate::view_mode::ViewMode;
pub use crate::view_sidecar::{MAX_VIEWS, ViewCatalog};
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Leaf {
pub index: Vec<u8>,
pub min: IndexValue,
pub max: IndexValue,
}
impl Leaf {
pub fn new(index: impl Into<Vec<u8>>, min: IndexValue, max: IndexValue) -> Leaf {
Leaf { index: index.into(), min, max }
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Tree {
Leaf(Leaf),
And(Box<Tree>, Box<Tree>),
Or(Box<Tree>, Box<Tree>),
Diff(Box<Tree>, Box<Tree>),
}
impl Tree {
pub fn eval<'a>(&self, seg: &impl Fn(&[u8]) -> Option<&'a Segment>) -> Vec<Vec<u8>> {
eval_tree(self, seg)
}
pub fn contains<'a>(&self, key: &[u8], seg: &impl Fn(&[u8]) -> Option<&'a Segment>) -> bool {
key_in_tree(self, key, seg)
}
pub fn contains_values(&self, vals: &impl Fn(&[u8]) -> Option<IndexValue>) -> bool {
key_in_tree_vals(self, vals)
}
pub fn leaves(&self) -> usize {
match self {
Tree::Leaf(_) => 1,
Tree::And(a, b) | Tree::Or(a, b) | Tree::Diff(a, b) => a.leaves() + b.leaves(),
}
}
pub fn depth(&self) -> usize {
match self {
Tree::Leaf(_) => 1,
Tree::And(a, b) | Tree::Or(a, b) | Tree::Diff(a, b) => 1 + a.depth().max(b.depth()),
}
}
pub fn each_leaf<F: FnMut(&Leaf)>(&self, f: &mut F) {
match self {
Tree::Leaf(l) => f(l),
Tree::And(a, b) | Tree::Or(a, b) | Tree::Diff(a, b) => {
a.each_leaf(f);
b.each_leaf(f);
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ViewSpec {
pub name: Vec<u8>,
pub tree: Tree,
pub order_by: Vec<u8>,
pub order: SortOrder,
pub mode: ViewMode,
pub via: Option<Vec<u8>>,
}
pub const MAX_TREE_DEPTH: usize = 3;
pub const MAX_TREE_LEAVES: usize = 4;
impl ViewSpec {
pub fn new(name: impl Into<Vec<u8>>, tree: Tree, order_by: impl Into<Vec<u8>>) -> ViewSpec {
ViewSpec {
name: name.into(),
tree,
order_by: order_by.into(),
order: SortOrder::Asc,
mode: ViewMode::Virtual,
via: None,
}
}
#[must_use]
pub fn with_order(mut self, order: SortOrder) -> ViewSpec {
self.order = order;
self
}
#[must_use]
pub fn with_mode(mut self, mode: ViewMode) -> ViewSpec {
self.mode = mode;
self
}
#[must_use]
pub fn with_via(mut self, via: Vec<u8>) -> ViewSpec {
self.via = Some(via);
self
}
pub fn validate(&self) -> Result<(), crate::ViewError> {
if self.tree.depth() > MAX_TREE_DEPTH {
return Err(crate::ViewError::TooDeep);
}
if self.tree.leaves() > MAX_TREE_LEAVES {
return Err(crate::ViewError::TooManyLeaves);
}
Ok(())
}
}
pub(crate) fn eval_tree<'a>(
tree: &Tree,
seg: &impl Fn(&[u8]) -> Option<&'a Segment>,
) -> Vec<Vec<u8>> {
match tree {
Tree::Leaf(l) => match seg(&l.index) {
Some(s) => {
let (hits, _) = s.range(&l.min, &l.max, None, usize::MAX);
hits.into_iter().map(|(k, _)| k).collect()
}
None => Vec::new(),
},
Tree::And(a, b) => {
let (xa, xb) = (a.eval(seg), b.eval(seg));
let (mut drive, probe) = if xa.len() <= xb.len() { (xa, xb) } else { (xb, xa) };
let set: std::collections::HashSet<&[u8]> = probe.iter().map(Vec::as_slice).collect();
drive.retain(|k| set.contains(k.as_slice()));
drive
}
Tree::Or(a, b) => {
let mut xa = a.eval(seg);
xa.extend(b.eval(seg));
xa.sort();
xa.dedup();
xa
}
Tree::Diff(a, b) => {
let mut xa = a.eval(seg);
let xb = b.eval(seg);
let set: std::collections::HashSet<&[u8]> = xb.iter().map(Vec::as_slice).collect();
xa.retain(|k| !set.contains(k.as_slice()));
xa
}
}
}
pub(crate) fn key_in_tree<'a>(
tree: &Tree,
key: &[u8],
seg: &impl Fn(&[u8]) -> Option<&'a Segment>,
) -> bool {
match tree {
Tree::Leaf(l) => seg(&l.index)
.and_then(|s| s.key_dir()?.get(key))
.is_some_and(|v| v >= l.min && v <= l.max),
Tree::And(a, b) => a.contains(key, seg) && b.contains(key, seg),
Tree::Or(a, b) => a.contains(key, seg) || b.contains(key, seg),
Tree::Diff(a, b) => a.contains(key, seg) && !b.contains(key, seg),
}
}
pub(crate) fn key_in_tree_vals(tree: &Tree, vals: &impl Fn(&[u8]) -> Option<IndexValue>) -> bool {
match tree {
Tree::Leaf(l) => vals(&l.index).is_some_and(|v| v >= l.min && v <= l.max),
Tree::And(a, b) => a.contains_values(vals) && b.contains_values(vals),
Tree::Or(a, b) => a.contains_values(vals) || b.contains_values(vals),
Tree::Diff(a, b) => a.contains_values(vals) && !b.contains_values(vals),
}
}
#[cfg(test)]
#[path = "view_tests.rs"]
mod tests;