graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use crate::{
    EntityReference, Explain, IndexDomain, Indexed, Labeled, Mask, Operand, QueryResult, Unit,
    element::{Pipeline, Preserving},
    execution::EvaluationCache,
    index::IndicesInGroup,
    operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
    optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
    registry::operation_manifest,
    traits::InGroup,
};
use graphrecords_core::{GraphRecord, graphrecord::Group};

#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
#[operation(scope = Element)]
#[explain(label = "InGroup")]
#[plan(optimizer_hints(commutes_with_filter, allows_limit_pushdown, empty = if_any))]
pub struct InGroupOperation {
    #[explain(label)]
    group: Group,
}

impl Prepare for InGroupOperation {
    type Prepared<'a> = &'a Group;

    fn prepare<'a>(
        &'a self,
        _graphrecord: &'a GraphRecord,
        _cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<Self::Prepared<'a>> {
        Ok(&self.group)
    }
}

impl<I: IndicesInGroup> ElementKernel<Indexed<I, Unit>> for InGroupOperation {
    type Emission = Preserving;
    type OutShape = Indexed<I, Mask>;

    fn pipeline<'a>(
        graphrecord: &'a GraphRecord,
        prepared: Self::Prepared<'a>,
    ) -> QueryResult<ElementPipeline<'a, Indexed<I, Unit>, Self>> {
        let members = I::indices_in_group(Self::LABEL, graphrecord, prepared)?;

        Ok(Pipeline::keyed(move |index, membership: QueryResult<_>| {
            membership.map(|()| members.contains(&index))
        }))
    }

    fn estimate(&self, input: Estimate, stats: &Stats) -> Estimate {
        let size = I::group_size(stats, &self.group);
        let selectivity = input
            .elements
            .map(|elements| size.min(elements) as f64 / elements.max(1) as f64);

        Estimate {
            selectivity,
            ..input
        }
    }
}

impl<E: IndicesInGroup, I: IndexDomain> ElementKernel<Indexed<I, EntityReference<E>>>
    for InGroupOperation
{
    type Emission = Preserving;
    type OutShape = Indexed<I, Mask>;

    fn pipeline<'a>(
        graphrecord: &'a GraphRecord,
        prepared: Self::Prepared<'a>,
    ) -> QueryResult<ElementPipeline<'a, Indexed<I, EntityReference<E>>, Self>> {
        let members = E::indices_in_group(Self::LABEL, graphrecord, prepared)?;

        Ok(Pipeline::unkeyed(move |reference: QueryResult<_>| {
            reference.map(|entity| members.contains(&entity))
        }))
    }

    fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
        Estimate {
            selectivity: None,
            ..input
        }
    }
}

impl<O: Apply<InGroupOperation>> InGroup for O {
    type ReturnOperand = O::Output;

    fn in_group(&self, group: Group) -> Self::ReturnOperand {
        Self::ReturnOperand::new(OperationContext::new(
            self.clone(),
            InGroupOperation { group },
        ))
    }
}

operation_manifest! {
    InGroupOperation {
        method: InGroup::in_group;
        scope: element;

        kernel {
            parameters: <I: IndicesInGroup>;
            field: group: Group;
            input: Indexed<I, Unit>;
            output: Indexed<I, Mask>;
            emission: Preserving;
        }

        kernel {
            parameters: <E: IndicesInGroup, I: IndexDomain>;
            field: group: Group;
            input: Indexed<I, EntityReference<E>>;
            output: Indexed<I, Mask>;
            emission: Preserving;
        }
    }
}