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

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new a85f3466188 branch-4.0: [feat](function) Add new function of 
strip_null_value #57386 (#57689)
a85f3466188 is described below

commit a85f346618818854035f00c8698121ccce6e7204
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Nov 4 21:32:27 2025 +0800

    branch-4.0: [feat](function) Add new function of strip_null_value #57386 
(#57689)
    
    Cherry-picked from #57386
    
    Co-authored-by: Jerry Hu <[email protected]>
---
 be/src/vec/functions/function_jsonb.cpp            | 67 ++++++++++++++++
 .../doris/catalog/BuiltinScalarFunctions.java      |  2 +
 .../functions/scalar/StripNullValue.java           | 67 ++++++++++++++++
 .../expressions/visitor/ScalarFunctionVisitor.java |  5 ++
 .../test_strip_null_value.out                      | 28 +++++++
 .../test_strip_null_value.groovy                   | 88 ++++++++++++++++++++++
 6 files changed, 257 insertions(+)

diff --git a/be/src/vec/functions/function_jsonb.cpp 
b/be/src/vec/functions/function_jsonb.cpp
index aaf6a2f2295..c4ab4efef81 100644
--- a/be/src/vec/functions/function_jsonb.cpp
+++ b/be/src/vec/functions/function_jsonb.cpp
@@ -3028,6 +3028,71 @@ private:
     }
 };
 
+class FunctionStripNullValue : public IFunction {
+public:
+    static constexpr auto name = "strip_null_value";
+    static FunctionPtr create() { return 
std::make_shared<FunctionStripNullValue>(); }
+
+    String get_name() const override { return name; }
+    bool is_variadic() const override { return false; }
+    size_t get_number_of_arguments() const override { return 1; }
+
+    bool use_default_implementation_for_nulls() const override { return false; 
}
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        return make_nullable(std::make_shared<DataTypeJsonb>());
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        uint32_t result, size_t input_rows_count) const 
override {
+        const auto& arg_column = block.get_by_position(arguments[0]).column;
+        const ColumnString* json_column = nullptr;
+        const NullMap* json_null_map = nullptr;
+        if (arg_column->is_nullable()) {
+            const auto& nullable_col = assert_cast<const 
ColumnNullable&>(*arg_column);
+            json_column = assert_cast<const 
ColumnString*>(&nullable_col.get_nested_column());
+            json_null_map = &nullable_col.get_null_map_data();
+        } else {
+            json_column = assert_cast<const ColumnString*>(arg_column.get());
+        }
+
+        auto return_data_type = 
make_nullable(std::make_shared<DataTypeJsonb>());
+        auto result_column = return_data_type->create_column();
+
+        auto& result_nullmap = 
assert_cast<ColumnNullable&>(*result_column).get_null_map_data();
+        auto& result_data_col = assert_cast<ColumnString&>(
+                
assert_cast<ColumnNullable&>(*result_column).get_nested_column());
+
+        result_nullmap.resize_fill(input_rows_count, 0);
+        for (size_t i = 0; i != input_rows_count; ++i) {
+            if (json_null_map && (*json_null_map)[i]) {
+                result_nullmap[i] = 1;
+                result_data_col.insert_default();
+                continue;
+            }
+            JsonbDocument* json_doc = nullptr;
+            const auto& json_str = json_column->get_data_at(i);
+            RETURN_IF_ERROR(
+                    JsonbDocument::checkAndCreateDocument(json_str.data, 
json_str.size, &json_doc));
+            if (json_doc) [[likely]] {
+                if (json_doc->getValue()->isNull()) {
+                    result_nullmap[i] = 1;
+                    result_data_col.insert_default();
+                } else {
+                    result_nullmap[i] = 0;
+                    result_data_col.insert_data(json_str.data, json_str.size);
+                }
+            } else {
+                result_nullmap[i] = 1;
+                result_data_col.insert_default();
+            }
+        }
+
+        block.get_by_position(result).column = std::move(result_column);
+        return Status::OK();
+    }
+};
+
 void register_function_jsonb(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionJsonbParse>(FunctionJsonbParse::name);
     factory.register_alias(FunctionJsonbParse::name, 
FunctionJsonbParse::alias);
@@ -3079,6 +3144,8 @@ void register_function_jsonb(SimpleFunctionFactory& 
factory) {
 
     factory.register_function<FunctionJsonbRemove>();
     factory.register_alias(FunctionJsonbRemove::name, 
FunctionJsonbRemove::alias);
+
+    factory.register_function<FunctionStripNullValue>();
 }
 
 } // namespace doris::vectorized
\ No newline at end of file
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
index cefc5dc6eb5..8b5ea5e5b79 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
@@ -468,6 +468,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StripNullValue;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBinary;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
@@ -1009,6 +1010,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(StY.class, "st_y"),
             scalar(StartsWith.class, "starts_with"),
             scalar(Strcmp.class, "strcmp"),
