Skip to main content

graphrecords_query/operations/structure/
has_attribute.rs

1use crate::{
2    EntityReference, Explain, IndexDomain, Indexed, Mask, Operand, QueryResult, Unit,
3    element::{Pipeline, Preserving},
4    execution::EvaluationCache,
5    index::EntityAttributes,
6    operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
7    optimizer::{OperationInputs, OptimizerHints, PlanIdentity, PlanInputs},
8    registry::operation_manifest,
9    traits::HasAttribute,
10};
11use graphrecords_core::{GraphRecord, graphrecord::GraphRecordAttribute};
12
13#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
14#[operation(scope = Element)]
15#[explain(label = "HasAttribute")]
16#[plan(optimizer_hints(commutes_with_filter, allows_limit_pushdown, empty = if_any))]
17pub struct HasAttributeOperation {
18    #[explain(label)]
19    attribute: GraphRecordAttribute,
20}
21
22impl Prepare for HasAttributeOperation {
23    type Prepared<'a> = &'a GraphRecordAttribute;
24
25    fn prepare<'a>(
26        &'a self,
27        _graphrecord: &'a GraphRecord,
28        _cache: &'a EvaluationCache<'a>,
29    ) -> QueryResult<Self::Prepared<'a>> {
30        Ok(&self.attribute)
31    }
32}
33
34impl<E: EntityAttributes> ElementKernel<Indexed<E, Unit>> for HasAttributeOperation {
35    type Emission = Preserving;
36    type OutShape = Indexed<E, Mask>;
37
38    fn pipeline<'a>(
39        graphrecord: &'a GraphRecord,
40        prepared: Self::Prepared<'a>,
41    ) -> QueryResult<ElementPipeline<'a, Indexed<E, Unit>, Self>> {
42        Ok(Pipeline::keyed(move |index, membership: QueryResult<_>| {
43            membership.map(|()| {
44                E::attributes(graphrecord, &index)
45                    .expect("Entity must exist")
46                    .contains_key(prepared)
47            })
48        }))
49    }
50}
51
52impl<E: EntityAttributes, I: IndexDomain> ElementKernel<Indexed<I, EntityReference<E>>>
53    for HasAttributeOperation
54{
55    type Emission = Preserving;
56    type OutShape = Indexed<I, Mask>;
57
58    fn pipeline<'a>(
59        graphrecord: &'a GraphRecord,
60        prepared: Self::Prepared<'a>,
61    ) -> QueryResult<ElementPipeline<'a, Indexed<I, EntityReference<E>>, Self>> {
62        Ok(Pipeline::unkeyed(move |reference: QueryResult<_>| {
63            reference.map(|entity| {
64                E::attributes(graphrecord, &entity)
65                    .expect("Entity must exist")
66                    .contains_key(prepared)
67            })
68        }))
69    }
70}
71
72impl<O: Apply<HasAttributeOperation>> HasAttribute for O {
73    type ReturnOperand = O::Output;
74
75    fn has_attribute(&self, attribute: GraphRecordAttribute) -> Self::ReturnOperand {
76        Self::ReturnOperand::new(OperationContext::new(
77            self.clone(),
78            HasAttributeOperation { attribute },
79        ))
80    }
81}
82
83operation_manifest! {
84    HasAttributeOperation {
85        method: HasAttribute::has_attribute;
86        scope: element;
87
88        kernel {
89            parameters: <E: EntityAttributes>;
90            field: attribute: GraphRecordAttribute;
91            input: Indexed<E, Unit>;
92            output: Indexed<E, Mask>;
93            emission: Preserving;
94        }
95
96        kernel {
97            parameters: <E: EntityAttributes, I: IndexDomain>;
98            field: attribute: GraphRecordAttribute;
99            input: Indexed<I, EntityReference<E>>;
100            output: Indexed<I, Mask>;
101            emission: Preserving;
102        }
103    }
104}