mod agg;
mod chunk;
mod query;
mod ranked;
pub(crate) use chunk::{
encode_view_cursor_bytes, read_kbytes_at, read_u32_at, resp3_upgrade, value_repr_pub,
};
use kevy_resp::encode_error;
use crate::cmd_index_query::{ST_BADARGS, ST_BUILDING, ST_NOINDEX, ST_OVERBUDGET};
pub(crate) fn extension_reduce(argv: &[Vec<u8>], chunks: Vec<Vec<u8>>) -> Vec<u8> {
let verb = argv.first().map(Vec::as_slice).unwrap_or(b"");
if let Some(err) = triage_status(argv, &chunks) {
return err;
}
if verb.eq_ignore_ascii_case(b"IDX.EXPLAIN") {
return query::reduce_explain(argv, &chunks);
}
if verb.eq_ignore_ascii_case(b"IDX.COUNT") {
return query::reduce_count(&chunks);
}
if verb.eq_ignore_ascii_case(b"IDX.LIST") {
return query::reduce_list(&chunks);
}
if verb.eq_ignore_ascii_case(b"IDX.VERIFY") {
return query::reduce_verify(&chunks);
}
if argv.first().is_some_and(|v| v.eq_ignore_ascii_case(b"AGG.FETCH")) {
return agg::reduce_agg_fetch(argv, &chunks);
}
if argv.get(2).is_some_and(|a| a.eq_ignore_ascii_case(b"GROUP") || a.eq_ignore_ascii_case(b"GROUPS")) {
return agg::reduce_agg(argv, &chunks);
}
if argv.get(2).is_some_and(|a| a.eq_ignore_ascii_case(b"KNN")) {
return ranked::reduce_ranked(argv, &chunks, true);
}
if argv.first().is_some_and(|v| v.eq_ignore_ascii_case(b"IDX.REBUILD")) {
return query::reduce_rebuild(&chunks);
}
if argv.get(1).is_some_and(|a| a.eq_ignore_ascii_case(b"HYBRID")) {
return ranked::reduce_hybrid(argv, &chunks);
}
if argv.get(2).is_some_and(|a| a.eq_ignore_ascii_case(b"MATCH")) {
return ranked::reduce_ranked(argv, &chunks, false);
}
if argv.get(1).is_some_and(|a| a.eq_ignore_ascii_case(b"COMPOSE")) {
return query::reduce_compose(argv, &chunks);
}
query::reduce_query(argv, &chunks)
}
fn triage_status(argv: &[Vec<u8>], chunks: &[Vec<u8>]) -> Option<Vec<u8>> {
let verb = argv.first().map(Vec::as_slice).unwrap_or(b"");
let verb_s = String::from_utf8_lossy(verb);
let name_i = if argv.get(1).is_some_and(|a| a.eq_ignore_ascii_case(b"HYBRID")) { 2 } else { 1 };
let name_s = argv.get(name_i).map(|a| String::from_utf8_lossy(a).into_owned()).unwrap_or_default();
let mut out = Vec::new();
for c in chunks {
match c.first().copied() {
Some(ST_BADARGS) | None => {
encode_error(
&mut out,
&format!("ERR {verb_s} '{name_s}': bad arguments — run COMMAND DOCS {verb_s} for the syntax"),
);
return Some(out);
}
Some(ST_NOINDEX) => {
encode_error(
&mut out,
&format!("ERR no such index '{name_s}' (IDX.LIST enumerates them)"),
);
return Some(out);
}
Some(ST_BUILDING) => {
encode_error(
&mut out,
&format!("INDEXBUILDING index '{name_s}' is still building (poll IDX.LIST until state=ready)"),
);
return Some(out);
}
Some(ST_OVERBUDGET) => {
encode_error(
&mut out,
&format!("INDEXOVERBUDGET index '{name_s}' build exceeded MAXMEM (raise maxmemory or DROP the index)"),
);
return Some(out);
}
_ => {}
}
}
None
}