Coverage Report

Created: 2026-10-15 05:06

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
class TabletColumn;
58
59
using namespace doris::segment_v2;
60
61
class FunctionSearch;
62
class IndexExecContext;
63
64
using SearchLeafQueryMapper = std::function<Status(
65
        const std::string&, std::shared_ptr<segment_v2::inverted_index::query_v2::Query>*)>;
66
67
enum class SearchFieldBindingState {
68
    BOUND,
69
    MISSING_IN_SEGMENT,
70
};
71
72
enum class SearchFieldExecutionMode {
73
    UNBOUND,
74
    CLUCENE,
75
    DIRECT_INDEX,
76
    SNII_NATIVE,
77
};
78
79
struct FieldReaderBinding {
80
    std::string logical_field_name;
81
    std::string stored_field_name;
82
    std::wstring stored_field_wstr;
83
    DataTypePtr column_type;
84
    InvertedIndexQueryType query_type;
85
    InvertedIndexReaderPtr inverted_reader;
86
    std::shared_ptr<lucene::index::IndexReader> lucene_reader;
87
    std::map<std::string, std::string> index_properties;
88
    std::string binding_key;
89
    std::string analyzer_key;
90
    InvertedIndexAnalyzerCtxSPtr analyzer_context;
91
    SearchFieldBindingState state = SearchFieldBindingState::MISSING_IN_SEGMENT;
92
    SearchFieldExecutionMode execution_mode = SearchFieldExecutionMode::UNBOUND;
93
94
80
    bool is_bound() const {
95
80
        return state == SearchFieldBindingState::BOUND || inverted_reader != nullptr ||
96
80
               lucene_reader != nullptr;
97
80
    }
98
8
    bool use_direct_index_reader() const {
99
8
        return is_bound() && execution_mode == SearchFieldExecutionMode::DIRECT_INDEX;
100
8
    }
101
45
    bool use_snii_native_reader() const {
102
45
        return is_bound() && execution_mode == SearchFieldExecutionMode::SNII_NATIVE;
103
45
    }
104
};
105
106
// Build the analyzer context a SEARCH binding executes with, converting a failure to build the
107
// analyzer provider into a Status instead of letting the exception escape a Status-returning caller.
108
Result<InvertedIndexAnalyzerCtxSPtr> build_search_analyzer_context(
109
        const std::map<std::string, std::string>& properties, const std::string& analyzer_key);
