diff --git a/arrow-string/src/like.rs b/arrow-string/src/like.rs index 55fe292ecb76..2ad7e1cfd961 100644 --- a/arrow-string/src/like.rs +++ b/arrow-string/src/like.rs @@ -15,7 +15,11 @@ // specific language governing permissions and limitations // under the License. -//! Provide SQL's LIKE operators for Arrow's string arrays +//! String predicate kernels for Arrow arrays. +//! +//! Provides SQL `LIKE`/`ILIKE` kernels as well as related +//! string predicates such as `contains`, `starts_with`, `ends_with`, and +//! ASCII case-insensitive equality. use crate::predicate::Predicate; @@ -34,6 +38,7 @@ pub(crate) enum Op { Like(bool), ILike(bool), Contains, + EqIgnoreAsciiCase, StartsWith, EndsWith, } @@ -46,6 +51,7 @@ impl std::fmt::Display for Op { Op::ILike(false) => write!(f, "ILIKE"), Op::ILike(true) => write!(f, "NILIKE"), Op::Contains => write!(f, "CONTAINS"), + Op::EqIgnoreAsciiCase => write!(f, "EQ_IGNORE_ASCII_CASE"), Op::StartsWith => write!(f, "STARTS_WITH"), Op::EndsWith => write!(f, "ENDS_WITH"), } @@ -124,7 +130,7 @@ pub fn nilike(left: &dyn Datum, right: &dyn Datum) -> Result Result Result Result Result { + like_op(Op::EqIgnoreAsciiCase, left, right) +} + fn like_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> Result { use arrow_schema::DataType::*; let (l, l_s) = lhs.get(); @@ -328,6 +358,7 @@ fn op_scalar<'a, T: StringArrayType<'a>>( Op::Like(neg) => Predicate::like(r)?.evaluate_array(l, neg), Op::ILike(neg) => Predicate::ilike(r, l.is_ascii())?.evaluate_array(l, neg), Op::Contains => Predicate::contains(r).evaluate_array(l, false), + Op::EqIgnoreAsciiCase => Predicate::IEqAscii(r).evaluate_array(l, false), Op::StartsWith => Predicate::StartsWith(r).evaluate_array(l, false), Op::EndsWith => Predicate::EndsWith(r).evaluate_array(l, false), }; @@ -362,6 +393,10 @@ fn op_binary<'a>( Op::Like(neg) => binary_predicate(l, r, neg, Predicate::like), Op::ILike(neg) => binary_predicate(l, r, neg, |s| Predicate::ilike(s, false)), Op::Contains => Ok(l.zip(r).map(|(l, r)| Some(str_contains(l?, r?))).collect()), + Op::EqIgnoreAsciiCase => Ok(l + .zip(r) + .map(|(l, r)| Some(Predicate::IEqAscii(l?).evaluate(r?))) + .collect()), Op::StartsWith => Ok(l .zip(r) .map(|(l, r)| Some(Predicate::StartsWith(r?).evaluate(l?))) @@ -1394,6 +1429,22 @@ mod tests { vec![true, false, true, true, true] ); + test_utf8!( + test_utf8_array_eq_ignore_ascii_case, + vec!["arrow", "arrow", "arrow", "arrow", "parquet", "parquet"], + vec!["arrow", "ARROW", "arro", "aRrOw", "arrow", "ARROW"], + eq_ignore_ascii_case, + vec![true, true, false, true, false, false] + ); + + test_utf8_scalar!( + test_utf8_array_eq_ignore_ascii_case_scalar, + vec!["arrow", "aRrOW", "arro", "ARROW", "parquet", "PARQUET"], + "arrow", + eq_ignore_ascii_case, + vec![true, true, false, true, false, false] + ); + #[test] fn test_dict_like_kernels() { let data = vec![