use super::*;
use crate::{
context::Context,
filter::alias::{Alias, AliasMode},
model_extensions::*,
};
use quaint::ast::*;
use query_structure::*;
#[derive(Debug, Default)]
pub(crate) struct SubqueriesSelectBuilder {
alias: Alias,
}
impl JoinSelectBuilder for SubqueriesSelectBuilder {
fn build(&mut self, args: QueryArguments, selected_fields: &FieldSelection, ctx: &Context<'_>) -> Select<'static> {
let (select, alias) = self.build_default_select(&args, ctx);
self.with_selection(select, selected_fields, alias, ctx)
}
fn build_selection<'a>(
&mut self,
select: Select<'a>,
field: &SelectedField,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Select<'a> {
match field {
SelectedField::Scalar(sf) => select.column(
sf.as_column(ctx)
.table(parent_alias.to_table_string())
.set_is_selected(true),
),
SelectedField::Relation(rs) => self.with_relation(select, rs, parent_alias, ctx),
_ => select,
}
}
fn add_to_one_relation<'a>(
&mut self,
select: Select<'a>,
rs: &RelationSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Select<'a> {
let (subselect, _) = self.build_to_one_select(rs, parent_alias, |x| x, ctx);
select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
}
fn add_to_many_relation<'a>(
&mut self,
select: Select<'a>,
rs: &RelationSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Select<'a> {
let subselect = self.build_to_many_select(rs, parent_alias, ctx);
select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
}
fn add_many_to_many_relation<'a>(
&mut self,
select: Select<'a>,
rs: &RelationSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Select<'a> {
let subselect = self.build_m2m_select(rs, parent_alias, ctx);
select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
}
fn add_virtual_selection<'a>(
&mut self,
select: Select<'a>,
vs: &VirtualSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Select<'a> {
let virtual_select = self.build_virtual_select(vs, parent_alias, ctx);
let alias = relation_count_alias_name(vs.relation_field());
select.value(Expression::from(virtual_select).alias(alias))
}
fn build_json_obj_fn(
&mut self,
rs: &RelationSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Expression<'static> {
let virtuals = self.build_json_obj_virtual_selection(rs.virtuals(), parent_alias, ctx);
let build_obj_params = rs
.selections
.iter()
.filter_map(|field| match field {
SelectedField::Scalar(sf) => Some((
Cow::from(sf.db_name().to_owned()),
Expression::from(sf.as_column(ctx).table(parent_alias.to_table_string())),
)),
SelectedField::Relation(rs) => Some((
Cow::from(rs.field.name().to_owned()),
Expression::from(self.with_relation(Select::default(), rs, parent_alias, ctx)),
)),
_ => None,
})
.chain(virtuals)
.collect();
json_build_object(build_obj_params).into()
}
fn build_virtual_expr(
&mut self,
vs: &VirtualSelection,
parent_alias: Alias,
ctx: &Context<'_>,
) -> Expression<'static> {
coalesce([
Expression::from(self.build_virtual_select(vs, parent_alias, ctx)),
Expression::from(0.raw()),
])
.into()
}
fn next_alias(&mut self) -> Alias {
self.alias = self.alias.inc(AliasMode::Table);
self.alias
}
}
impl SubqueriesSelectBuilder {
fn build_m2m_select<'a>(&mut self, rs: &RelationSelection, parent_alias: Alias, ctx: &Context<'_>) -> Select<'a> {
let rf = rs.field.clone();
let m2m_table_alias = self.next_alias();
let root_alias = self.next_alias();
let outer_alias = self.next_alias();
let m2m_join_data =
rf.related_model()
.as_table(ctx)
.on(rf.m2m_join_conditions(Some(m2m_table_alias), None, ctx));
let m2m_table = rf.as_table(ctx).alias(m2m_table_alias.to_table_string());
let root = Select::from_table(m2m_table)
.inner_join(m2m_join_data)
.value(rf.related_model().as_table(ctx).asterisk())
.with_ordering(&rs.args, None, ctx) .and_where(
rf.related_field()
.m2m_join_conditions(Some(m2m_table_alias), Some(parent_alias), ctx),
) .with_filters(rs.args.filter.clone(), None, ctx) .comment("root");
let take = match rs.args.order_by.is_empty() {
true => rs.args.take_abs(),
false => rs.args.take_abs().or(Some(i64::MAX)),
};
let inner = Select::from_table(Table::from(root).alias(root_alias.to_table_string()))
.value(self.build_json_obj_fn(rs, root_alias, ctx).alias(JSON_AGG_IDENT))
.with_pagination(take, rs.args.skip)
.comment("inner"); Select::from_table(Table::from(inner).alias(outer_alias.to_table_string()))
.value(json_agg())
.comment("outer")
}
}