110
111
class FieldReaderResolver {
112
public:
113
    FieldReaderResolver(
114
            const std::unordered_map<std::string, IndexFieldNameAndTypePair>& data_type_with_names,
115
            const std::unordered_map<std::string, IndexIterator*>& iterators,
116
            std::shared_ptr<IndexQueryContext> context,
117
            const std::vector<TSearchFieldBinding>& field_bindings = {});
118
119
    Status resolve(const std::string& field_name, InvertedIndexQueryType query_type,
120
                   FieldReaderBinding* binding);
121
122
    Status resolve_with_analyzer_context(const std::string& field_name,
123
                                         InvertedIndexQueryType query_type,
124
                                         FieldReaderBinding* binding);
125
126
65
    bool is_variant_subcolumn(const std::string& field_name) const {
127
65
        return _variant_subcolumn_fields.count(field_name) > 0;
128
65
    }
129
130
11
    const std::vector<std::shared_ptr<lucene::index::IndexReader>>& readers() const {
131
11
        return _readers;
132
11
    }
133
134
    const std::unordered_map<std::string, std::shared_ptr<lucene::index::IndexReader>>&
135
10
    reader_bindings() const {
136
10
        return _binding_readers;
137
10
    }
138
139
    const std::unordered_map<std::wstring, std::shared_ptr<lucene::index::IndexReader>>&
140
10
    field_readers() const {
141
10
        return _field_readers;
142
10
    }
143
144
14
    const std::unordered_map<std::string, FieldReaderBinding>& binding_cache() const {
145
14
        return _cache;
146
14
    }
147
148
2
    IndexIterator* get_iterator(const std::string& field_name) const {
149
2
        auto it = _iterators.find(field_name);
150
2
        return (it != _iterators.end()) ? it->second : nullptr;
151
2
    }
152
153
2
    void set_leaf_query_mapper(SearchLeafQueryMapper mapper) {
154
2
        _leaf_query_mapper = std::move(mapper);
155
2
    }
156
157
    Status map_leaf_query(
158
            const std::string& field_name,
159
24
            std::shared_ptr<segment_v2::inverted_index::query_v2::Query>* query) const {
160
24
        if (!_leaf_query_mapper || query == nullptr || *query == nullptr) {
161
22
            return Status::OK();
162
22
        }
163
2
        return _leaf_query_mapper(field_name, query);
164
24
    }
165
166
private:
167
    std::string binding_key_for(const std::string& stored_field_name,
168
64
                                InvertedIndexQueryType query_type) const {
169
64
        return stored_field_name + "#" + std::to_string(static_cast<int>(query_type));
170
64
    }
171
172
    const std::unordered_map<std::string, IndexFieldNameAndTypePair>& _data_type_with_names;
173
    const std::unordered_map<std::string, IndexIterator*>& _iterators;
174
    std::shared_ptr<IndexQueryContext> _context;
175
    std::vector<TSearchFieldBinding> _field_bindings;
176
    std::unordered_map<std::string, const TSearchFieldBinding*> _field_binding_map;
177
    std::unordered_set<std::string> _variant_subcolumn_fields;
178
    std::unordered_map<std::string, FieldReaderBinding> _cache;
179
    std::vector<std::shared_ptr<lucene::index::IndexReader>> _readers;
180
    std::unordered_map<std::string, std::shared_ptr<lucene::index::IndexReader>> _binding_readers;
181
    std::unordered_map<std::wstring, std::shared_ptr<lucene::index::IndexReader>> _field_readers;
182
    std::vector<segment_v2::InvertedIndexCacheHandle> _searcher_cache_handles;
183
    SearchLeafQueryMapper _leaf_query_mapper;
184
};
185
186
class VariantSearchNullBitmapAdapter final : public inverted_index::query_v2::NullBitmapResolver {
187
public:
188
    explicit VariantSearchNullBitmapAdapter(const FieldReaderResolver& resolver)
189
2
            : _resolver(resolver) {}
190
191
    segment_v2::IndexIterator* iterator_for(const inverted_index::query_v2::Scorer& scorer,
192
                                            const std::string& logical_field) const override;
193
194
private:
195
    const FieldReaderResolver& _resolver;
196
};
197
198
void populate_variant_search_binding_context(
199
        const FieldReaderResolver& resolver,
200
        inverted_index::query_v2::QueryExecutionContext* exec_ctx);
201
202
inverted_index::query_v2::QueryExecutionContext build_variant_search_query_execution_context(
203
        uint32_t segment_num_rows, const FieldReaderResolver& resolver,
204
        inverted_index::query_v2::NullBitmapResolver* null_resolver);
205
206
struct VariantNestedDocMapperContext {
207
    std::string root_field;
208
    std::vector<const segment_v2::NestedGroupReader*> active_group_chain;
209
    const segment_v2::VariantColumnReader* variant_reader = nullptr;
210
    const segment_v2::NestedGroupReadProvider* read_provider = nullptr;
211
    segment_v2::ColumnIteratorOptions column_iter_opts;
212
};
213
214
Status map_variant_nested_leaf_query_to_active_group(const VariantNestedDocMapperContext& context,
215
                                                     const std::string& logical_field_name,
216
                                                     inverted_index::query_v2::QueryPtr* query);
217
218
inverted_index::query_v2::QueryPtr make_variant_nested_doc_mapping_query(
219
        inverted_index::query_v2::QueryPtr child_query,
220
        std::vector<const segment_v2::NestedGroupReader*> child_to_parent_chain,
221
        const segment_v2::NestedGroupReadProvider* read_provider,
222
        segment_v2::ColumnIteratorOptions column_iter_opts);
223
224
class VariantNestedSearchEvaluator {
225
public:
226
    explicit VariantNestedSearchEvaluator(const FunctionSearch& function_search)
227
8
            : _function_search(function_search) {}
228
229
    Status evaluate(const TSearchParam& search_param, const TSearchClause& nested_clause,
230
                    const std::shared_ptr<segment_v2::IndexQueryContext>& context,
231
                    FieldReaderResolver& resolver, uint32_t num_rows,
232
                    const IndexExecContext* index_exec_ctx, const TabletColumn* nested_column,
233
                    std::shared_ptr<roaring::Roaring>& result_bitmap) const;
234
235
private:
236
    const FunctionSearch& _function_search;
237
};
238
239
} // namespace doris