vortex_expr/
binary.rs

1use std::any::Any;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::sync::Arc;
5
6use vortex_array::compute::{Operator as ArrayOperator, and_kleene, compare, or_kleene};
7use vortex_array::{Array, ArrayRef};
8use vortex_dtype::DType;
9use vortex_error::VortexResult;
10
11use crate::{ExprRef, Operator, VortexExpr};
12
13#[derive(Debug, Clone, Eq, Hash)]
14#[allow(clippy::derived_hash_with_manual_eq)]
15pub struct BinaryExpr {
16    lhs: ExprRef,
17    operator: Operator,
18    rhs: ExprRef,
19}
20
21impl BinaryExpr {
22    pub fn new_expr(lhs: ExprRef, operator: Operator, rhs: ExprRef) -> ExprRef {
23        Arc::new(Self { lhs, operator, rhs })
24    }
25
26    pub fn lhs(&self) -> &ExprRef {
27        &self.lhs
28    }
29
30    pub fn rhs(&self) -> &ExprRef {
31        &self.rhs
32    }
33
34    pub fn op(&self) -> Operator {
35        self.operator
36    }
37}
38
39impl Display for BinaryExpr {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        write!(f, "({} {} {})", self.lhs, self.operator, self.rhs)
42    }
43}
44
45impl VortexExpr for BinaryExpr {
46    fn as_any(&self) -> &dyn Any {
47        self
48    }
49
50    fn unchecked_evaluate(&self, batch: &dyn Array) -> VortexResult<ArrayRef> {
51        let lhs = self.lhs.evaluate(batch)?;
52        let rhs = self.rhs.evaluate(batch)?;
53
54        match self.operator {
55            Operator::Eq => compare(&lhs, &rhs, ArrayOperator::Eq),
56            Operator::NotEq => compare(&lhs, &rhs, ArrayOperator::NotEq),
57            Operator::Lt => compare(&lhs, &rhs, ArrayOperator::Lt),
58            Operator::Lte => compare(&lhs, &rhs, ArrayOperator::Lte),
59            Operator::Gt => compare(&lhs, &rhs, ArrayOperator::Gt),
60            Operator::Gte => compare(&lhs, &rhs, ArrayOperator::Gte),
61            Operator::And => and_kleene(&lhs, &rhs),
62            Operator::Or => or_kleene(&lhs, &rhs),
63        }
64    }
65
66    fn children(&self) -> Vec<&ExprRef> {
67        vec![&self.lhs, &self.rhs]
68    }
69
70    fn replacing_children(self: Arc<Self>, children: Vec<ExprRef>) -> ExprRef {
71        assert_eq!(children.len(), 2);
72        BinaryExpr::new_expr(children[0].clone(), self.operator, children[1].clone())
73    }
74
75    fn return_dtype(&self, scope_dtype: &DType) -> VortexResult<DType> {
76        let lhs = self.lhs.return_dtype(scope_dtype)?;
77        let rhs = self.rhs.return_dtype(scope_dtype)?;
78        Ok(DType::Bool((lhs.is_nullable() || rhs.is_nullable()).into()))
79    }
80}
81
82impl PartialEq for BinaryExpr {
83    fn eq(&self, other: &BinaryExpr) -> bool {
84        other.operator == self.operator && other.lhs.eq(&self.lhs) && other.rhs.eq(&self.rhs)
85    }
86}
87
88/// Create a new `BinaryExpr` using the `Eq` operator.
89///
90/// ## Example usage
91///
92/// ```
93/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
94/// use vortex_array::{Array, IntoArray, ToCanonical};
95/// use vortex_array::validity::Validity;
96/// use vortex_buffer::buffer;
97/// use vortex_expr::{eq, ident, lit};
98///
99/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
100/// let result = eq(ident(), lit(3)).evaluate(&xs).unwrap();
101///
102/// assert_eq!(
103///     result.to_bool().unwrap().boolean_buffer(),
104///     BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
105/// );
106/// ```
107pub fn eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
108    BinaryExpr::new_expr(lhs, Operator::Eq, rhs)
109}
110
111/// Create a new `BinaryExpr` using the `NotEq` operator.
112///
113/// ## Example usage
114///
115/// ```
116/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
117/// use vortex_array::{IntoArray, ToCanonical};
118/// use vortex_array::validity::Validity;
119/// use vortex_buffer::buffer;
120/// use vortex_expr::{ident, lit, not_eq};
121///
122/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
123/// let result = not_eq(ident(), lit(3)).evaluate(&xs).unwrap();
124///
125/// assert_eq!(
126///     result.to_bool().unwrap().boolean_buffer(),
127///     BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
128/// );
129/// ```
130pub fn not_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
131    BinaryExpr::new_expr(lhs, Operator::NotEq, rhs)
132}
133
134/// Create a new `BinaryExpr` using the `Gte` operator.
135///
136/// ## Example usage
137///
138/// ```
139/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
140/// use vortex_array::{IntoArray, ToCanonical};
141/// use vortex_array::validity::Validity;
142/// use vortex_buffer::buffer;
143/// use vortex_expr::{gt_eq, ident, lit};
144///
145/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
146/// let result = gt_eq(ident(), lit(3)).evaluate(&xs).unwrap();
147///
148/// assert_eq!(
149///     result.to_bool().unwrap().boolean_buffer(),
150///     BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
151/// );
152/// ```
153pub fn gt_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
154    BinaryExpr::new_expr(lhs, Operator::Gte, rhs)
155}
156
157/// Create a new `BinaryExpr` using the `Gt` operator.
158///
159/// ## Example usage
160///
161/// ```
162/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
163/// use vortex_array::{IntoArray, ToCanonical};
164/// use vortex_array::validity::Validity;
165/// use vortex_buffer::buffer;
166/// use vortex_expr::{gt, ident, lit};
167///
168/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
169/// let result = gt(ident(), lit(2)).evaluate(&xs).unwrap();
170///
171/// assert_eq!(
172///     result.to_bool().unwrap().boolean_buffer(),
173///     BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
174/// );
175/// ```
176pub fn gt(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
177    BinaryExpr::new_expr(lhs, Operator::Gt, rhs)
178}
179
180/// Create a new `BinaryExpr` using the `Lte` operator.
181///
182/// ## Example usage
183///
184/// ```
185/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
186/// use vortex_array::{IntoArray, ToCanonical};
187/// use vortex_array::validity::Validity;
188/// use vortex_buffer::buffer;
189/// use vortex_expr::{ident, lit, lt_eq};
190///
191/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
192/// let result = lt_eq(ident(), lit(2)).evaluate(&xs).unwrap();
193///
194/// assert_eq!(
195///     result.to_bool().unwrap().boolean_buffer(),
196///     BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
197/// );
198/// ```
199pub fn lt_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
200    BinaryExpr::new_expr(lhs, Operator::Lte, rhs)
201}
202
203/// Create a new `BinaryExpr` using the `Lt` operator.
204///
205/// ## Example usage
206///
207/// ```
208/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
209/// use vortex_array::{IntoArray, ToCanonical};
210/// use vortex_array::validity::Validity;
211/// use vortex_buffer::buffer;
212/// use vortex_expr::{ident, lit, lt};
213///
214/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
215/// let result = lt(ident(), lit(3)).evaluate(&xs).unwrap();
216///
217/// assert_eq!(
218///     result.to_bool().unwrap().boolean_buffer(),
219///     BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
220/// );
221/// ```
222pub fn lt(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
223    BinaryExpr::new_expr(lhs, Operator::Lt, rhs)
224}
225
226/// Create a new `BinaryExpr` using the `Or` operator.
227///
228/// ## Example usage
229///
230/// ```
231/// use vortex_array::arrays::BoolArray;
232/// use vortex_array::{IntoArray, ToCanonical};
233/// use vortex_expr::{ ident, lit, or};
234///
235/// let xs = BoolArray::from_iter(vec![true, false, true]);
236/// let result = or(ident(), lit(false)).evaluate(&xs).unwrap();
237///
238/// assert_eq!(
239///     result.to_bool().unwrap().boolean_buffer(),
240///     BoolArray::from_iter(vec![true, false, true]).boolean_buffer(),
241/// );
242/// ```
243pub fn or(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
244    BinaryExpr::new_expr(lhs, Operator::Or, rhs)
245}
246
247/// Create a new `BinaryExpr` using the `And` operator.
248///
249/// ## Example usage
250///
251/// ```
252/// use vortex_array::arrays::BoolArray;
253/// use vortex_array::{IntoArray, ToCanonical};
254/// use vortex_expr::{and, ident, lit};
255///
256/// let xs = BoolArray::from_iter(vec![true, false, true]);
257/// let result = and(ident(), lit(true)).evaluate(&xs).unwrap();
258///
259/// assert_eq!(
260///     result.to_bool().unwrap().boolean_buffer(),
261///     BoolArray::from_iter(vec![true, false, true]).boolean_buffer(),
262/// );
263/// ```
264pub fn and(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
265    BinaryExpr::new_expr(lhs, Operator::And, rhs)
266}
267
268#[cfg(test)]
269mod tests {
270    use std::sync::Arc;
271
272    use vortex_dtype::{DType, Nullability};
273
274    use crate::{VortexExpr, and, col, eq, gt, gt_eq, lt, lt_eq, not_eq, or, test_harness};
275
276    #[test]
277    fn dtype() {
278        let dtype = test_harness::struct_dtype();
279        let bool1: Arc<dyn VortexExpr> = col("bool1");
280        let bool2: Arc<dyn VortexExpr> = col("bool2");
281        assert_eq!(
282            and(bool1.clone(), bool2.clone())
283                .return_dtype(&dtype)
284                .unwrap(),
285            DType::Bool(Nullability::NonNullable)
286        );
287        assert_eq!(
288            or(bool1.clone(), bool2.clone())
289                .return_dtype(&dtype)
290                .unwrap(),
291            DType::Bool(Nullability::NonNullable)
292        );
293
294        let col1: Arc<dyn VortexExpr> = col("col1");
295        let col2: Arc<dyn VortexExpr> = col("col2");
296
297        assert_eq!(
298            eq(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
299            DType::Bool(Nullability::Nullable)
300        );
301        assert_eq!(
302            not_eq(col1.clone(), col2.clone())
303                .return_dtype(&dtype)
304                .unwrap(),
305            DType::Bool(Nullability::Nullable)
306        );
307        assert_eq!(
308            gt(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
309            DType::Bool(Nullability::Nullable)
310        );
311        assert_eq!(
312            gt_eq(col1.clone(), col2.clone())
313                .return_dtype(&dtype)
314                .unwrap(),
315            DType::Bool(Nullability::Nullable)
316        );
317        assert_eq!(
318            lt(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
319            DType::Bool(Nullability::Nullable)
320        );
321        assert_eq!(
322            lt_eq(col1.clone(), col2.clone())
323                .return_dtype(&dtype)
324                .unwrap(),
325            DType::Bool(Nullability::Nullable)
326        );
327
328        assert_eq!(
329            or(
330                lt(col1.clone(), col2.clone()),
331                not_eq(col1.clone(), col2.clone())
332            )
333            .return_dtype(&dtype)
334            .unwrap(),
335            DType::Bool(Nullability::Nullable)
336        );
337    }
338}