use serde_json::Value;
use crate::schema::Schema;
use crate::dialect::filter::build_filter;
use crate::dialect::ir::{RowCol, RowShape, SqlStmt};
use crate::dialect::{field_is_bool, field_is_json, physical_of, Backend, ColumnRef};
use super::{col_fn, projection_fields, q, tname};
pub(super) fn translate_find(
backend: Backend,
schema: &Schema,
filter: &Value,
projection: Option<&Value>,
warnings: crate::dialect::filter::Warnings<'_>,
) -> Result<Vec<SqlStmt>, String> {
let mut seq = 0usize;
let wh = build_filter(filter, backend, "t", &col_fn(schema), &mut seq, warnings)?;
super::check_projection_supported(schema, projection)?;
let mut selected = projection_fields(schema, projection);
let id_excluded = projection
.and_then(|p| p.as_object())
.and_then(|o| o.get("_id"))
.map(|v| v.as_i64() == Some(0) || v.as_bool() == Some(false))
.unwrap_or(false);
if !id_excluded && !selected.iter().any(|f| f == "_id") {
selected.insert(0, "_id".to_string());
}
let mut cols_sql = Vec::new();
let mut columns = Vec::new();
for f in &selected {
if schema.compute(f).is_some() {
continue;
}
match col_fn(schema)(f) {
Some(ColumnRef::Scalar(c)) => {
cols_sql.push(format!("t.{}", backend.pcol(&c)));
if field_is_json(schema, f) {
columns.push(RowCol::json(&physical_of(&c), &[f.as_str()]));
} else {
columns.push(RowCol::scalar_bool(
&physical_of(&c),
&[f.as_str()],
field_is_bool(schema, f),
));
}
}
Some(ColumnRef::Json(c, _)) => {
cols_sql.push(format!("t.{}", backend.pcol(&c)));
columns.push(RowCol::json(&physical_of(&c), &[f.as_str()]));
}
Some(ColumnRef::JsonPath(c, path)) => {
let base = format!("t.{}", backend.pcol(&c));
let segs: Vec<&str> = path.iter().map(|s| s.as_str()).collect();
let alias = f.replace('.', "_");
let expr = backend.json_extract_scalar(&base, &segs)?;
cols_sql.push(format!("{} AS {}", expr, q(backend, &alias)));
columns.push(RowCol::scalar(&alias, &f.split('.').collect::<Vec<_>>()));
}
None => {}
}
}
cols_sql.push("t.__present AS __present".to_string());
let shape = RowShape {
columns,
present_alias: Some("__present".to_string()),
};
let select_list = if cols_sql.is_empty() {
q(backend, "_id")
} else {
cols_sql.join(", ")
};
let from = tname(backend, schema);
let where_sql = if wh.text.is_empty() {
String::new()
} else {
format!(" WHERE {}", wh.text)
};
Ok(vec![SqlStmt::select(
format!("SELECT {} FROM {} t{}", select_list, from, where_sql),
wh.params,
shape,
)])
}