vortex_expr/
get_item.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::any::Any;
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::sync::Arc;

use vortex_array::ArrayData;
use vortex_dtype::Field;
use vortex_error::{vortex_err, VortexResult};

use crate::{ExprRef, VortexExpr};

#[derive(Debug, Clone, Eq, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct GetItem {
    field: Field,
    child: ExprRef,
}

impl GetItem {
    pub fn new_expr(field: impl Into<Field>, child: ExprRef) -> ExprRef {
        Arc::new(Self {
            field: field.into(),
            child,
        })
    }

    pub fn field(&self) -> &Field {
        &self.field
    }

    pub fn child(&self) -> &ExprRef {
        &self.child
    }
}

pub fn get_item(field: impl Into<Field>, child: ExprRef) -> ExprRef {
    GetItem::new_expr(field, child)
}

impl Display for GetItem {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}", self.child, self.field)
    }
}

impl VortexExpr for GetItem {
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn evaluate(&self, batch: &ArrayData) -> VortexResult<ArrayData> {
        let child = self.child.evaluate(batch)?;
        child
            .as_struct_array()
            .ok_or_else(|| vortex_err!("GetItem: child array into struct"))?
            // TODO(joe): apply struct validity
            .maybe_null_field(self.field())
            .ok_or_else(|| vortex_err!("Field {} not found", self.field))
    }

    fn children(&self) -> Vec<&ExprRef> {
        vec![self.child()]
    }

    fn replacing_children(self: Arc<Self>, children: Vec<ExprRef>) -> ExprRef {
        assert_eq!(children.len(), 1);
        Self::new_expr(self.field().clone(), children[0].clone())
    }
}

impl PartialEq for GetItem {
    fn eq(&self, other: &GetItem) -> bool {
        self.field == other.field && self.child.eq(&other.child)
    }
}

#[cfg(test)]
mod tests {
    use vortex_array::array::StructArray;
    use vortex_array::{ArrayDType, IntoArrayData};
    use vortex_buffer::buffer;
    use vortex_dtype::DType;
    use vortex_dtype::PType::{I32, I64};

    use crate::get_item::get_item;
    use crate::ident;

    fn test_array() -> StructArray {
        StructArray::from_fields(&[
            ("a", buffer![0i32, 1, 2].into_array()),
            ("b", buffer![4i64, 5, 6].into_array()),
        ])
        .unwrap()
    }

    #[test]
    pub fn get_item_by_name() {
        let st = test_array();
        let get_item = get_item("a", ident());
        let item = get_item.evaluate(st.as_ref()).unwrap();
        assert_eq!(item.dtype(), &DType::from(I32))
    }

    #[test]
    pub fn get_item_by_name_none() {
        let st = test_array();
        let get_item = get_item("c", ident());
        assert!(get_item.evaluate(st.as_ref()).is_err());
    }

    #[test]
    pub fn get_item_by_idx() {
        let st = test_array();
        let get_item = get_item(1, ident());
        let item = get_item.evaluate(st.as_ref()).unwrap();
        assert_eq!(item.dtype(), &DType::from(I64))
    }
}