Skip to main content

graphrecords_query/operations/structure/
attributes.rs

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