You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2022/08/26 22:43:15 UTC

[arrow-rs] branch master updated: Dictionary like scalar kernels (#2591)

This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 9abc5f580 Dictionary like scalar kernels (#2591)
9abc5f580 is described below

commit 9abc5f5804c47147178e1f4c39be053b853b04d0
Author: Vrishabh <ps...@gmail.com>
AuthorDate: Sat Aug 27 04:13:10 2022 +0530

    Dictionary like scalar kernels (#2591)
    
    * Intial implmentation of like kernels
    
    * Refactor nlike_scalar kernels
    
    * Fix cargo.toml
    
    * Add other dict scalar kernels
    
    * Replace macro with array accessor functions
    
    * Remove commented code
    
    * Fix typo in error message
    
    * Fix doc comments
---
 arrow/src/compute/kernels/comparison.rs | 528 ++++++++++++++++++++++++++++----
 1 file changed, 467 insertions(+), 61 deletions(-)

diff --git a/arrow/src/compute/kernels/comparison.rs b/arrow/src/compute/kernels/comparison.rs
index dab93a8c8..f873079a0 100644
--- a/arrow/src/compute/kernels/comparison.rs
+++ b/arrow/src/compute/kernels/comparison.rs
@@ -235,12 +235,9 @@ pub fn like_utf8<OffsetSize: OffsetSizeTrait>(
     })
 }
 
-/// Perform SQL `left LIKE right` operation on [`StringArray`] /
-/// [`LargeStringArray`] and a scalar.
-///
-/// See the documentation on [`like_utf8`] for more details.
-pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
-    left: &GenericStringArray<OffsetSize>,
+#[inline]
+fn like_scalar<'a, L: ArrayAccessor<Item = &'a str>>(
+    left: L,
     right: &str,
 ) -> Result<BooleanArray> {
     let null_bit_buffer = left.data().null_buffer().cloned();
@@ -251,8 +248,10 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
     if !right.contains(is_like_pattern) {
         // fast path, can use equals
         for i in 0..left.len() {
-            if left.value(i) == right {
-                bit_util::set_bit(bool_slice, i);
+            unsafe {
+                if left.value_unchecked(i) == right {
+                    bit_util::set_bit(bool_slice, i);
+                }
             }
         }
     } else if right.ends_with('%')
@@ -262,8 +261,10 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         // fast path, can use starts_with
         let starts_with = &right[..right.len() - 1];
         for i in 0..left.len() {
-            if left.value(i).starts_with(starts_with) {
-                bit_util::set_bit(bool_slice, i);
+            unsafe {
+                if left.value_unchecked(i).starts_with(starts_with) {
+                    bit_util::set_bit(bool_slice, i);
+                }
             }
         }
     } else if right.starts_with('%') && !right[1..].contains(is_like_pattern) {
@@ -271,8 +272,10 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         let ends_with = &right[1..];
 
         for i in 0..left.len() {
-            if left.value(i).ends_with(ends_with) {
-                bit_util::set_bit(bool_slice, i);
+            unsafe {
+                if left.value_unchecked(i).ends_with(ends_with) {
+                    bit_util::set_bit(bool_slice, i);
+                }
             }
         }
     } else if right.starts_with('%')
@@ -282,8 +285,10 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         // fast path, can use contains
         let contains = &right[1..right.len() - 1];
         for i in 0..left.len() {
-            if left.value(i).contains(contains) {
-                bit_util::set_bit(bool_slice, i);
+            unsafe {
+                if left.value_unchecked(i).contains(contains) {
+                    bit_util::set_bit(bool_slice, i);
+                }
             }
         }
     } else {
@@ -296,7 +301,7 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         })?;
 
         for i in 0..left.len() {
-            let haystack = left.value(i);
+            let haystack = unsafe { left.value_unchecked(i) };
             if re.is_match(haystack) {
                 bit_util::set_bit(bool_slice, i);
             }
@@ -317,6 +322,42 @@ pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
     Ok(BooleanArray::from(data))
 }
 
+/// Perform SQL `left LIKE right` operation on [`StringArray`] /
+/// [`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn like_utf8_scalar<OffsetSize: OffsetSizeTrait>(
+    left: &GenericStringArray<OffsetSize>,
+    right: &str,
+) -> Result<BooleanArray> {
+    like_scalar(left, right)
+}
+
+/// Perform SQL `left LIKE right` operation on [`DictionaryArray`] with values
+/// [`StringArray`]/[`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn like_dict_scalar<K: ArrowNumericType>(
+    left: &DictionaryArray<K>,
+    right: &str,
+) -> Result<BooleanArray> {
+    match left.value_type() {
+        DataType::Utf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i32>>().unwrap();
+            like_scalar(left, right)
+        }
+        DataType::LargeUtf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i64>>().unwrap();
+            like_scalar(left, right)
+        }
+        _ => {
+            Err(ArrowError::ComputeError(
+                "like_dict_scalar only supports DictionaryArray with Utf8 or LargeUtf8 values".to_string(),
+            ))
+        }
+    }
+}
+
 /// Transforms a like `pattern` to a regex compatible pattern. To achieve that, it does:
 ///
 /// 1. Replace like wildcards for regex expressions as the pattern will be evaluated using regex match: `%` => `.*` and `_` => `.`