+            scalar(StripNullValue.class, "strip_null_value"),
             scalar(StrToDate.class, "str_to_date"),
             scalar(StrToMap.class, "str_to_map"),
             scalar(SubBinary.class, "sub_binary"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StripNullValue.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StripNullValue.java
new file mode 100644
index 00000000000..58dbfed4c3b
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StripNullValue.java
@@ -0,0 +1,67 @@
+// 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.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.JsonType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'strip_null_value'.
+ */
+public class StripNullValue extends ScalarFunction implements 
ExplicitlyCastableSignature, AlwaysNullable {
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(JsonType.INSTANCE).args(JsonType.INSTANCE)
+    );
+
+    public StripNullValue(Expression arg) {
+        super("strip_null_value", arg);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private StripNullValue(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public StripNullValue withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new StripNullValue(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitStripNullValue(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
index 5e4ce74823f..4727d9a773f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
@@ -469,6 +469,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StripNullValue;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBinary;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
@@ -2432,6 +2433,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(strcmp, context);
     }
 
+    default R visitStripNullValue(StripNullValue stripNullValue, C context) {
+        return visitScalarFunction(stripNullValue, context);
+    }
+
     default R visitVersion(Version version, C context) {
         return visitScalarFunction(version, context);
     }
diff --git 
a/regression-test/data/query_p0/sql_functions/conditional_functions/test_strip_null_value.out
 
b/regression-test/data/query_p0/sql_functions/conditional_functions/test_strip_null_value.out
new file mode 100644
index 00000000000..e282f5526c7
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/conditional_functions/test_strip_null_value.out
@@ -0,0 +1,28 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !test --
+1      "null"  30
+2      "Bob"   \N
+3      \N      \N
+4      \N      \N
+
+-- !test2 --
+1      "Alice2"        \N
+2      "Bob"   \N
+3      "Jack"  28
+4      "Jim"   33
+
+-- !test2 --
+1      "a"     "a"
+2      \N      \N
+3      \N      "c"
+4      \N      \N
+
+-- !test3 --
+1      "aaa"   123     "aaa"   123
+2      "bbbb"  "a123"  "bbbb"  "a123"
+3      \N      \N      \N      \N
+4      \N      \N      \N      7890
+
+-- !const --
+"aaa"  \N      "ccc"   \N      \N
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/conditional_functions/test_strip_null_value.groovy
 
b/regression-test/suites/query_p0/sql_functions/conditional_functions/test_strip_null_value.groovy
new file mode 100644
index 00000000000..6308e318dc8
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/conditional_functions/test_strip_null_value.groovy
@@ -0,0 +1,88 @@
+// 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.
+
+suite("test_strip_null_value") {
+    sql """DROP TABLE IF EXISTS `test_strip_null_value_table`;"""
+    sql """CREATE TABLE test_strip_null_value_table (
+            id INT,
+            json_value JSON,
+            json_value_non_null JSON not null
+        ) PROPERTIES ("replication_num"="1");"""
+
+    sql """INSERT INTO test_strip_null_value_table VALUES
+        (1, '{"name": "null", "age": 30, "a": "aaa", "b": "b", "c": null}', 
'{"name": "Alice2", "age": null, "a": 123, "c": null}'),
+        (2, '{"name": "Bob", "age": null, "b": "bbbb", "c": 23423, "d": 
null}', '{"name": "Bob", "age": null, "b": "a123", "c": null, "d": 9993}'),
+        (3, null, '{"name": "Jack", "age": 28, "a": null, "b": null, "c": 
null}'),
+        (4, null, '{"name": "Jim", "age": 33, "a": 1234, "b": 4567, "d": 
7890}');
+    """
+
+    qt_test """
+        select id, 
+            strip_null_value(json_extract(json_value, '\$.name')) striped,
+            strip_null_value(json_extract(json_value, '\$.age')) as striped2
+        from test_strip_null_value_table order by 1;
+    """
+
+    qt_test2 """
+        select id,
+            strip_null_value(json_extract(json_value_non_null, '\$.name')) 
striped,
+            strip_null_value(json_extract(json_value_non_null, '\$.age')) 
striped2 
+        from test_strip_null_value_table order by 1;
+    """
+
+    sql """DROP TABLE IF EXISTS `test_strip_null_value_paths_table`;"""
+    sql """CREATE TABLE test_strip_null_value_paths_table (
+            id INT,
+            path string,
+            path_not_null string not null
+        ) PROPERTIES ("replication_num"="1");"""
+
+    sql """INSERT INTO test_strip_null_value_paths_table VALUES
+        (1, '\$.a', '\$.a'),
+        (2, '\$.b', '\$.b'),
+        (3, null, '\$.c'),
+        (4, null, '\$.d');
+    """
+
+    qt_test2 """
+        select
+            id,
+            strip_null_value(json_extract('{"a": "a", "b": null, "c": "c", 
"d": null}', path)) striped1, 
+            strip_null_value(json_extract('{"a": "a", "b": null, "c": "c", 
"d": null}', path_not_null)) striped2
+        from test_strip_null_value_paths_table order by 1;
+    """
+
+    qt_test3 """
+        select
+            t1.id,
+            strip_null_value(json_extract(t1.json_value, t2.path)) striped1,
+            strip_null_value(json_extract(t1.json_value_non_null, t2.path)) 
striped2,
+            strip_null_value(json_extract(t1.json_value, t2.path_not_null)) 
striped3,
+            strip_null_value(json_extract(t1.json_value_non_null, 
t2.path_not_null)) striped4
+        from test_strip_null_value_table t1
+        inner join test_strip_null_value_paths_table t2 on t1.id = t2.id
+        order by t1.id;
+    """
+
+    qt_const """
+        select strip_null_value(json_extract('{"a": "aaa", "b": null, "c": 
"ccc", "d": null}', '\$.a')) as striped1,
+               strip_null_value(json_extract('{"a": "aaa", "b": null, "c": 
"ccc", "d": null}', '\$.b')) as striped2,
+               strip_null_value(json_extract('{"a": "aaa", "b": null, "c": 
"ccc", "d": null}', '\$.c')) as striped3,
+               strip_null_value(json_extract('{"a": "aaa", "b": null, "c": 
"ccc", "d": null}', '\$.d')) as striped4,
+               strip_null_value(NULL) as striped5;
+    """
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to