use fraiseql_core::{
db::{RelevanceOrder, to_snake_case},
runtime::can_reference_field,
schema::{SecurityConfig, TypeDefinition},
security::SecurityContext,
};
use serde_json::json;
pub(super) struct SearchPlan {
pub where_clause: serde_json::Value,
pub relevance: RelevanceOrder,
}
pub(super) fn plan_search(
query: &str,
type_def: Option<&TypeDefinition>,
security: Option<&SecurityConfig>,
security_context: Option<&SecurityContext>,
) -> Option<SearchPlan> {
let td = type_def?;
let fields: Vec<_> = td
.searchable_fields()
.into_iter()
.filter(|f| can_reference_field(security, f, security_context))
.collect();
if fields.is_empty() {
return None;
}
let clauses: Vec<serde_json::Value> = fields
.iter()
.map(|f| json!({ f.name.as_str(): { "websearch_query": query } }))
.collect();
let where_clause = if clauses.len() == 1 {
clauses.into_iter().next().expect("len checked above")
} else {
json!({ "_or": clauses })
};
Some(SearchPlan {
where_clause,
relevance: RelevanceOrder {
fields: fields.iter().map(|f| to_snake_case(f.name.as_str())).collect(),
query: query.to_string(),
},
})
}
#[cfg(test)]
mod tests;