@@ -372,34 +413,48 @@ pub fn nlike_utf8<OffsetSize: OffsetSizeTrait>(
     })
 }
 
-/// Perform SQL `left NOT LIKE right` operation on [`StringArray`] /
-/// [`LargeStringArray`] and a scalar.
-///
-/// See the documentation on [`like_utf8`] for more details.
-pub fn nlike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
-    left: &GenericStringArray<OffsetSize>,
+#[inline]
+fn nlike_scalar<'a, L: ArrayAccessor<Item = &'a str>>(
+    left: L,
     right: &str,
 ) -> Result<BooleanArray> {
     let null_bit_buffer = left.data().null_buffer().cloned();
-    let mut result = BooleanBufferBuilder::new(left.len());
+    let bytes = bit_util::ceil(left.len(), 8);
+    let mut bool_buf = MutableBuffer::from_len_zeroed(bytes);
+    let bool_slice = bool_buf.as_slice_mut();
 
     if !right.contains(is_like_pattern) {
         // fast path, can use equals
         for i in 0..left.len() {
-            result.append(left.value(i) != right);
+            unsafe {
+                if left.value_unchecked(i) != right {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.ends_with('%')
         && !right.ends_with("\\%")
         && !right[..right.len() - 1].contains(is_like_pattern)
     {
-        // fast path, can use ends_with
+        // fast path, can use starts_with
+        let starts_with = &right[..right.len() - 1];
         for i in 0..left.len() {
-            result.append(!left.value(i).starts_with(&right[..right.len() - 1]));
+            unsafe {
+                if !(left.value_unchecked(i).starts_with(starts_with)) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.starts_with('%') && !right[1..].contains(is_like_pattern) {
-        // fast path, can use starts_with
+        // fast path, can use ends_with
+        let ends_with = &right[1..];
+
         for i in 0..left.len() {
-            result.append(!left.value(i).ends_with(&right[1..]));
+            unsafe {
+                if !(left.value_unchecked(i).ends_with(ends_with)) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.starts_with('%')
         && right.ends_with('%')
@@ -408,7 +463,11 @@ pub fn nlike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         // fast path, can use contains
         let contains = &right[1..right.len() - 1];
         for i in 0..left.len() {
-            result.append(!left.value(i).contains(contains));
+            unsafe {
+                if !(left.value_unchecked(i).contains(contains)) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else {
         let re_pattern = replace_like_wildcards(right)?;
@@ -418,11 +477,14 @@ pub fn nlike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
                 e
             ))
         })?;
+
         for i in 0..left.len() {
-            let haystack = left.value(i);
-            result.append(!re.is_match(haystack));
+            let haystack = unsafe { left.value_unchecked(i) };
+            if !re.is_match(haystack) {
+                bit_util::set_bit(bool_slice, i);
+            }
         }
-    }
+    };
 
     let data = unsafe {
         ArrayData::new_unchecked(
@@ -431,13 +493,49 @@ pub fn nlike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
             None,
             null_bit_buffer,
             0,
-            vec![result.finish()],
+            vec![bool_buf.into()],
             vec![],
         )
     };
     Ok(BooleanArray::from(data))
 }
 
+/// Perform SQL `left NOT LIKE right` operation on [`StringArray`] /
+/// [`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn nlike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
+    left: &GenericStringArray<OffsetSize>,
+    right: &str,
+) -> Result<BooleanArray> {
+    nlike_scalar(left, right)
+}
+
+/// Perform SQL `left NOT LIKE right` operation on [`DictionaryArray`] with values
+/// [`StringArray`]/[`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn nlike_dict_scalar<K: ArrowNumericType>(
+    left: &DictionaryArray<K>,
+    right: &str,
+) -> Result<BooleanArray> {
+    match left.value_type() {
+        DataType::Utf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i32>>().unwrap();
+            nlike_scalar(left, right)
+        }
+        DataType::LargeUtf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i64>>().unwrap();
+            nlike_scalar(left, right)
+        }
+        _ => {
+            Err(ArrowError::ComputeError(
+                "nlike_dict_scalar only supports DictionaryArray with Utf8 or LargeUtf8 values".to_string(),
+            ))
+        }
+    }
+}
+
 /// Perform SQL `left ILIKE right` operation on [`StringArray`] /
 /// [`LargeStringArray`].
 ///
@@ -456,22 +554,25 @@ pub fn ilike_utf8<OffsetSize: OffsetSizeTrait>(
     })
 }
 
-/// Perform SQL `left ILIKE right` operation on [`StringArray`] /
-/// [`LargeStringArray`] and a scalar.
-///
-/// See the documentation on [`like_utf8`] for more details.
-pub fn ilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
-    left: &GenericStringArray<OffsetSize>,
+#[inline]
+fn ilike_scalar<'a, L: ArrayAccessor<Item = &'a str>>(
+    left: L,
     right: &str,
 ) -> Result<BooleanArray> {
     let null_bit_buffer = left.data().null_buffer().cloned();
-    let mut result = BooleanBufferBuilder::new(left.len());
+    let bytes = bit_util::ceil(left.len(), 8);
+    let mut bool_buf = MutableBuffer::from_len_zeroed(bytes);
+    let bool_slice = bool_buf.as_slice_mut();
 
     if !right.contains(is_like_pattern) {
         // fast path, can use equals
         let right_uppercase = right.to_uppercase();
         for i in 0..left.len() {
-            result.append(left.value(i).to_uppercase() == right_uppercase);
+            unsafe {
+                if left.value_unchecked(i).to_uppercase() == right_uppercase {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.ends_with('%')
         && !right.ends_with("\\%")
@@ -480,13 +581,39 @@ pub fn ilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         // fast path, can use starts_with
         let start_str = &right[..right.len() - 1].to_uppercase();
         for i in 0..left.len() {
-            result.append(left.value(i).to_uppercase().starts_with(start_str));
+            unsafe {
+                if left
+                    .value_unchecked(i)
+                    .to_uppercase()
+                    .starts_with(start_str)
+                {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.starts_with('%') && !right[1..].contains(is_like_pattern) {
         // fast path, can use ends_with
         let ends_str = &right[1..].to_uppercase();
+
         for i in 0..left.len() {
-            result.append(left.value(i).to_uppercase().ends_with(ends_str));
+            unsafe {
+                if left.value_unchecked(i).to_uppercase().ends_with(ends_str) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
+        }
+    } else if right.starts_with('%')
+        && right.ends_with('%')
+        && !right[1..right.len() - 1].contains(is_like_pattern)
+    {
+        // fast path, can use contains
+        let contains = &right[1..right.len() - 1].to_uppercase();
+        for i in 0..left.len() {
+            unsafe {
+                if left.value_unchecked(i).to_uppercase().contains(contains) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else {
         let re_pattern = replace_like_wildcards(right)?;
@@ -496,11 +623,14 @@ pub fn ilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
                 e
             ))
         })?;
+
         for i in 0..left.len() {
-            let haystack = left.value(i);
-            result.append(re.is_match(haystack));
+            let haystack = unsafe { left.value_unchecked(i) };
+            if re.is_match(haystack) {
+                bit_util::set_bit(bool_slice, i);
+            }
         }
-    }
+    };
 
     let data = unsafe {
         ArrayData::new_unchecked(
@@ -509,13 +639,49 @@ pub fn ilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
             None,
             null_bit_buffer,
             0,
-            vec![result.finish()],
+            vec![bool_buf.into()],
             vec![],
         )
     };
     Ok(BooleanArray::from(data))
 }
 
+/// Perform SQL `left ILIKE right` operation on [`StringArray`] /
+/// [`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn ilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
+    left: &GenericStringArray<OffsetSize>,
+    right: &str,
+) -> Result<BooleanArray> {
+    ilike_scalar(left, right)
+}
+
+/// Perform SQL `left ILIKE right` operation on [`DictionaryArray`] with values
+/// [`StringArray`]/[`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn ilike_dict_scalar<K: ArrowNumericType>(
+    left: &DictionaryArray<K>,
+    right: &str,
+) -> Result<BooleanArray> {
+    match left.value_type() {
+        DataType::Utf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i32>>().unwrap();
+            ilike_scalar(left, right)
+        }
+        DataType::LargeUtf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i64>>().unwrap();
+            ilike_scalar(left, right)
+        }
+        _ => {
+            Err(ArrowError::ComputeError(
+                "ilike_dict_scalar only supports DictionaryArray with Utf8 or LargeUtf8 values".to_string(),
+            ))
+        }
+    }
+}
+
 /// Perform SQL `left NOT ILIKE right` operation on [`StringArray`] /
 /// [`LargeStringArray`].
 ///
@@ -534,22 +700,25 @@ pub fn nilike_utf8<OffsetSize: OffsetSizeTrait>(
     })
 }
 
-/// Perform SQL `left NOT ILIKE right` operation on [`StringArray`] /
-/// [`LargeStringArray`] and a scalar.
-///
-/// See the documentation on [`like_utf8`] for more details.
-pub fn nilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
-    left: &GenericStringArray<OffsetSize>,
+#[inline]
+fn nilike_scalar<'a, L: ArrayAccessor<Item = &'a str>>(
+    left: L,
     right: &str,
 ) -> Result<BooleanArray> {
     let null_bit_buffer = left.data().null_buffer().cloned();
-    let mut result = BooleanBufferBuilder::new(left.len());
+    let bytes = bit_util::ceil(left.len(), 8);
+    let mut bool_buf = MutableBuffer::from_len_zeroed(bytes);
+    let bool_slice = bool_buf.as_slice_mut();
 
     if !right.contains(is_like_pattern) {
         // fast path, can use equals
         let right_uppercase = right.to_uppercase();
         for i in 0..left.len() {
-            result.append(left.value(i).to_uppercase() != right_uppercase);
+            unsafe {
+                if left.value_unchecked(i).to_uppercase() != right_uppercase {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.ends_with('%')
         && !right.ends_with("\\%")
@@ -558,13 +727,39 @@ pub fn nilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         // fast path, can use starts_with
         let start_str = &right[..right.len() - 1].to_uppercase();
         for i in 0..left.len() {
-            result.append(!left.value(i).to_uppercase().starts_with(start_str));
+            unsafe {
+                if !(left
+                    .value_unchecked(i)
+                    .to_uppercase()
+                    .starts_with(start_str))
+                {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else if right.starts_with('%') && !right[1..].contains(is_like_pattern) {
         // fast path, can use ends_with
-        let end_str = &right[1..].to_uppercase();
+        let ends_str = &right[1..].to_uppercase();
+
+        for i in 0..left.len() {
+            unsafe {
+                if !(left.value_unchecked(i).to_uppercase().ends_with(ends_str)) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
+        }
+    } else if right.starts_with('%')
+        && right.ends_with('%')
+        && !right[1..right.len() - 1].contains(is_like_pattern)
+    {
+        // fast path, can use contains
+        let contains = &right[1..right.len() - 1].to_uppercase();
         for i in 0..left.len() {
-            result.append(!left.value(i).to_uppercase().ends_with(end_str));
+            unsafe {
+                if !(left.value_unchecked(i).to_uppercase().contains(contains)) {
+                    bit_util::set_bit(bool_slice, i);
+                }
+            }
         }
     } else {
         let re_pattern = replace_like_wildcards(right)?;
@@ -574,11 +769,14 @@ pub fn nilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
                 e
             ))
         })?;
+
         for i in 0..left.len() {
-            let haystack = left.value(i);
-            result.append(!re.is_match(haystack));
+            let haystack = unsafe { left.value_unchecked(i) };
+            if !re.is_match(haystack) {
+                bit_util::set_bit(bool_slice, i);
+            }
         }
-    }
+    };
 
     let data = unsafe {
         ArrayData::new_unchecked(
@@ -587,13 +785,49 @@ pub fn nilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
             None,
             null_bit_buffer,
             0,
-            vec![result.finish()],
+            vec![bool_buf.into()],
             vec![],
         )
     };
     Ok(BooleanArray::from(data))
 }
 
+/// Perform SQL `left NOT ILIKE right` operation on [`StringArray`] /
+/// [`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn nilike_utf8_scalar<OffsetSize: OffsetSizeTrait>(
+    left: &GenericStringArray<OffsetSize>,
+    right: &str,
+) -> Result<BooleanArray> {
+    nilike_scalar(left, right)
+}
+
+/// Perform SQL `left NOT ILIKE right` operation on [`DictionaryArray`] with values
+/// [`StringArray`]/[`LargeStringArray`] and a scalar.
+///
+/// See the documentation on [`like_utf8`] for more details.
+pub fn nilike_dict_scalar<K: ArrowNumericType>(
+    left: &DictionaryArray<K>,
+    right: &str,
+) -> Result<BooleanArray> {
+    match left.value_type() {
+        DataType::Utf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i32>>().unwrap();
+            nilike_scalar(left, right)
+        }
+        DataType::LargeUtf8 => {
+            let left = left.downcast_dict::<GenericStringArray<i64>>().unwrap();
+            nilike_scalar(left, right)
+        }
+        _ => {
+            Err(ArrowError::ComputeError(
+                "nilike_dict_scalar only supports DictionaryArray with Utf8 or LargeUtf8 values".to_string(),
+            ))
+        }
+    }
+}
+
 /// Perform SQL `array ~ regex_array` operation on [`StringArray`] / [`LargeStringArray`].
 /// If `regex_array` element has an empty value, the corresponding result value is always true.
 ///
@@ -5890,4 +6124,176 @@ mod tests {
         );
         assert_eq!(gt_eq_dyn_scalar(&array, f64::NAN).unwrap(), expected);
     }
+
+    #[test]
+    fn test_dict_like_kernels() {
+        let data =
+            vec![Some("Earth"), Some("Fire"), Some("Water"), Some("Air"), None, Some("Air")];
+
+        let dict_array: DictionaryArray<Int8Type> = data.into_iter().collect();
+
+        assert_eq!(
+            like_dict_scalar(&dict_array, "Air").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            like_dict_scalar(&dict_array, "Wa%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            like_dict_scalar(&dict_array, "%r").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(true), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            like_dict_scalar(&dict_array, "%i%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(true), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            like_dict_scalar(&dict_array, "%a%r%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(false), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+    }
+
+    #[test]
+    fn test_dict_nlike_kernels() {
+        let data =
+            vec![Some("Earth"), Some("Fire"), Some("Water"), Some("Air"), None, Some("Air")];
+
+        let dict_array: DictionaryArray<Int8Type> = data.into_iter().collect();
+
+        assert_eq!(
+            nlike_dict_scalar(&dict_array, "Air").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nlike_dict_scalar(&dict_array, "Wa%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            nlike_dict_scalar(&dict_array, "%r").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(false), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nlike_dict_scalar(&dict_array, "%i%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(false), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nlike_dict_scalar(&dict_array, "%a%r%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(true), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+    }
+
+    #[test]
+    fn test_dict_ilike_kernels() {
+        let data =
+            vec![Some("Earth"), Some("Fire"), Some("Water"), Some("Air"), None, Some("Air")];
+
+        let dict_array: DictionaryArray<Int8Type> = data.into_iter().collect();
+
+        assert_eq!(
+            ilike_dict_scalar(&dict_array, "air").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            ilike_dict_scalar(&dict_array, "wa%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            ilike_dict_scalar(&dict_array, "%R").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(false), Some(true), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            ilike_dict_scalar(&dict_array, "%I%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(true), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            ilike_dict_scalar(&dict_array, "%A%r%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(false), Some(true), Some(true), None, Some(true)]
+            ),
+        );
+    }
+
+    #[test]
+    fn test_dict_nilike_kernels() {
+        let data =
+            vec![Some("Earth"), Some("Fire"), Some("Water"), Some("Air"), None, Some("Air")];
+
+        let dict_array: DictionaryArray<Int8Type> = data.into_iter().collect();
+
+        assert_eq!(
+            nilike_dict_scalar(&dict_array, "air").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nilike_dict_scalar(&dict_array, "wa%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(false), Some(true), None, Some(true)]
+            ),
+        );
+
+        assert_eq!(
+            nilike_dict_scalar(&dict_array, "%R").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(true), Some(false), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nilike_dict_scalar(&dict_array, "%I%").unwrap(),
+            BooleanArray::from(
+                vec![Some(true), Some(false), Some(true), Some(false), None, Some(false)]
+            ),
+        );
+
+        assert_eq!(
+            nilike_dict_scalar(&dict_array, "%A%r%").unwrap(),
+            BooleanArray::from(
+                vec![Some(false), Some(true), Some(false), Some(false), None, Some(false)]
+            ),
+        );
+    }
 }