Skip to content

Commit

Permalink
support json_depth
Browse files Browse the repository at this point in the history
  • Loading branch information
LemonLiTree committed Oct 7, 2023
1 parent f1e948e commit bccd9f5
Show file tree
Hide file tree
Showing 13 changed files with 521 additions and 4 deletions.
53 changes: 53 additions & 0 deletions be/src/util/jsonb_document.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,9 @@ class JsonbValue {
//Get the number of jsonbvalue elements
int length() const;

//Get the depth of jsonbvalue
int depth() const;

//Whether to include the jsonbvalue rhs
bool contains(JsonbValue* rhs) const;

Expand Down Expand Up @@ -1264,6 +1267,56 @@ inline int JsonbValue::length() const {
}
}

inline int JsonbValue::depth() const {
switch (type_) {
case JsonbType::T_Int8:
case JsonbType::T_Int16:
case JsonbType::T_Int32:
case JsonbType::T_Int64:
case JsonbType::T_Double:
case JsonbType::T_Float:
case JsonbType::T_Int128:
case JsonbType::T_String:
case JsonbType::T_Binary:
case JsonbType::T_Null:
case JsonbType::T_True:
case JsonbType::T_False: {
return 1;
}
case JsonbType::T_Object: {
int depth = 1;
int numElem = ((ObjectVal*)this)->numElem();
if (numElem == 0) return depth;

JsonbKeyValue* first_key = ((ObjectVal*)this)->getJsonbKeyValue(0);
JsonbValue* first_value =
((ObjectVal*)this)->find(first_key->getKeyStr(), first_key->klen());
int max_depth = depth + first_value->depth();

for (int i = 1; i < numElem; ++i) {
JsonbKeyValue* key = ((ObjectVal*)this)->getJsonbKeyValue(i);
JsonbValue* value = ((ObjectVal*)this)->find(key->getKeyStr(), key->klen());
int current_depth = depth + value->depth();
if (current_depth > max_depth) max_depth = current_depth;
}
return max_depth;
}
case JsonbType::T_Array: {
int depth = 1;
int numElem = ((ArrayVal*)this)->numElem();
if (numElem == 0) return depth;
int max_depth = depth + ((ArrayVal*)this)->get(0)->depth();
for (int i = 1; i < numElem; ++i) {
int current_depth = depth + ((ArrayVal*)this)->get(i)->depth();
if (current_depth > max_depth) max_depth = current_depth;
}
return max_depth;
}
default:
return 0;
}
}

