Skip to content

Commit c38f68a

Browse files
committed
feat(json): Add json_object_keys function.
1 parent 35b635f commit c38f68a

File tree

4 files changed

+236
-0
lines changed

4 files changed

+236
-0
lines changed

src/common/function/src/scalars/json.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use std::sync::Arc;
1616
mod json_get;
1717
mod json_is;
18+
mod json_object_keys;
1819
mod json_path_exists;
1920
mod json_path_match;
2021
mod json_to_string;
@@ -49,6 +50,7 @@ impl JsonFunction {
4950
registry.register(Arc::new(JsonIsArray));
5051
registry.register(Arc::new(JsonIsObject));
5152

53+
registry.register(Arc::new(json_object_keys::JsonObjectKeysFunction));
5254
registry.register(Arc::new(json_path_exists::JsonPathExistsFunction));
5355
registry.register(Arc::new(json_path_match::JsonPathMatchFunction));
5456
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright 2023 Greptime Team
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::fmt::{self, Display};
16+
17+
use common_query::error::{InvalidFuncArgsSnafu, Result, UnsupportedInputDataTypeSnafu};
18+
use common_query::prelude::Signature;
19+
use datafusion::logical_expr::Volatility;
20+
use datatypes::data_type::ConcreteDataType;
21+
use datatypes::prelude::VectorRef;
22+
use datatypes::scalars::ScalarVectorBuilder;
23+
use datatypes::value::{ListValue, ListValueRef};
24+
use datatypes::vectors::{ListVectorBuilder, MutableVector};
25+
use snafu::ensure;
26+
27+
use crate::function::{Function, FunctionContext};
28+
29+
/// Get all the keys from the JSON object.
30+
#[derive(Clone, Debug, Default)]
31+
pub struct JsonObjectKeysFunction;
32+
33+
const NAME: &str = "json_object_keys";
34+
35+
impl Function for JsonObjectKeysFunction {
36+
fn name(&self) -> &str {
37+
NAME
38+
}
39+
40+
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
41+
Ok(ConcreteDataType::list_datatype(
42+
ConcreteDataType::string_datatype(),
43+
))
44+
}
45+
46+
fn signature(&self) -> Signature {
47+
Signature::exact(
48+
vec![ConcreteDataType::json_datatype()],
49+
Volatility::Immutable,
50+
)
51+
}
52+
53+
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
54+
ensure!(
55+
columns.len() == 1,
56+
InvalidFuncArgsSnafu {
57+
err_msg: format!(
58+
"The length of the args is not correct, expect exactly one, have: {}",
59+
columns.len()
60+
),
61+
}
62+
);
63+
let jsons = &columns[0];
64+
65+
let size = jsons.len();
66+
let mut results =
67+
ListVectorBuilder::with_type_capacity(ConcreteDataType::string_datatype(), size);
68+
69+
for i in 0..size {
70+
let json = jsons.get_ref(i);
71+
match json.data_type() {
72+
// JSON data type uses binary vector
73+
ConcreteDataType::Binary(_) => {
74+
if let Ok(Some(json)) = json.as_binary()
75+
&& let Ok(json) = jsonb::from_slice(json)
76+
&& let Some(keys) = json.object_keys()
77+
&& let jsonb::Value::Array(keys) = keys
78+
{
79+
let result = ListValue::new(
80+
keys.iter().map(|k| k.to_string().into()).collect(),
81+
ConcreteDataType::string_datatype(),
82+
);
83+
results.push(Some(ListValueRef::Ref { val: &result }));
84+
} else {
85+
results.push(None)
86+
}
87+
}
88+
89+
_ => {
90+
return UnsupportedInputDataTypeSnafu {
91+
function: NAME,
92+
datatypes: columns.iter().map(|c| c.data_type()).collect::<Vec<_>>(),
93+
}
94+
.fail();
95+
}
96+
}
97+
}
98+
99+
Ok(results.to_vector())
100+
}
101+
}
102+
103+
impl Display for JsonObjectKeysFunction {
104+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105+
write!(f, "JSON_OBJECT_KEYS")
106+
}
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use std::sync::Arc;
112+
113+
use common_query::prelude::TypeSignature;
114+
use datatypes::value::{ListValueRef, Value};
115+
use datatypes::vectors::{BinaryVector, Vector};
116+
117+
use super::*;
118+
119+
#[test]
120+
fn test_json_object_keys_function() {
121+
let json_object_keys = JsonObjectKeysFunction;
122+
123+
assert_eq!("json_object_keys", json_object_keys.name());
124+
assert_eq!(
125+
ConcreteDataType::list_datatype(ConcreteDataType::string_datatype(),),
126+
json_object_keys
127+
.return_type(&[ConcreteDataType::list_datatype(
128+
ConcreteDataType::string_datatype(),
129+
)])
130+
.unwrap()
131+
);
132+
133+
assert!(matches!(json_object_keys.signature(),
134+
Signature {
135+
type_signature: TypeSignature::Exact(valid_types),
136+
volatility: Volatility::Immutable
137+
} if valid_types == vec![ConcreteDataType::json_datatype()],
138+
));
139+
140+
let json_strings = [
141+
Some(r#"{"a": {"b": 2}, "b": 2, "c": 3}"#.to_string()),
142+
Some(r#"{"a": 1, "b": [1,2,3]}"#.to_string()),
143+
Some(r#"[1,2,3]"#.to_string()),
144+
Some(r#"null"#.to_string()),
145+
];
146+
147+
let results = [
148+
Some(Value::List(ListValue::new(
149+
vec![
150+
Value::String(r#""a""#.into()),
151+
Value::String(r#""b""#.into()),
152+
Value::String(r#""c""#.into()),
153+
],
154+
ConcreteDataType::string_datatype(),
155+
))),
156+
Some(Value::List(ListValue::new(
157+
vec![
158+
Value::String(r#""a""#.into()),
159+
Value::String(r#""b""#.into()),
160+
],
161+
ConcreteDataType::string_datatype(),
162+
))),
163+
None,
164+
None,
165+
];
166+
167+
let jsonbs = json_strings
168+
.into_iter()
169+
.map(|s| s.map(|json| jsonb::parse_value(json.as_bytes()).unwrap().to_vec()))
170+
.collect::<Vec<_>>();
171+
172+
let json_vector = BinaryVector::from(jsonbs);
173+
let args: Vec<VectorRef> = vec![Arc::new(json_vector)];
174+
let vector = json_object_keys
175+
.eval(FunctionContext::default(), &args)
176+
.unwrap();
177+
178+
assert_eq!(4, vector.len());
179+
180+
let result = vector.get_ref(0);
181+
assert!(!result.is_null());
182+
let result_value = result.as_list().unwrap().unwrap();
183+
let ListValueRef::Indexed {
184+
vector: result_value,
185+
idx: _,
186+
} = result_value
187+
else {
188+
unreachable!()
189+
};
190+
191+
for (i, expected) in results.iter().enumerate() {
192+
match expected {
193+
Some(expected_value) => {
194+
assert_eq!(expected_value, &result_value.get(i));
195+
}
196+
None => {
197+
assert!(result_value.is_null(i));
198+
}
199+
}
200+
}
201+
}
202+
}

tests/cases/standalone/common/function/json/json.result

+25
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,28 @@ SELECT json_path_match(parse_json('null'), '$.a == 1');
104104
| |
105105
+------------------------------------------------------------+
106106

107+
--- json_object_keys ---
108+
SELECT json_object_keys(parse_json('{"a": 1, "b": [1, 2, 3]}')) AS keys;
109+
110+
+------------+
111+
| keys |
112+
+------------+
113+
| ["a", "b"] |
114+
+------------+
115+
116+
SELECT json_object_keys(parse_json('{"a": 1, "b": 2, "c": 3}')) AS keys;
117+
118+
+-----------------+
119+
| keys |
120+
+-----------------+
121+
| ["a", "b", "c"] |
122+
+-----------------+
123+
124+
SELECT json_object_keys(parse_json('[1, 2, 3]')) AS keys;
125+
126+
+------+
127+
| keys |
128+
+------+
129+
| |
130+
+------+
131+

tests/cases/standalone/common/function/json/json.sql

+7
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,10 @@ SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), '$.b[1 to last] >= 2')
2626
SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), 'null');
2727

2828
SELECT json_path_match(parse_json('null'), '$.a == 1');
29+
30+
--- json_object_keys ---
31+
SELECT json_object_keys(parse_json('{"a": 1, "b": [1, 2, 3]}')) AS keys;
32+
33+
SELECT json_object_keys(parse_json('{"a": 1, "b": 2, "c": 3}')) AS keys;
34+
35+
SELECT json_object_keys(parse_json('[1, 2, 3]')) AS keys;

0 commit comments

Comments
 (0)