use super::MirrorState;
use crate::column_store::TypedColumn;
use crate::filter::Condition;
use roaring::RoaringBitmap;
pub(super) struct Eval {
pub(super) bits: RoaringBitmap,
pub(super) exact: bool,
}
impl Eval {
fn exact(bits: RoaringBitmap) -> Self {
Self { bits, exact: true }
}
fn empty() -> Self {
Self::exact(RoaringBitmap::new())
}
}
enum FieldCol {
Float,
Str,
Bool,
Absent,
}
pub(super) fn condition_bitmap(state: &MirrorState, cond: &Condition) -> Option<Eval> {
match cond {
Condition::Eq { field, value } => leaf_eq(state, field, value),
Condition::Neq { field, value } => {
let eq = leaf_eq(state, field, value)?;
complement(state, &eq)
}
Condition::Gt { field, value } => leaf_ord(state, field, value, std::cmp::Ordering::is_gt),
Condition::Gte { field, value } => leaf_ord(state, field, value, std::cmp::Ordering::is_ge),
Condition::Lt { field, value } => leaf_ord(state, field, value, std::cmp::Ordering::is_lt),
Condition::Lte { field, value } => leaf_ord(state, field, value, std::cmp::Ordering::is_le),
Condition::In { field, values } => leaf_in(state, field, values),
Condition::And { conditions } => and_bitmap(state, conditions),
Condition::Or { conditions } => or_bitmap(state, conditions),
Condition::Not { condition } => {
let inner = condition_bitmap(state, condition)?;
complement(state, &inner)
}
_ => None,
}
}
fn complement(state: &MirrorState, eval: &Eval) -> Option<Eval> {
eval.exact.then(|| Eval::exact(&state.live - &eval.bits))
}
fn and_bitmap(state: &MirrorState, conditions: &[Condition]) -> Option<Eval> {
if conditions.is_empty() {
return Some(Eval::exact(state.live.clone()));
}
let mut acc: Option<RoaringBitmap> = None;
let mut exact = true;
for cond in conditions {
match condition_bitmap(state, cond) {
Some(eval) => {
exact &= eval.exact;
acc = Some(match acc {
Some(bits) => bits & eval.bits,
None => eval.bits,
});
}
None => exact = false,
}
}
acc.map(|bits| Eval { bits, exact })
}
fn or_bitmap(state: &MirrorState, conditions: &[Condition]) -> Option<Eval> {
let mut acc = RoaringBitmap::new();
let mut exact = true;
for cond in conditions {
let eval = condition_bitmap(state, cond)?;
exact &= eval.exact;
acc |= eval.bits;
}
Some(Eval { bits: acc, exact })
}
fn classify_field(state: &MirrorState, field: &str) -> Option<FieldCol> {
if field.contains('.') {
return None; }
match state.store.get_column(field) {
Some(TypedColumn::Float(_)) => Some(FieldCol::Float),
Some(TypedColumn::String(_)) => Some(FieldCol::Str),
Some(TypedColumn::Bool(_)) => Some(FieldCol::Bool),
Some(_) => None,
None if state.uncolumnized.contains(field) => None,
None => Some(FieldCol::Absent),
}
}
fn leaf_eq(state: &MirrorState, field: &str, value: &serde_json::Value) -> Option<Eval> {
let col = classify_field(state, field)?;
let bits = match (col, value) {
(FieldCol::Absent, v) if is_scalar(v) => RoaringBitmap::new(),
(FieldCol::Float, serde_json::Value::Number(n)) => {
let lit = n.as_f64()?;
state
.store
.filter_float_bitmap(field, move |v| (v - lit).abs() < f64::EPSILON)
}
(FieldCol::Str, serde_json::Value::String(s)) => {
state.store.filter_eq_string_bitmap(field, s)
}
(FieldCol::Bool, serde_json::Value::Bool(b)) => {
state.store.filter_bool_eq_bitmap(field, *b)
}
_ => return None,
};
Some(Eval::exact(bits))
}
fn leaf_ord(
state: &MirrorState,
field: &str,
value: &serde_json::Value,
holds: impl Fn(std::cmp::Ordering) -> bool,
) -> Option<Eval> {
let col = classify_field(state, field)?;
match (col, value) {
(_, v) if !v.is_number() && !v.is_string() => Some(Eval::empty()),
(FieldCol::Absent, _) => Some(Eval::empty()),
(FieldCol::Float, serde_json::Value::Number(n)) => {
let lit = n.as_f64()?;
let bits = state
.store
.filter_float_bitmap(field, move |v| v.partial_cmp(&lit).is_some_and(&holds));
Some(Eval::exact(bits))
}
_ => None,
}
}
fn leaf_in(state: &MirrorState, field: &str, values: &[serde_json::Value]) -> Option<Eval> {
let col = classify_field(state, field)?;
if values.is_empty() {
return Some(Eval::empty());
}
let bits = match col {
FieldCol::Absent => RoaringBitmap::new(),
FieldCol::Float => {
let lits: Vec<f64> = values
.iter()
.map(serde_json::Value::as_f64)
.collect::<Option<Vec<f64>>>()?;
state.store.filter_float_bitmap(field, move |v| {
lits.iter().any(|lit| (v - lit).abs() < f64::EPSILON)
})
}
FieldCol::Str => {
let strs: Vec<&str> = values
.iter()
.map(serde_json::Value::as_str)
.collect::<Option<Vec<&str>>>()?;
state.store.filter_in_string_bitmap(field, &strs)
}
FieldCol::Bool => bool_in_bitmap(state, field, values)?,
};
Some(Eval::exact(bits))
}
fn bool_in_bitmap(
state: &MirrorState,
field: &str,
values: &[serde_json::Value],
) -> Option<RoaringBitmap> {
let bools: Vec<bool> = values
.iter()
.map(serde_json::Value::as_bool)
.collect::<Option<Vec<bool>>>()?;
let mut bits = RoaringBitmap::new();
if bools.contains(&true) {
bits |= state.store.filter_bool_eq_bitmap(field, true);
}
if bools.contains(&false) {
bits |= state.store.filter_bool_eq_bitmap(field, false);
}
Some(bits)
}
fn is_scalar(value: &serde_json::Value) -> bool {
value.is_number() || value.is_string() || value.is_boolean()
}