use kevy_index::{IndexSpec, IndexValue, Segment};
use kevy_store::Store;
use super::{BuildState, ShardIndex};
pub(super) fn apply_scalar_row(store: &mut Store, spec: &IndexSpec, seg: &mut Segment, key: &[u8]) {
let names = spec.scalar_read_names();
let w = spec.primary_width();
match store.peek_hash_fields(key, &names) {
Ok(None) | Err(_) => seg.remove(key),
Ok(Some(vals)) => {
let primary = spec.derive_scalar(&vals[..w]);
match primary {
None => seg.apply_with_values(key, None, &[]),
Some(v) if spec.values.is_empty() => seg.apply(key, Some(v)),
Some(v) => {
let refs: Vec<Option<&[u8]>> = vals[w..].iter().map(|o| o.as_deref()).collect();
seg.apply_with_values(key, Some(v), &refs);
}
}
}
}
}
pub(super) fn apply_row(store: &mut Store, si: &mut ShardIndex, key: &[u8]) {
if let Some(a) = &mut si.agg {
apply_row_agg(store, &si.spec, a, key);
return;
}
if let Some(g) = &mut si.ann {
let v = match store.peek_hash_fields(key, &[si.spec.field()]) {
Ok(Some(mut vals)) => {
vals[0].take().and_then(|raw| kevy_vector::parse_vector(&raw, g.dim()))
}
_ => None,
};
g.apply(key, v);
return;
}
if let Some(ts) = &mut si.text {
apply_row_text(store, &si.spec, ts, key);
if let Some(cold) = &mut si.cold_text {
cold.on_row_write(key);
}
return;
}
apply_scalar_row(store, &si.spec, &mut si.seg, key);
if let Some(win) = &mut si.window {
win.on_row_write(key);
}
}
fn apply_row_text(
store: &mut Store,
spec: &IndexSpec,
ts: &mut kevy_text::TextSegment,
key: &[u8],
) {
let names: Vec<&[u8]> = spec
.fields
.iter()
.map(|f| f.name.as_slice())
.chain(spec.values.iter().map(|v| v.name.as_slice()))
.collect();
let fetched = store.peek_hash_fields(key, &names).ok().flatten();
let (fields, values) = spec.read_row(|f| {
let vals = fetched.as_ref()?;
names.iter().position(|n| *n == f).and_then(|i| vals[i].clone())
});
let vals: Vec<Option<&[u8]>> = values.iter().map(|v| v.as_deref()).collect();
if fields.is_empty() {
ts.apply_doc(key, None, &vals);
} else {
ts.apply_doc(key, Some(&fields), &vals);
}
}
fn apply_row_agg(store: &mut Store, spec: &IndexSpec, a: &mut kevy_index::AggSegment, key: &[u8]) {
let group_field = spec.group_by.as_deref().unwrap_or_default();
match store.peek_hash_fields(key, &[group_field, spec.field()]) {
Ok(Some(mut vals)) => {
let group = vals[0].take();
let val = vals[1].take().and_then(|raw| kevy_index::IndexValue::coerce(spec.ty, &raw));
match (group, val) {
(Some(g), Some(v)) => a.apply(key, Some((g, v)), false),
_ => a.apply(key, None, true),
}
}
Ok(None) => a.apply(key, None, false),
Err(_) => a.apply(key, None, true),
}
}
pub(crate) enum RowValue {
Value(IndexValue),
CoerceFailed,
Gone,
}
pub(crate) fn row_value(store: &mut Store, spec: &IndexSpec, key: &[u8]) -> RowValue {
if spec.composite.is_some() {
let names = spec.scalar_read_names();
return match store.peek_hash_fields(key, &names[..spec.primary_width()]) {
Ok(None) | Err(_) => RowValue::Gone,
Ok(Some(vals)) => match spec.derive_scalar(&vals) {
Some(v) => RowValue::Value(v),
None => RowValue::CoerceFailed,
},
};
}
match store.hget(key, spec.field()) {
Ok(Some(raw)) => {
let raw = raw.to_vec();
match IndexValue::coerce(spec.ty, &raw) {
Some(v) => RowValue::Value(v),
None => RowValue::CoerceFailed,
}
}
Ok(None) => {
if store.exists(&[key]) == 0 {
RowValue::Gone
} else {
RowValue::CoerceFailed
}
}
Err(_) => RowValue::Gone, }
}
pub(super) fn advance_backfill(store: &mut Store, si: &mut ShardIndex, batch: usize) {
let BuildState::Backfilling { keys, pos } = &mut si.build else {
return;
};
let end = (*pos + batch).min(keys.len());
let slice: Vec<Vec<u8>> = keys[*pos..end].to_vec();
*pos = end;
let done = *pos >= keys.len();
for key in &slice {
let already = match (&si.text, &si.ann, &si.agg) {
(Some(ts), _, _) => ts.contains(key),
(_, Some(g), _) => g.contains(key),
(_, _, Some(a)) => a.contains(key),
_ => si.seg.verify_entry(key).is_some(),
};
if !already {
apply_row_backfill(store, si, key);
}
}
if (si.spec.max_bytes > 0 && si.seg.stats().approx_bytes > si.spec.max_bytes)
|| store.tier_index_floor_blocked(0)
{
si.seg = Segment::new();
si.build = BuildState::FailedOverBudget;
return;
}
if done {
si.build = BuildState::Ready;
}
}
fn apply_row_backfill(store: &mut Store, si: &mut ShardIndex, key: &[u8]) {
if si.text.is_some() || si.ann.is_some() || si.agg.is_some() {
apply_row(store, si, key);
return;
}
apply_scalar_row(store, &si.spec, &mut si.seg, key);
if let Some(win) = &mut si.window {
win.on_row_write(key);
}
}