use std::collections::HashMap;
use crate::catalog::ValType;
use crate::segment_claused::{FacetBucket, FacetCounts, ScalarClauses, ScalarHit};
use crate::value::{IndexValue, ValueTest, order_key};
use kevy_text::{SortOrder, sorted_order};
pub type ColdEntryRow = (IndexValue, Vec<u8>, Vec<Option<Vec<u8>>>);
pub fn claused_over(
items: impl Iterator<Item = ColdEntryRow>,
c: &ScalarClauses<'_>,
) -> (Vec<ScalarHit>, Vec<Vec<FacetBucket>>) {
let mut facets: Vec<FacetCounts> = vec![HashMap::new(); c.facets.len()];
let mut hits: Vec<ScalarHit> = Vec::new();
let mut groups: HashMap<Vec<u8>, usize> = HashMap::new();
let full_walk = c.sort.is_some() || !c.facets.is_empty();
for (v, k, vals) in items {
if !values_pass(&vals, c.filters) {
continue;
}
for ((f, ty), counts) in c.facets.iter().zip(facets.iter_mut()) {
let Some(raw) = vals.get(*f).and_then(Option::as_deref) else { continue };
let Some(id) = order_key(*ty, raw) else { continue };
counts.entry(id).or_insert_with(|| (raw.to_vec(), 0)).1 += 1;
}
if !full_walk && hits.len() == c.fetch {
break;
}
select_values_hit(v, k, &vals, c, &mut hits, &mut groups);
}
if let Some((_, order, _)) = c.sort {
hits.sort_by(|a, b| {
sorted_order((a.okey.as_deref(), &a.key), (b.okey.as_deref(), &b.key), order)
});
}
hits.truncate(c.fetch);
(hits, finish_facets(facets))
}
pub fn values_pass(values: &[Option<Vec<u8>>], filters: &[(usize, ValueTest)]) -> bool {
filters
.iter()
.all(|(f, t)| values.get(*f).and_then(Option::as_deref).is_some_and(|raw| t.passes(raw)))
}
fn values_clause_key(values: &[Option<Vec<u8>>], field: usize, ty: ValType) -> Option<Vec<u8>> {
values.get(field).and_then(Option::as_deref).and_then(|raw| order_key(ty, raw))
}
fn select_values_hit(
v: IndexValue,
k: Vec<u8>,
vals: &[Option<Vec<u8>>],
c: &ScalarClauses<'_>,
hits: &mut Vec<ScalarHit>,
groups: &mut HashMap<Vec<u8>, usize>,
) {
let okey = c.sort.and_then(|(f, _, ty)| values_clause_key(vals, f, ty));
let dkey = c.distinct.and_then(|(f, ty)| values_clause_key(vals, f, ty));
if let Some(id) = &dkey {
match groups.entry(id.clone()) {
std::collections::hash_map::Entry::Occupied(e) => {
let Some((_, order, _)) = c.sort else { return };
let prev = &mut hits[*e.get()];
if sorted_order((okey.as_deref(), &k), (prev.okey.as_deref(), &prev.key), order)
== std::cmp::Ordering::Less
{
*prev = ScalarHit { key: k, value: v, okey, dkey };
}
return;
}
std::collections::hash_map::Entry::Vacant(slot) => {
slot.insert(hits.len());
}
}
}
hits.push(ScalarHit { key: k, value: v, okey, dkey });
}
pub fn merge_claused<T>(
mut all: Vec<(ScalarHit, T)>,
sort: Option<SortOrder>,
offset: usize,
limit: usize,
) -> Vec<(ScalarHit, T)> {
match sort {
Some(order) => all.sort_by(|(a, _), (b, _)| {
sorted_order((a.okey.as_deref(), &a.key), (b.okey.as_deref(), &b.key), order)
}),
None => all.sort_by(|(a, _), (b, _)| (&a.value, &a.key).cmp(&(&b.value, &b.key))),
}
let mut seen: std::collections::HashSet<Vec<u8>> = std::collections::HashSet::new();
all.retain(|(h, _)| match &h.dkey {
Some(k) => seen.insert(k.clone()),
None => true,
});
if offset > 0 {
all.drain(..offset.min(all.len()));
}
all.truncate(limit);
all
}
pub fn fold_facets(into: &mut [Vec<FacetBucket>], from: Vec<Vec<FacetBucket>>) {
for (acc, part) in into.iter_mut().zip(from) {
for (id, label, n) in part {
match acc.iter_mut().find(|(k, _, _)| *k == id) {
Some(e) => e.2 += n,
None => acc.push((id, label, n)),
}
}
}
}
pub fn sort_facets(facets: &mut [Vec<FacetBucket>]) {
for f in facets.iter_mut() {
f.sort_by(|a, b| b.2.cmp(&a.2).then_with(|| a.1.cmp(&b.1)));
}
}
pub(crate) fn finish_facets(facets: Vec<FacetCounts>) -> Vec<Vec<FacetBucket>> {
facets
.into_iter()
.map(|counts| {
let mut out: Vec<FacetBucket> =
counts.into_iter().map(|(id, (label, n))| (id, label, n)).collect();
out.sort_by(|a, b| b.2.cmp(&a.2).then_with(|| a.1.cmp(&b.1)));
out
})
.collect()
}