use kevy_index::{IndexSpec, IndexValue};
use kevy_store::{RowChange, RowChanges, RowWatch, Store};
use crate::ops_index::ShardSegs;
pub(crate) type Rules = Vec<(Vec<u8>, Vec<Vec<u8>>)>;
#[derive(Debug, Default)]
pub(crate) struct Drain {
spare: RowChanges,
installed: Rules,
slots: Vec<Option<(usize, Vec<usize>)>>,
olds: Vec<Option<IndexValue>>,
version: Option<u64>,
}
const MAX_READ: usize = 32;
fn wanted(ss: &ShardSegs) -> Rules {
let mut rules: Rules = Vec::new();
let mut add = |spec: &IndexSpec, scalar: bool| {
let p = spec.prefix();
let at = match rules.iter().position(|(q, _)| q.as_slice() == p) {
Some(i) => i,
None => {
rules.push((p.to_vec(), Vec::new()));
rules.len() - 1
}
};
if scalar {
for n in spec.scalar_read_names() {
if !rules[at].1.iter().any(|f| f.as_slice() == n) {
rules[at].1.push(n.to_vec());
}
}
}
};
ss.segs.iter().for_each(|(s, _)| add(s, true));
#[cfg(feature = "text")]
ss.text.iter().for_each(|(s, _)| add(s, false));
#[cfg(feature = "vector")]
ss.ann.iter().for_each(|(s, _)| add(s, false));
ss.agg.iter().for_each(|(s, _)| add(s, false));
rules.sort();
rules
}
fn locate(rules: &Rules, spec: &IndexSpec) -> Option<(usize, Vec<usize>)> {
let rule = rules.iter().position(|(p, _)| p.as_slice() == spec.prefix())?;
let fields = &rules[rule].1;
let names = spec.scalar_read_names();
let at = names.iter().map(|n| fields.iter().position(|f| f.as_slice() == *n));
Some((rule, at.collect::<Option<Vec<usize>>>()?))
}
fn slots_for(ss: &ShardSegs) -> Vec<Option<(usize, Vec<usize>)>> {
ss.segs.iter().map(|(s, _)| locate(&ss.drain.installed, s)).collect()
}
fn old_value(
spec: &IndexSpec,
slots: Option<&(usize, Vec<usize>)>,
c: &RowChange<'_>,
) -> Option<IndexValue> {
let (rule, fields) = slots?;
let w = spec.primary_width();
let mut prim: [Option<&[u8]>; MAX_READ] = [None; MAX_READ];
for (slot, &f) in prim.iter_mut().zip(&fields[..w]) {
*slot = c.field(*rule, f);
}
spec.derive_scalar_refs(&prim[..w])
}
pub(crate) fn drain(ss: &mut ShardSegs, store: &mut Store) -> bool {
let mut applied = false;
if store.has_row_changes() {
let changes = store.take_row_changes(std::mem::take(&mut ss.drain.spare));
if changes.is_reset() {
crate::ops_index_sync::reset_all_segs(ss);
}
if ss.drain.version != Some(ss.version) {
ss.drain.slots = slots_for(ss);
}
let mut olds = std::mem::take(&mut ss.drain.olds);
for c in changes.iter() {
olds.clear();
let slots = &ss.drain.slots;
olds.extend(
ss.segs.iter().zip(slots).map(|((s, _), at)| old_value(s, at.as_ref(), &c)),
);
crate::ops_index_sync::apply_one_key(ss, store, c.key(), &olds);
}
ss.drain.olds = olds;
ss.drain.spare = changes;
applied = true;
}
if ss.drain.version != Some(ss.version) {
let want = wanted(ss);
if want != ss.drain.installed {
let w =
want.iter().fold(RowWatch::new(), |w, (p, f)| w.with_prefix(p.clone(), f.clone()));
store.set_row_watch(w);
ss.drain.installed = want;
}
ss.drain.slots = slots_for(ss);
ss.drain.version = Some(ss.version);
}
applied
}