Skip to main content

graphrecords_query/operations/string_operations/
length.rs

1use super::{string_map_bare, string_map_indexed};
2use crate::{
3    Bare, BareValueDomain, Explain, Failure, IndexDomain, Indexed, Labeled, Operand, QueryResult,
4    Scalar,
5    capabilities::StringValue,
6    element::Preserving,
7    error::string::StringLengthOverflow,
8    execution::EvaluationCache,
9    operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
10    optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
11    registry::operation_manifest,
12    traits::Length,
13};
14use graphrecords_core::{GraphRecord, graphrecord::GraphRecordValue};
15
16pub(super) fn length_chars(label: &'static str, value: &str) -> QueryResult<GraphRecordValue> {
17    let length = value.chars().count();
18    let length = i64::try_from(length)
19        .map_err(|_| Failure::new(label, StringLengthOverflow::new(length)))?;
20
21    Ok(GraphRecordValue::Int(length))
22}
23
24#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
25#[operation(scope = Element)]
26#[explain(label = "Length")]
27#[plan(optimizer_hints(allows_limit_pushdown, empty = if_any))]
28pub struct LengthOperation;
29
30impl Prepare for LengthOperation {
31    type Prepared<'a> = ();
32
33    fn prepare<'a>(
34        &'a self,
35        _graphrecord: &'a GraphRecord,
36        _cache: &'a EvaluationCache<'a>,
37    ) -> QueryResult<Self::Prepared<'a>> {
38        Ok(())
39    }
40}
41
42impl<I, V> ElementKernel<Indexed<I, V>> for LengthOperation
43where
44    I: IndexDomain,
45    V: StringValue,
46{
47    type Emission = Preserving;
48    type OutShape = Indexed<I, Scalar>;
49
50    fn pipeline<'a>(
51        _graphrecord: &'a GraphRecord,
52        _prepared: Self::Prepared<'a>,
53    ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
54        Ok(string_map_indexed::<I, V, Scalar>(
55            Self::LABEL,
56            |label, value| length_chars(label, &value),
57        ))
58    }
59
60    fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
61        input.with_unknown_distinct()
62    }
63}
64
65impl<V> ElementKernel<Bare<V>> for LengthOperation
66where
67    V: StringValue + BareValueDomain,
68{
69    type Emission = Preserving;
70    type OutShape = Bare<Scalar>;
71
72    fn pipeline<'a>(
73        _graphrecord: &'a GraphRecord,
74        _prepared: Self::Prepared<'a>,
75    ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
76        Ok(string_map_bare::<V, Scalar>(Self::LABEL, |label, value| {
77            length_chars(label, &value)
78        }))
79    }
80
81    fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
82        input.with_unknown_distinct()
83    }
84}
85
86impl<O: Apply<LengthOperation>> Length for O {
87    type ReturnOperand = O::Output;
88
89    fn length(&self) -> Self::ReturnOperand {
90        Self::ReturnOperand::new(OperationContext::new(self.clone(), LengthOperation))
91    }
92}
93
94operation_manifest! {
95    LengthOperation {
96        method: Length::length;
97        scope: element;
98
99        kernel {
100            parameters: <I: IndexDomain, V: StringValue>;
101            input: Indexed<I, V>;
102            output: Indexed<I, Scalar>;
103            emission: Preserving;
104        }
105        kernel {
106            parameters: <V: StringValue + BareValueDomain>;
107            input: Bare<V>;
108            output: Bare<Scalar>;
109            emission: Preserving;
110        }
111    }
112}