inline bool JsonbValue::contains(JsonbValue* rhs) const {
switch (type_) {
case JsonbType::T_Int8:
Expand Down
51 changes: 51 additions & 0 deletions be/src/vec/functions/function_jsonb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,56 @@ struct JsonbContainsAndPathImpl {
}
};

class FunctionJsonbDepth : public IFunction {
public:
static constexpr auto name = "json_depth";
String get_name() const override { return name; }
static FunctionPtr create() { return std::make_shared<FunctionJsonbDepth>(); }

DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return make_nullable(std::make_shared<DataTypeInt32>());
}

size_t get_number_of_arguments() const override { return 1; }

bool use_default_implementation_for_nulls() const override { return false; }

Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) override {
DCHECK_GE(arguments.size(), 1);
auto jsonb_data_column = block.get_by_position(arguments[0]).column;

auto null_map = ColumnUInt8::create(input_rows_count, 0);
auto return_type = block.get_data_type(result);
MutableColumnPtr res = return_type->create_column();

//if(jsonb_data_column->)

for (size_t i = 0; i < input_rows_count; ++i) {
if (jsonb_data_column->is_null_at(i)) {
null_map->get_data()[i] = 1;
res->insert_data(nullptr, 0);
continue;
}

auto jsonb_value = jsonb_data_column->get_data_at(i);
// doc is NOT necessary to be deleted since JsonbDocument will not allocate memory
JsonbDocument* doc = JsonbDocument::createDocument(jsonb_value.data, jsonb_value.size);
JsonbValue* value = doc->getValue();
if (UNLIKELY(jsonb_value.size == 0 || !value)) {
null_map->get_data()[i] = 1;
res->insert_data(nullptr, 0);
continue;
}
auto depth = value->depth();
res->insert_data(const_cast<const char*>((char*)&depth), 0);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};

void register_function_jsonb(SimpleFunctionFactory& factory) {
factory.register_function<FunctionJsonbParse>(FunctionJsonbParse::name);
factory.register_alias(FunctionJsonbParse::name, FunctionJsonbParse::alias);
Expand Down Expand Up @@ -1353,6 +1403,7 @@ void register_function_jsonb(SimpleFunctionFactory& factory) {
factory.register_function<FunctionJsonbLength<JsonbLengthAndPathImpl>>();
factory.register_function<FunctionJsonbContains<JsonbContainsImpl>>();
factory.register_function<FunctionJsonbContains<JsonbContainsAndPathImpl>>();
factory.register_function<FunctionJsonbDepth>();
}

} // namespace doris::vectorized
94 changes: 94 additions & 0 deletions docs/en/docs/sql-manual/sql-functions/json-functions/json-depth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
{
"title": "JSON_DEPTH",
"language": "en"
}
---

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

## json_depth
### description
#### Syntax

`INT json_depth(JSON json_str)`

returns the maximum depth of a JSON document.

JSON_DEPTH() calculates depth according to the following rules:

* The depth of an empty array, or an empty object, or a scalar is 1.
* The depth of an nonempty array containing only elements of depth 1 is 2.
* The depth of an nonempty object containing only member values of depth 1 is 2.
* return NULL if the argument is NULL.

### example

```
mysql> select JSON_DEPTH('[]');
+------------------+
| json_depth('[]') |
+------------------+
| 1 |
+------------------+
1 row in set (0.07 sec)
mysql> select JSON_DEPTH('1');
+-----------------+
| json_depth('1') |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.09 sec)
mysql> select JSON_DEPTH('[1, 2]');
+----------------------+
| json_depth('[1, 2]') |
+----------------------+
| 2 |
+----------------------+
1 row in set (0.05 sec)
mysql> select JSON_DEPTH('[1, [2, 3]]');
+---------------------------+
| json_depth('[1, [2, 3]]') |
+---------------------------+
| 3 |
+---------------------------+
1 row in set (0.05 sec)
mysql> select JSON_DEPTH('{"x": {"y": 1}}');
+-------------------------------+
| json_depth('{"x": {"y": 1}}') |
+-------------------------------+
| 3 |
+-------------------------------+
1 row in set (0.06 sec)
mysql> select JSON_DEPTH(NULL);
+------------------+
| json_depth(NULL) |
+------------------+
| NULL |
+------------------+
1 row in set (0.08 sec)
```
### keywords
json,json_depth
1 change: 1 addition & 0 deletions docs/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@
"sql-manual/sql-functions/json-functions/json-valid",
"sql-manual/sql-functions/json-functions/json-contains",
"sql-manual/sql-functions/json-functions/json-length",
"sql-manual/sql-functions/json-functions/json-depth",
"sql-manual/sql-functions/json-functions/get-json-double",
"sql-manual/sql-functions/json-functions/get-json-int",
"sql-manual/sql-functions/json-functions/get-json-bigint",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
{
"title": "JSON_DEPTH",
"language": "zh-CN"
}
---

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

## json_depth
### description
#### Syntax

`INT json_depth(JSON json_str)`

返回 JSON 文档的最大深度。

JSON_DEPTH()根据以下规则计算深度:

* 空数组、空对象或标量的深度为 1。
* 仅包含深度为 1 的元素的非空数组的深度为2。
* 仅包含深度为 1 的成员值的非空对象的深度为2。
* NULL如果参数为 ,则该函数将返回NULL。

### example

```
mysql> select JSON_DEPTH('[]');
+------------------+
| json_depth('[]') |
+------------------+
| 1 |
+------------------+
1 row in set (0.07 sec)
mysql> select JSON_DEPTH('1');
+-----------------+
| json_depth('1') |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.09 sec)
mysql> select JSON_DEPTH('[1, 2]');
+----------------------+
| json_depth('[1, 2]') |
+----------------------+
| 2 |
+----------------------+
1 row in set (0.05 sec)
mysql> select JSON_DEPTH('[1, [2, 3]]');
+---------------------------+
| json_depth('[1, [2, 3]]') |
+---------------------------+
| 3 |
+---------------------------+
1 row in set (0.05 sec)
mysql> select JSON_DEPTH('{"x": {"y": 1}}');
+-------------------------------+
| json_depth('{"x": {"y": 1}}') |
+-------------------------------+
| 3 |
+-------------------------------+
1 row in set (0.06 sec)
mysql> select JSON_DEPTH(NULL);
+------------------+
| json_depth(NULL) |
+------------------+
| NULL |
+------------------+
1 row in set (0.08 sec)
```
### keywords
json,json_depth
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
import org.apache.doris.nereids.trees.expressions.functions.scalar.Instr;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonArray;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonContains;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonDepth;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtract;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonInsert;
import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonLength;
Expand Down Expand Up @@ -292,6 +293,7 @@
import org.apache.doris.nereids.trees.expressions.functions.scalar.RoundBankers;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rpad;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rtrim;
import org.apache.doris.nereids.trees.expressions.functions.scalar.RunningDifference;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SecToTime;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Second;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondCeil;
Expand Down Expand Up @@ -564,9 +566,6 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(JsonQuote.class, "json_quote"),
scalar(JsonUnQuote.class, "json_unquote"),
scalar(JsonExtract.class, "json_extract"),
scalar(JsonInsert.class, "json_insert"),
scalar(JsonReplace.class, "json_replace"),
scalar(JsonSet.class, "json_set"),
scalar(JsonbExistsPath.class, "json_exists_path"),
scalar(JsonbExistsPath.class, "jsonb_exists_path"),
scalar(JsonbExtract.class, "jsonb_extract"),
Expand Down Expand Up @@ -611,7 +610,7 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(JsonbType.class, "json_type"),
scalar(JsonbType.class, "jsonb_type"),
scalar(JsonLength.class, "json_length"),
scalar(JsonContains.class, "json_contains"),
scalar(JsonContains.class, "json_conatins"),
scalar(LastDay.class, "last_day"),
scalar(Least.class, "least"),
scalar(Left.class, "left"),
Expand Down Expand Up @@ -691,6 +690,7 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(RoundBankers.class, "round_bankers"),
scalar(Rpad.class, "rpad"),
scalar(Rtrim.class, "rtrim"),
scalar(RunningDifference.class, "running_difference"),
scalar(Second.class, "second"),
scalar(SecondCeil.class, "second_ceil"),
scalar(SecondFloor.class, "second_floor"),
Expand Down
Loading

0 comments on commit bccd9f5

Please sign in to comment.