mem-query 0.0.1

Relational algebra interface for Rust collections
use crate::{
    col::Col,
    query::{
        request::{QueryRequest,NewRequest},
        filter::{FilterForHeader},
        {QueryPlanImpl, QueryPlan},
    },
    record::{Record, ExternalRecord},
    header::{HasCol, ProjectFrom, Header},
    relation::{Relation,RelationImpl,Queryable,QueryOutput},
    query::{fallback::FallbackPlanner},
};
use tylisp::{
    sexpr, defun_nocalc, defun, calc,
    typenum as tn,
    ops::{
        Ret, If,
        list::{EmptyP},
    },
};
use std::{
    marker::PhantomData,
};

pub struct SubordinateJoin<Parent,Field>(Parent, PhantomData<Field>);

impl<P,F> SubordinateJoin<P,F> {
    pub(super) fn new(parent:P) -> Self where Self: RelationImpl {
        SubordinateJoin(parent, PhantomData)
    }
}

impl<Parent, Field, Child> RelationImpl for SubordinateJoin<Parent,Field> where
    Parent: RelationImpl,
    Parent::Cols: HasCol<Field>,
    Field: Col<Inner=Child>,
    Child: RelationImpl,
    (Parent::Cols, Child::Cols): Record,
{
    type Cols = <(Parent::Cols, Child::Cols) as Record>::Cols;
    type FastCols = Parent::FastCols;
    type Planner = SubJoinPlanner;
}

impl<'a, Parent, Field, Child, OutCols>
    QueryOutput<'a> for SubordinateJoin<Parent,Field>
where
    Parent: QueryOutput<'a>,
    OutCols: Header + HasCol<Field>,
    Field: Col<Inner=Child>,
    Child: QueryOutput<'a>,
    Self: RelationImpl<Cols=OutCols>,
    (Parent::QueryRow, Child::QueryRow): ExternalRecord<'a, Cols=OutCols>
{
    type QueryRow = (Parent::QueryRow, Child::QueryRow);
}
    
#[derive(Debug, Default)]
pub struct SubJoinPlanner;
defun_nocalc!{() SubJoinPlanner {
    ('a, F:Col, P:RelationImpl, Req:QueryRequest)
    { _:&'a SubordinateJoin<P,F>, _:Req }
    => {If, {EmptyP, @Req::OrderBy},
            {Ret, @SubJoinPlan<'a, P,F,Req>},
            {FallbackPlanner, @&'a SubordinateJoin<P,F>, @Req}};
}}

pub struct SubJoinPlan<'a,P,F,Req> {
    join: &'a SubordinateJoin<P,F>,
    req: Req
}

impl<'a, Parent, Field, Req>
    QueryPlanImpl<'a, SubordinateJoin<Parent,Field>, Req>
for SubJoinPlan<'a, Parent, Field, Req>
where
{
    fn prepare(rel: &'a SubordinateJoin<Parent,Field>, req:Req) -> Self {
        SubJoinPlan { join: rel, req }
    }
}

impl<'a,Parent,Field,Child,Req>
    IntoIterator for SubJoinPlan<'a,Parent,Field,Req>
where
    Req: QueryRequest + 'a,
    Parent:Queryable<'a,Req>,
    Child:Queryable<'a,Req>,
    Parent::Cols: HasCol<Field>,
    Field: Col<Inner=Child>
{
    type Item = (Parent::QueryRow, Child::QueryRow);
    type IntoIter = impl Iterator<Item = Self::Item>;
    fn into_iter(self)->Self::IntoIter {
        self.join.0
            .query(self.req.clone())
            .flat_map(move |parent_row| {
                parent_row.ext_col_ref::<Field>()
                          .query(self.req.clone())
                          .map(move |child_row| (parent_row, child_row))
        })
    }
}

impl<'a,Parent,Field,Child>
    IntoIterator for &'a SubordinateJoin<Parent,Field>
where
    Parent:Relation<'a>,
    Child: 'a+Relation<'a>,
    Parent::Cols: HasCol<Field>,
    Field: Col<Inner=Child>
{
    type Item = (Parent::QueryRow, Child::QueryRow);
    type IntoIter = impl Iterator<Item = Self::Item>;
    fn into_iter(self)->Self::IntoIter {
        self.0
            .iter_all()
            .flat_map(move |parent_row| {
                parent_row.ext_col_ref::<Field>()
                          .iter_all()
                          .map(move |child_row| (parent_row, child_row))
        })
    }
}

#[cfg(test)] mod test {
    use super::*;
    use crate::relation::SelfQuery;

    #[test]
    fn test_subjoin_plan() {
        col!{A: usize}
        col!{B: usize}
        col!{C: usize}
        col!{CVec: Vec<C>}
        assert_trait!{Vec<C>: RelationImpl}
        assert_trait!{Vec<(A, CVec)>: RelationImpl}
        
        use crate::query::request::*;
        use crate::query::filter::Exact;
        use crate::relation::RelProxy;

        type Join<'a> = SubordinateJoin<RelProxy<&'a Vec<(A, CVec)>>, CVec>;

        let rel: Vec<(A, CVec)> = vec![
            (A(1), CVec(vec![C(3), C(6)])),
            (A(2), CVec(vec![C(6)])),
        ];

        assert_trait!{Join: RelationImpl}
        let join: Join = SubordinateJoin::new(RelationImpl::as_ref(&rel));

        assert_eq!(3, join.iter_all().count());
        assert_eq!(2, join.as_ref().where_eq(C(6)).iter_all().count());
        assert_eq!(2, join.as_ref().where_eq(A(1)).iter_all().count());
    }
}