use sqlparser::ast;
use super::super::helpers::extract_func_args;
use super::super::text_call::resolve_text_call;
use super::aliases::function_call_name;
use super::hybrid::{no_args_rrf_score_error, plan_hybrid_from_sort};
use super::text_score::{ScanSort, attach_scores};
use crate::error::Result;
use crate::functions::registry::{FunctionRegistry, SearchTrigger};
use crate::parser::normalize::normalize_ident;
use crate::planner::select::post_process::post_process;
use crate::resolver::columns::ResolvedTable;
use crate::types::{SqlPlan, TextScoreColumn};
pub(in crate::planner::select) fn try_hybrid_from_projection(
plan: &SqlPlan,
select_items: &[ast::SelectItem],
functions: &FunctionRegistry,
table: &ResolvedTable,
) -> Result<Option<SqlPlan>> {
if !matches!(plan, SqlPlan::Scan { .. } | SqlPlan::TextSearch(_)) {
return Ok(None);
}
let mut scores = Vec::new();
for item in select_items {
let (expr, alias) = match item {
ast::SelectItem::ExprWithAlias { expr, alias } => (expr, Some(normalize_ident(alias))),
ast::SelectItem::UnnamedExpr(expr) => (expr, None),
_ => continue,
};
let ast::Expr::Function(func) = expr else {
continue;
};
let name = function_call_name(expr).unwrap_or_default();
match functions.search_trigger(&name) {
SearchTrigger::HybridSearch => {
let SqlPlan::Scan { sort_keys, .. } = plan else {
continue;
};
let args = extract_func_args(func)?;
if args.is_empty() {
return Err(no_args_rrf_score_error());
}
let Some(hybrid) =
plan_hybrid_from_sort(&args, table, plan, alias.as_deref(), functions)?
else {
return Ok(None);
};
if sort_keys.is_empty() {
return Ok(Some(hybrid));
}
return post_process(hybrid, sort_keys.clone(), None, 0).map(Some);
}
SearchTrigger::TextSearch | SearchTrigger::TextMatch => {
let call = resolve_text_call(&name, func, table)?;
scores.push(TextScoreColumn {
field: call.field,
query: call.query,
mode: call.options.params.mode,
fuzzy: call.options.params.fuzzy,
alias: alias.unwrap_or_else(|| expr.to_string()),
});
}
_ => {}
}
}
if scores.is_empty() {
return Ok(None);
}
attach_scores(plan, scores, ScanSort::Keep)
}