micromegas_datafusion_extensions/jsonb/
keys.rs1use datafusion::arrow::array::{
2 Array, ArrayRef, DictionaryArray, GenericBinaryArray, Int32Array, ListBuilder, StringBuilder,
3};
4use datafusion::arrow::datatypes::{DataType, Field, Int32Type};
5use datafusion::common::{Result, internal_err};
6use datafusion::error::DataFusionError;
7use datafusion::logical_expr::{
8 ColumnarValue, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility,
9};
10use jsonb::RawJsonb;
11use std::collections::HashMap;
12use std::sync::Arc;
13
14#[derive(Debug, PartialEq, Eq, Hash)]
20pub struct JsonbObjectKeys {
21 signature: Signature,
22}
23
24impl JsonbObjectKeys {
25 pub fn new() -> Self {
26 Self {
27 signature: Signature::any(1, Volatility::Immutable),
28 }
29 }
30}
31
32impl Default for JsonbObjectKeys {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38fn extract_keys_from_jsonb(jsonb_bytes: &[u8]) -> Result<Option<Vec<String>>> {
39 let jsonb = RawJsonb::new(jsonb_bytes);
40 match jsonb.object_keys() {
41 Ok(Some(keys_jsonb)) => {
42 let keys_raw = keys_jsonb.as_raw();
44 match keys_raw.array_values() {
45 Ok(Some(values)) => {
46 let mut keys = Vec::with_capacity(values.len());
47 for value in values {
48 let raw = value.as_raw();
49 match raw.as_str() {
50 Ok(Some(s)) => keys.push(s.to_string()),
51 Ok(None) => {
52 return Ok(None);
54 }
55 Err(e) => return Err(DataFusionError::External(e.into())),
56 }
57 }
58 Ok(Some(keys))
59 }
60 Ok(None) => Ok(Some(Vec::new())), Err(e) => Err(DataFusionError::External(e.into())),
62 }
63 }
64 Ok(None) => Ok(None), Err(e) => Err(DataFusionError::External(e.into())),
66 }
67}
68
69impl ScalarUDFImpl for JsonbObjectKeys {
70 fn name(&self) -> &str {
71 "jsonb_object_keys"
72 }
73
74 fn signature(&self) -> &Signature {
75 &self.signature
76 }
77
78 fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
79 Ok(DataType::Dictionary(
80 Box::new(DataType::Int32),
81 Box::new(DataType::List(Arc::new(Field::new_list_field(
82 DataType::Utf8,
83 true,
84 )))),
85 ))
86 }
87
88 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
89 let args = ColumnarValue::values_to_arrays(&args.args)?;
90 if args.len() != 1 {
91 return internal_err!("wrong number of arguments to jsonb_object_keys()");
92 }
93
94 match args[0].data_type() {
95 DataType::Binary => {
96 let binary_array = args[0]
97 .as_any()
98 .downcast_ref::<GenericBinaryArray<i32>>()
99 .ok_or_else(|| {
100 DataFusionError::Internal("error casting to binary array".into())
101 })?;
102
103 let result = build_dict_list_array(binary_array.len(), |i| {
104 if binary_array.is_null(i) {
105 Ok(None)
106 } else {
107 extract_keys_from_jsonb(binary_array.value(i))
108 }
109 })?;
110 Ok(ColumnarValue::Array(result))
111 }
112 DataType::Dictionary(_, value_type)
113 if matches!(value_type.as_ref(), DataType::Binary) =>
114 {
115 let dict_array = args[0]
116 .as_any()
117 .downcast_ref::<DictionaryArray<Int32Type>>()
118 .ok_or_else(|| {
119 DataFusionError::Internal("error casting dictionary array".into())
120 })?;
121
122 let binary_values = dict_array
123 .values()
124 .as_any()
125 .downcast_ref::<GenericBinaryArray<i32>>()
126 .ok_or_else(|| {
127 DataFusionError::Internal("dictionary values are not a binary array".into())
128 })?;
129
130 let result = build_dict_list_array(dict_array.len(), |i| {
131 if dict_array.is_null(i) {
132 Ok(None)
133 } else {
134 let key_index = dict_array.keys().value(i) as usize;
135 if key_index < binary_values.len() {
136 extract_keys_from_jsonb(binary_values.value(key_index))
137 } else {
138 internal_err!("Dictionary key index out of bounds in jsonb_object_keys")
139 }
140 }
141 })?;
142 Ok(ColumnarValue::Array(result))
143 }
144 _ => internal_err!(
145 "jsonb_object_keys: unsupported input type, expected Binary or Dictionary<Int32, Binary>"
146 ),
147 }
148 }
149}
150
151fn build_dict_list_array<F>(len: usize, mut get_keys: F) -> Result<ArrayRef>
155where
156 F: FnMut(usize) -> Result<Option<Vec<String>>>,
157{
158 let mut unique_lists: HashMap<Vec<String>, i32> = HashMap::new();
160 let mut key_indices: Vec<Option<i32>> = Vec::with_capacity(len);
161 let mut ordered_lists: Vec<Vec<String>> = Vec::new();
162
163 for i in 0..len {
165 let keys = get_keys(i)?;
166 match keys {
167 Some(key_list) => {
168 if let Some(idx) = unique_lists.get(&key_list) {
169 key_indices.push(Some(*idx));
170 } else {
171 let idx = ordered_lists.len() as i32;
172 unique_lists.insert(key_list.clone(), idx);
173 key_indices.push(Some(idx));
174 ordered_lists.push(key_list);
175 }
176 }
177 None => {
178 key_indices.push(None);
180 }
181 }
182 }
183
184 let mut list_builder = ListBuilder::new(StringBuilder::new());
186 for keys in &ordered_lists {
187 for key in keys {
188 list_builder.values().append_value(key);
189 }
190 list_builder.append(true);
191 }
192 let values_array = Arc::new(list_builder.finish());
193
194 let keys_array = Int32Array::from(key_indices);
196
197 let dict_array =
199 DictionaryArray::<Int32Type>::try_new(keys_array, values_array).map_err(|e| {
200 DataFusionError::Internal(format!("Failed to create dictionary array: {e}"))
201 })?;
202
203 Ok(Arc::new(dict_array))
204}
205
206pub fn make_jsonb_object_keys_udf() -> ScalarUDF {
208 ScalarUDF::new_from_impl(JsonbObjectKeys::new())
209}