Coverage Report

Created: 2026-08-18 13:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/variant_inverted_index_search.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <CLucene.h>
21
#include <gen_cpp/Exprs_types.h>
22
23
#include <cstdint>
24
#include <functional>
25
#include <map>
26
#include <memory>
27
#include <roaring/roaring.hh>
28
#include <string>
29
#include <unordered_map>
30
#include <unordered_set>
31
#include <utility>
32
#include <vector>
33
34
#include "common/status.h"
35
#include "core/block/columns_with_type_and_name.h"
36
#include "core/data_type/data_type.h"
37
#include "storage/index/index_query_context.h"
38
#include "storage/index/inverted/inverted_index_cache.h"
39
#include "storage/index/inverted/inverted_index_iterator.h"
40
#include "storage/index/inverted/inverted_index_reader.h"
41
#include "storage/index/inverted/query_v2/query.h"
42
#include "storage/index/inverted/query_v2/weight.h"
43
#include "storage/olap_common.h"
44
#include "storage/segment/column_reader.h"
45
46
namespace doris::segment_v2::inverted_index::query_v2 {
47
class Query;
48
}
49
50
namespace doris::segment_v2 {
51
class NestedGroupReadProvider;
52
struct NestedGroupReader;
53
class VariantColumnReader;
54
} // namespace doris::segment_v2
55
56
namespace doris {
57
58
using namespace doris::segment_v2;
59
60
class FunctionSearch;
61
class IndexExecContext;
62
63
using SearchLeafQueryMapper = std::function<Status(
64
        const std::string&, std::shared_ptr<segment_v2::inverted_index::query_v2::Query>*)>;
65
66
enum class SearchFieldBindingState {
67
    BOUND,
68
    MISSING_IN_SEGMENT,
69
};
70
71
enum class SearchFieldExecutionMode {
72
    UNBOUND,
73
    CLUCENE,
74
    DIRECT_INDEX,
75
    SNII_NATIVE,
76
};
77
78
struct FieldReaderBinding {
79
    std::string logical_field_name;
80
    std::string stored_field_name;
81
    std::wstring stored_field_wstr;
82
    DataTypePtr column_type;
83
    InvertedIndexQueryType query_type;
84
    InvertedIndexReaderPtr inverted_reader;
85
    std::shared_ptr<lucene::index::IndexReader> lucene_reader;
86
    std::map<std::string, std::string> index_properties;
87
    std::string binding_key;
88
    std::string analyzer_key;
89
    SearchFieldBindingState state = SearchFieldBindingState::MISSING_IN_SEGMENT;
90
    SearchFieldExecutionMode execution_mode = SearchFieldExecutionMode::UNBOUND;
91
92
55
    bool is_bound() const {
93
55
        return state == SearchFieldBindingState::BOUND || inverted_reader != nullptr ||
94
55
               lucene_reader != nullptr;
95
55
    }
96
8
    bool use_direct_index_reader() const {
97
8
        return is_bound() && execution_mode == SearchFieldExecutionMode::DIRECT_INDEX;
98
8
    }
99
23
    bool use_snii_native_reader() const {
100
23
        return is_bound() && execution_mode == SearchFieldExecutionMode::SNII_NATIVE;
101
23
    }
102
};
103
104
class FieldReaderResolver {
105
public:
106
    FieldReaderResolver(
107
            const std::unordered_map<std::string, IndexFieldNameAndTypePair>& data_type_with_names,
108
            const std::unordered_map<std::string, IndexIterator*>& iterators,
109
            std::shared_ptr<IndexQueryContext> context,
110
            const std::vector<TSearchFieldBinding>& field_bindings = {});
111
112
    Status resolve(const std::string& field_name, InvertedIndexQueryType query_type,
113
                   FieldReaderBinding* binding);
114
115
59
    bool is_variant_subcolumn(const std::string& field_name) const {
116
59
        return _variant_subcolumn_fields.count(field_name) > 0;
117
59
    }
118
119
11
    const std::vector<std::shared_ptr<lucene::index::IndexReader>>& readers() const {
120
11
        return _readers;
121
11
    }
122
123
    const std::unordered_map<std::string, std::shared_ptr<lucene::index::IndexReader>>&
124
10
    reader_bindings() const {
125
10
        return _binding_readers;
126
10
    }
127
128
    const std::unordered_map<std::wstring, std::shared_ptr<lucene::index::IndexReader>>&
129
10
    field_readers() const {
130
10
        return _field_readers;
131
10
    }
132
133
14
    const std::unordered_map<std::string, FieldReaderBinding>& binding_cache() const {
134
14
        return _cache;
135
14
    }
136
137
2
    IndexIterator* get_iterator(const std::string& field_name) const {
138
2
        auto it = _iterators.find(field_name);
139
2
        return (it != _iterators.end()) ? it->second : nullptr;
140
2
    }
141
142
2
    void set_leaf_query_mapper(SearchLeafQueryMapper mapper) {
143
2
        _leaf_query_mapper = std::move(mapper);
144
2
    }
145
146
    Status map_leaf_query(
147
            const std::string& field_name,
148
22
            std::shared_ptr<segment_v2::inverted_index::query_v2::Query>* query) const {
149
22
        if (!_leaf_query_mapper || query == nullptr || *query == nullptr) {
150
20
            return Status::OK();
151
20
        }
152
2
        return _leaf_query_mapper(field_name, query);
153
22
    }
154
155
private:
156
    std::string binding_key_for(const std::string& stored_field_name,
157
58
                                InvertedIndexQueryType query_type) const {
158
58
        return stored_field_name + "#" + std::to_string(static_cast<int>(query_type));
159
58
    }
160
161
    const std::unordered_map<std::string, IndexFieldNameAndTypePair>& _data_type_with_names;
162
    const std::unordered_map<std::string, IndexIterator*>& _iterators;
163
    std::shared_ptr<IndexQueryContext> _context;
164
    std::vector<TSearchFieldBinding> _field_bindings;
165
    std::unordered_map<std::string, const TSearchFieldBinding*> _field_binding_map;
166
    std::unordered_set<std::string> _variant_subcolumn_fields;
167
    std::unordered_map<std::string, FieldReaderBinding> _cache;
168
    std::vector<std::shared_ptr<lucene::index::IndexReader>> _readers;
169
    std::unordered_map<std::string, std::shared_ptr<lucene::index::IndexReader>> _binding_readers;
170
    std::unordered_map<std::wstring, std::shared_ptr<lucene::index::IndexReader>> _field_readers;
171
    std::vector<segment_v2::InvertedIndexCacheHandle> _searcher_cache_handles;
172
    SearchLeafQueryMapper _leaf_query_mapper;
173
};
174
175
class VariantSearchNullBitmapAdapter final : public inverted_index::query_v2::NullBitmapResolver {
176
public:
177
    explicit VariantSearchNullBitmapAdapter(const FieldReaderResolver& resolver)
178
2
            : _resolver(resolver) {}
179
180
    segment_v2::IndexIterator* iterator_for(const inverted_index::query_v2::Scorer& scorer,
181
                                            const std::string& logical_field) const override;
182
183
private:
184
    const FieldReaderResolver& _resolver;
185
};
186
187
void populate_variant_search_binding_context(
188
        const FieldReaderResolver& resolver,
189
        inverted_index::query_v2::QueryExecutionContext* exec_ctx);
190
191
inverted_index::query_v2::QueryExecutionContext build_variant_search_query_execution_context(
192
        uint32_t segment_num_rows, const FieldReaderResolver& resolver,
193
        inverted_index::query_v2::NullBitmapResolver* null_resolver);
194
195
struct VariantNestedDocMapperContext {
196
    std::string root_field;
197
    std::vector<const segment_v2::NestedGroupReader*> active_group_chain;
198
    const segment_v2::VariantColumnReader* variant_reader = nullptr;
199
    const segment_v2::NestedGroupReadProvider* read_provider = nullptr;
200
    segment_v2::ColumnIteratorOptions column_iter_opts;
201
};
202
203
Status map_variant_nested_leaf_query_to_active_group(const VariantNestedDocMapperContext& context,
204
                                                     const std::string& logical_field_name,
205
                                                     inverted_index::query_v2::QueryPtr* query);
206
207
inverted_index::query_v2::QueryPtr make_variant_nested_doc_mapping_query(
208
        inverted_index::query_v2::QueryPtr child_query,
209
        std::vector<const segment_v2::NestedGroupReader*> child_to_parent_chain,
210
        const segment_v2::NestedGroupReadProvider* read_provider,
211
        segment_v2::ColumnIteratorOptions column_iter_opts);
212
213
class VariantNestedSearchEvaluator {
214
public:
215
    explicit VariantNestedSearchEvaluator(const FunctionSearch& function_search)
216
7
            : _function_search(function_search) {}
217
218
    Status evaluate(const TSearchParam& search_param, const TSearchClause& nested_clause,
219
                    const std::shared_ptr<segment_v2::IndexQueryContext>& context,
220
                    FieldReaderResolver& resolver, uint32_t num_rows,
221
                    const IndexExecContext* index_exec_ctx,
222
                    const std::unordered_map<std::string, int>& field_name_to_column_id,
223
                    std::shared_ptr<roaring::Roaring>& result_bitmap) const;
224
225
private:
226
    const FunctionSearch& _function_search;
227
};
228
229
} // namespace doris