This is an automated email from the ASF dual-hosted git repository.
HappenLee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new c5109e709ee [fix](be) Fix json contains duplicate array candidates
(#63301)
c5109e709ee is described below
commit c5109e709ee480404b141919469acc6e695fcc80
Author: Jerry Hu <[email protected]>
AuthorDate: Mon May 18 11:45:08 2026 +0800
[fix](be) Fix json contains duplicate array candidates (#63301)
Problem Summary: `json_contains('[1,1,1]', '[1,1]')` should return true,
but the previous JSONB array containment logic counted matching target
elements and compared that count with the candidate array length.
Duplicate candidate elements therefore produced incorrect false results.
This change checks each candidate array element independently against
the target array, matching non-consuming containment semantics.
### Release note
Fix `json_contains` returning false when the candidate array contains
duplicate elements already present in the target array.
### Check List (For Author)
- Test: Unit Test
- `./run-be-ut.sh --run --filter=JsonbContainsTest.* -j 32`
- `build-support/check-format.sh`
- `git diff --check 66dbb85fe3deaf9069bc58f78446fe998ba5810b..HEAD`
- `build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN` (failed
because clang-tidy could not analyze existing headers in this
environment: `be/src/core/types.h` has an unmatched `NOLINTEND`, and the
toolchain could not resolve `stddef.h`/intrinsic headers)
- Behavior changed: Yes
- `json_contains` now follows non-consuming array containment semantics
for duplicate candidate elements.
- Does this need documentation: No
---
be/src/util/jsonb_document.h | 48 +++++++++++++++------------
be/test/util/jsonb_contains_test.cpp | 64 ++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 21 deletions(-)
diff --git a/be/src/util/jsonb_document.h b/be/src/util/jsonb_document.h
index 7750a426a82..cb9425744ff 100644
--- a/be/src/util/jsonb_document.h
+++ b/be/src/util/jsonb_document.h
@@ -1002,6 +1002,30 @@ struct ArrayVal : public ContainerVal {
const_iterator end() const { return const_iterator((pointer)(payload +
size)); }
};
+namespace jsonb_detail {
+
+inline bool array_contains_value(const ArrayVal* target_array, const
JsonbValue* candidate) {
+ const int target_num = target_array->numElem();
+ for (int i = 0; i < target_num; ++i) {
+ if (target_array->get(i)->contains(candidate)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+inline bool array_contains_array(const ArrayVal* target_array, const ArrayVal*
candidate_array) {
+ const int candidate_num = candidate_array->numElem();
+ for (int i = 0; i < candidate_num; ++i) {
+ if (!array_contains_value(target_array, candidate_array->get(i))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace jsonb_detail
+
inline const JsonbValue* JsonbDocument::createValue(const char* pb, size_t
size) {
if (!pb || size < sizeof(JsonbHeader) + sizeof(JsonbValue)) {
return nullptr;
@@ -1153,29 +1177,11 @@ inline bool JsonbValue::contains(const JsonbValue* rhs)
const {
return false;
}
case JsonbType::T_Array: {
- int lhs_num = unpack<ArrayVal>()->numElem();
+ const auto* lhs_array = unpack<ArrayVal>();
if (rhs->isArray()) {
- int rhs_num = rhs->unpack<ArrayVal>()->numElem();
- if (rhs_num > lhs_num) {
- return false;
- }
- int contains_num = 0;
- for (int i = 0; i < lhs_num; ++i) {
- for (int j = 0; j < rhs_num; ++j) {
- if
(unpack<ArrayVal>()->get(i)->contains(rhs->unpack<ArrayVal>()->get(j))) {
- contains_num++;
- break;
- }
- }
- }
- return contains_num == rhs_num;
+ return jsonb_detail::array_contains_array(lhs_array,
rhs->unpack<ArrayVal>());
}
- for (int i = 0; i < lhs_num; ++i) {
- if (unpack<ArrayVal>()->get(i)->contains(rhs)) {
- return true;
- }
- }
- return false;
+ return jsonb_detail::array_contains_value(lhs_array, rhs);
}
case JsonbType::T_Object: {
if (rhs->isObject()) {
diff --git a/be/test/util/jsonb_contains_test.cpp
b/be/test/util/jsonb_contains_test.cpp
new file mode 100644
index 00000000000..34ac6382c9b
--- /dev/null
+++ b/be/test/util/jsonb_contains_test.cpp
@@ -0,0 +1,64 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include <string_view>
+
+#include "core/value/jsonb_value.h"
+#include "util/jsonb_document.h"
+
+namespace doris {
+namespace {
+
+void expect_jsonb_contains(std::string_view target_json, std::string_view
candidate_json,
+ bool expected) {
+ JsonBinaryValue target;
+ auto st = target.from_json_string(target_json.data(), target_json.size());
+ ASSERT_TRUE(st.ok()) << st.to_string();
+
+ JsonBinaryValue candidate;
+ st = candidate.from_json_string(candidate_json.data(),
candidate_json.size());
+ ASSERT_TRUE(st.ok()) << st.to_string();
+
+ const JsonbDocument* target_doc = nullptr;
+ st = JsonbDocument::checkAndCreateDocument(target.value(), target.size(),
&target_doc);
+ ASSERT_TRUE(st.ok()) << st.to_string();
+
+ const JsonbDocument* candidate_doc = nullptr;
+ st = JsonbDocument::checkAndCreateDocument(candidate.value(),
candidate.size(), &candidate_doc);
+ ASSERT_TRUE(st.ok()) << st.to_string();
+
+ EXPECT_EQ(target_doc->getValue()->contains(candidate_doc->getValue()),
expected);
+}
+
+} // namespace
+
+TEST(JsonbContainsTest, ArrayCandidateDoesNotConsumeTargetElements) {
+ expect_jsonb_contains("[1,1,1]", "[1,1]", true);
+ expect_jsonb_contains("[1]", "[1,1]", true);
+ expect_jsonb_contains("[1,2,3]", "[2,1]", true);
+
+ expect_jsonb_contains("[1,2,3]", "[2,4]", false);
+}
+
+TEST(JsonbContainsTest, ArrayCandidateUsesRecursiveContains) {
+ expect_jsonb_contains(R"([{"a":1,"b":2},[3,4]])", R"([{"a":1},[4]])",
true);
+ expect_jsonb_contains(R"([{"a":1},[3,4]])", R"([{"a":2}])", false);
+}
+
+} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]