blob: 08c7aa972063f9973a4f55d19b5dfe4dc3e10d4b [file]
// 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.
#pragma once
#include <parallel_hashmap/phmap.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "common/status.h"
#include "core/column/column.h"
#include "core/column/column_variant.h"
#include "storage/segment/variant/nested_group_path.h"
#include "util/json/path_in_data.h"
namespace doris {
struct JsonbValue;
} // namespace doris
namespace doris::segment_v2 {
/**
* English comment: NestedGroup is a storage-layer structure used to persist array<object>
* with shared offsets to preserve per-element field associations.
*
* This is intentionally independent from ColumnVariant's in-memory nested structures.
*/
struct NestedGroup {
// Full array path for top-level group (e.g. "voltage.list"),
// and relative path for nested groups within another NestedGroup (e.g. "cells").
PathInData path;
// Offsets per parent row (or per parent element for nested groups).
MutableColumnPtr offsets;
// Scalar (or flattened object) children under this array path.
phmap::flat_hash_map<PathInData, ColumnVariant::Subcolumn, PathInData::Hash> children;
// Sparse row positions for each child subcolumn value in flattened element space.
// When present for a child path, children[path] stores only non-missing values and
// child_rowids[path][i] is the logical row index for children[path][i].
phmap::flat_hash_map<PathInData, std::vector<uint32_t>, PathInData::Hash> child_rowids;
// Nested array<object> groups under this array path.
phmap::flat_hash_map<PathInData, std::shared_ptr<NestedGroup>, PathInData::Hash> nested_groups;
size_t current_flat_size = 0;
bool is_disabled = false;
enum struct StructureType { UNKNOWN, SCALAR, ARRAY, OBJECT };
StructureType expected_type = StructureType::UNKNOWN;
void ensure_offsets();
};
using NestedGroupsMap =
phmap::flat_hash_map<PathInData, std::shared_ptr<NestedGroup>, PathInData::Hash>;
// NestedGroup marker/path constants are defined in nested_group_path.h
/**
* English comment: Build NestedGroup(s) from JSONB columns at storage finalize stage.
* The builder scans JSONB values and only expands array<object>.
*/
class NestedGroupBuilder {
public:
NestedGroupBuilder() = default;
// Build NestedGroups from a JSONB column. base_path is the path of this JSONB column
// in ColumnVariant (empty for root JSONB).
Status build_from_jsonb(const ColumnPtr& jsonb_column, const PathInData& base_path,
NestedGroupsMap& nested_groups, size_t num_rows);
// Convenience overload for root JSONB.
Status build_from_jsonb(const ColumnPtr& jsonb_column, NestedGroupsMap& nested_groups,
size_t num_rows) {
return build_from_jsonb(jsonb_column, PathInData {}, nested_groups, num_rows);
}
// Collect paths that have ARRAY<OBJECT> vs non-array structural conflicts.
// Returned paths are de-duplicated and sorted.
void collect_conflict_paths(std::vector<std::string>* out_paths) const;
void set_max_depth(size_t max_depth) { _max_depth = max_depth; }
private:
using AppendedPathCache =
phmap::flat_hash_map<std::string, PathInData, phmap::priv::StringHashEqT<char>::Hash,
phmap::priv::StringHashEqT<char>::Eq>;
enum class PathShape { ARRAY_OBJECT, NON_ARRAY };
struct PathShapeState {
bool has_array_object = false;
bool has_non_array = false;
};
const PathInData& _normalize_group_path(const PathInData& path) const;
void _record_path_shape(const PathInData& path, PathShape shape);
PathInData _append_path_cached(const PathInData& base, std::string_view suffix);
Status _process_jsonb_value(const doris::JsonbValue* value, const PathInData& current_path,
NestedGroupsMap& nested_groups, size_t row_idx, size_t depth);
Status _process_object_as_paths(const doris::JsonbValue* obj_value,
const PathInData& current_prefix, NestedGroup& group,
size_t element_flat_idx, size_t depth,
const PathInData& group_absolute_path);
Status _process_array_of_objects(const doris::JsonbValue* arr_value, NestedGroup& group,
size_t parent_row_idx, size_t depth,
const PathInData& group_absolute_path);
Status _finalize_group(NestedGroup& group);
// Process nested object field by recursively flattening into dotted paths.
Status _process_object_field(const doris::JsonbValue* obj_value, const PathInData& next_prefix,
NestedGroup& group, size_t element_flat_idx, size_t depth,
const PathInData& group_absolute_path);
// Process nested array<object> field within a NestedGroup.
Status _process_nested_array_field(const doris::JsonbValue* arr_value,
const PathInData& next_prefix, NestedGroup& group,
size_t element_flat_idx, size_t depth,
const PathInData& group_absolute_path);
// Process scalar field and insert into subcolumn.
Status _process_scalar_field(const doris::JsonbValue* value, const PathInData& next_prefix,
NestedGroup& group, size_t element_flat_idx);
// Return true if this array can be treated as array<object> (nulls allowed).
bool _is_array_of_objects(const doris::JsonbValue* arr_value) const;
// Convert a JsonbValue to a scalar Field (or NULL Field). Container types are not supported.
Status _jsonb_to_field(const doris::JsonbValue* value, Field& out) const;
// Conflict policy placeholder. Returns true if the current value should be discarded.
bool _handle_conflict(NestedGroup& group, bool is_array_object) const;
private:
size_t _max_depth = 0; // 0 = unlimited
phmap::flat_hash_map<PathInData, PathShapeState, PathInData::Hash> _path_shape_states;
phmap::flat_hash_set<std::string> _conflict_paths;
phmap::flat_hash_map<PathInData, AppendedPathCache, PathInData::Hash> _appended_path_cache;
};
} // namespace doris::segment_v2