Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d9993ed
[feat](inverted-index) Add IK tokenizers to custom analyzers
airborne12 Sep 13, 2026
34e1761
[fix](inverted-index) Address IK analyzer review findings
airborne12 Sep 13, 2026
748c08d
[fix](inverted-index) Preserve IK analyzer semantics
airborne12 Sep 13, 2026
979d1c3
[fix](inverted-index) Unify IK analyzer identities
airborne12 Sep 13, 2026
c9595f6
[fix](inverted-index) Preserve IK analyzer semantics
airborne12 Sep 13, 2026
a93f752
[fix](inverted-index) Preserve IK pinyin source offsets
airborne12 Sep 13, 2026
02dc82e
[fix](inverted-index) Stabilize analyzer identity replay
airborne12 Sep 14, 2026
b86bfa1
[fix](inverted-index) Preserve custom analyzer replay semantics
airborne12 Sep 14, 2026
8d99469
[fix](inverted-index) Canonicalize analyzer filter identities
airborne12 Sep 14, 2026
e467aab
[fix](inverted-index) Preserve analyzer token offsets
airborne12 Sep 14, 2026
cc3bf33
[fix](inverted-index) Preserve exact analyzer offsets
airborne12 Sep 14, 2026
c8f7b67
[fix](inverted-index) Preserve exact legacy analyzer bindings
airborne12 Sep 14, 2026
6b794e9
[fix](inverted-index) Preserve analyzer bindings and token offsets
airborne12 Sep 18, 2026
b92b994
[test](inverted-index) Record deterministic MATCH regression output
airborne12 Sep 18, 2026
4501552
[fix](inverted-index) Preserve effective IK identities and index bind…
airborne12 Sep 18, 2026
5597615
[fix](be) Preserve MATCH index bindings during BE-first upgrades
airborne12 Sep 19, 2026
d1e7f89
[fix](inverted-index) Contain analyzer failures and preserve fold ide…
airborne12 Sep 20, 2026
afe90dd
[fix](inverted-index) Contain ARRAY analyzer failures
airborne12 Sep 20, 2026
4fbf21b
[fix](inverted-index) Preserve replayed policy semantics
airborne12 Sep 20, 2026
d76f9a2
[fix](inverted-index) Preserve nested tokenizer bindings
airborne12 Sep 20, 2026
afb3c5a
[fix](inverted-index) Preserve nested filter compatibility
airborne12 Sep 20, 2026
ea23758
[fix](inverted-index) Preserve analyzer selection and pinyin offsets
airborne12 Sep 20, 2026
6ecc7e1
[fix](inverted-index) Bound ICU offset tracking memory
airborne12 Sep 21, 2026
20c49dd
[fix](inverted-index) Address analyzer review feedback
airborne12 Sep 21, 2026
e9ef0ce
[fix](inverted-index) Align duplicate analyzer test expectation
airborne12 Sep 21, 2026
3145023
[fix](inverted-index) Close analyzer review findings
airborne12 Sep 21, 2026
6280919
[fix](inverted-index) Skip unmatched pinyin runes iteratively
airborne12 Sep 22, 2026
f4f79a4
[fix](inverted-index) Release oversized upstream provenance scratch o…
airborne12 Sep 22, 2026
31d36eb
[fix](inverted-index) Canonicalize effective analyzer settings and AL…
airborne12 Sep 22, 2026
4bb33c5
[fix](inverted-index) Reject cross-family exact tokenizer bindings
airborne12 Sep 22, 2026
73cd9c7
[fix](inverted-index) Publish exact or conservative provenance for IK…
airborne12 Sep 22, 2026
41ed29c
[fix](inverted-index) Resolve built-in normalizers before normalized …
airborne12 Sep 22, 2026
db18af0
[opt](inverted-index) Reuse tokenizer provenance scratch across tokens
airborne12 Sep 22, 2026
98adc06
[fix](inverted-index) Answer ICU offset corrections from the edit record
airborne12 Sep 22, 2026
f5d7670
[fix](inverted-index) Canonicalize case folds through custom pipeline…
airborne12 Sep 22, 2026
7345ff8
[fix](inverted-index) Close remaining fold and Pinyin gate gaps in an…
airborne12 Sep 22, 2026
7f514a6
[fix](inverted-index) Publish provenance for pinyin tokenizer candida…
airborne12 Sep 22, 2026
77fa364
[fix](inverted-index) Canonicalize effective component settings again…
airborne12 Sep 22, 2026
787e0ab
[fix](inverted-index) Map term starts inside char-filter expansions a…
airborne12 Sep 22, 2026
b0924c2
[fix](inverted-index) Resolve top-level analyzer names in BE order fo…
airborne12 Sep 22, 2026
5843206
[fix](inverted-index) Clip Pinyin provenance to the emitted prefix an…
airborne12 Sep 22, 2026
79715d0
[fix](inverted-index) Keep built-in normalizer bindings and collapse …
airborne12 Sep 22, 2026
6264c81
[test](inverted-index) Pin the built-in binding that survives a polic…
airborne12 Sep 22, 2026
2ecd08d
[fix](inverted-index) Index malformed bytes again and keep SEARCH ana…
airborne12 Sep 23, 2026
0e06be4
[fix](inverted-index) Canonicalize equivalent analyzer components
airborne12 Sep 23, 2026
9bcd4b4
[fix](inverted-index) Wait for analyzer policy before malformed UTF-8…
airborne12 Sep 23, 2026
ce8505f
[fix](inverted-index) Collapse repeated icu_normalizer and char_repla…
airborne12 Sep 23, 2026
9137635
[fix](inverted-index) Give built-in analyzers the identity of the pip…
airborne12 Sep 23, 2026
e3a55f5
[fix](inverted-index) Keep the settings and folds that built-in analy…
airborne12 Sep 24, 2026
50156ba
[fix](inverted-index) Check phrase support on the selected reader and…
airborne12 Sep 24, 2026
4ce410c
[fix](inverted-index) Guard direct SEARCH like MATCH for phrase suppo…
airborne12 Sep 24, 2026
4a30b86
[fix](inverted-index) Check support_phrase only for MATCH on a tokeni…
airborne12 Sep 24, 2026
2e22e29
[fix](inverted-index) Keep explicit IK queries bound to the only lega…
airborne12 Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions be/src/exprs/function/function_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,29 @@ Status FunctionSearch::evaluate_inverted_index_with_search_param(
const IndexExecContext* index_exec_ctx,
const std::unordered_map<std::string, int>& field_name_to_column_id,
const std::shared_ptr<IndexQueryContext>& index_query_context) const {
// VSearchExpr enters here directly, outside IFunction::execute() and its exception boundary,
// so this is where a failure inside the search, such as an analyzer whose first token
// stream throws, has to become a Status.
try {
return evaluate_inverted_index_with_search_param_unguarded(
search_param, data_type_with_names, std::move(iterators), num_rows, bitmap_result,
enable_cache, index_exec_ctx, field_name_to_column_id, index_query_context);
} catch (const CLuceneError& e) {
return Status::Error<ErrorCode::INVERTED_INDEX_ANALYZER_ERROR>("search failed: {}",
e.what());
} catch (const Exception& e) {
return e.to_status();
}
}

Status FunctionSearch::evaluate_inverted_index_with_search_param_unguarded(
const TSearchParam& search_param,
const std::unordered_map<std::string, IndexFieldNameAndTypePair>& data_type_with_names,
std::unordered_map<std::string, IndexIterator*> iterators, uint32_t num_rows,
InvertedIndexResultBitmap& bitmap_result, bool enable_cache,
const IndexExecContext* index_exec_ctx,
const std::unordered_map<std::string, int>& field_name_to_column_id,
const std::shared_ptr<IndexQueryContext>& index_query_context) const {
const bool is_nested_query = search_param.root.clause_type == "NESTED";
if (is_nested_query && !is_nested_group_search_supported()) {
return Status::NotSupported(
Expand Down
10 changes: 10 additions & 0 deletions be/src/exprs/function/function_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ class FunctionSearch : public IFunction {
const std::unordered_map<std::string, int>& field_name_to_column_id,
const std::shared_ptr<IndexQueryContext>& index_query_context = nullptr) const;

// The body of the overload above; it may throw, so only that overload calls it.
Status evaluate_inverted_index_with_search_param_unguarded(
const TSearchParam& search_param,
const std::unordered_map<std::string, IndexFieldNameAndTypePair>& data_type_with_names,
std::unordered_map<std::string, IndexIterator*> iterators, uint32_t num_rows,
InvertedIndexResultBitmap& bitmap_result, bool enable_cache,
const IndexExecContext* index_exec_ctx,
const std::unordered_map<std::string, int>& field_name_to_column_id,
const std::shared_ptr<IndexQueryContext>& index_query_context) const;

// Public methods for testing
enum class ClauseTypeCategory {
NON_TOKENIZED, // TERM, PREFIX, WILDCARD, REGEXP, RANGE, LIST - no tokenization, use EQUAL_QUERY
Expand Down
9 changes: 1 addition & 8 deletions be/src/exprs/function/match.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,7 @@ Status FunctionMatchBase::evaluate_inverted_index(
}
const std::string& function_name = get_name();

if (function_name == MATCH_PHRASE_FUNCTION || function_name == MATCH_PHRASE_PREFIX_FUNCTION ||
function_name == MATCH_PHRASE_EDGE_FUNCTION) {
auto reader = iter->get_reader(InvertedIndexReaderType::FULLTEXT);
if (reader && !segment_v2::IndexReaderHelper::is_support_phrase(reader)) {
return Status::Error<ErrorCode::INDEX_INVALID_PARAMETERS>(
"phrase queries require setting support_phrase = true");
}
}
// support_phrase is checked once the analyzer has selected the reader that runs the query.
Field param_value;
arguments[0].column->get(0, param_value);
if (param_value.is_null()) {
Expand Down
24 changes: 21 additions & 3 deletions be/src/exprs/function/variant_inverted_index_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ void add_search_binding_diagnostic(const std::shared_ptr<IndexQueryContext>& con
}
}

InvertedIndexAnalyzerCtxSPtr build_analyzer_context(
InvertedIndexAnalyzerCtxSPtr build_analyzer_context_unsafe(
const std::map<std::string, std::string>& properties, const std::string& analyzer_key) {
InvertedIndexAnalyzerConfig config;
config.analyzer_name = get_analyzer_name_from_properties(properties);
Expand All @@ -90,6 +90,20 @@ InvertedIndexAnalyzerCtxSPtr build_analyzer_context(

} // namespace

// Replayed components can collide across policy families, so building the provider throws.
Result<InvertedIndexAnalyzerCtxSPtr> build_search_analyzer_context(
const std::map<std::string, std::string>& properties, const std::string& analyzer_key) {
try {
return build_analyzer_context_unsafe(properties, analyzer_key);
} catch (const CLuceneError& error) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_ANALYZER_ERROR>(
"Build search analyzer failed: {}", error.what()));
} catch (const Exception& error) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_ANALYZER_ERROR>(
"Build search analyzer failed: {}", error.what()));
}
}

FieldReaderResolver::FieldReaderResolver(
const std::unordered_map<std::string, IndexFieldNameAndTypePair>& data_type_with_names,
const std::unordered_map<std::string, IndexIterator*>& iterators,
Expand Down Expand Up @@ -415,8 +429,12 @@ Status FieldReaderResolver::resolve_with_analyzer_context(const std::string& fie
return Status::OK();
}

binding->analyzer_context =
build_analyzer_context(binding->index_properties, binding->analyzer_key);
auto built_context =
build_search_analyzer_context(binding->index_properties, binding->analyzer_key);
if (!built_context.has_value()) {
return built_context.error();
}
binding->analyzer_context = std::move(built_context.value());
_cache.at(binding->binding_key).analyzer_context = binding->analyzer_context;
return Status::OK();
}
Expand Down
5 changes: 5 additions & 0 deletions be/src/exprs/function/variant_inverted_index_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ struct FieldReaderBinding {
}
};

// Build the analyzer context a SEARCH binding executes with, converting a failure to build the
// analyzer provider into a Status instead of letting the exception escape a Status-returning caller.
Result<InvertedIndexAnalyzerCtxSPtr> build_search_analyzer_context(
const std::map<std::string, std::string>& properties, const std::string& analyzer_key);

class FieldReaderResolver {
public:
FieldReaderResolver(
Expand Down
30 changes: 26 additions & 4 deletions be/src/exprs/vmatch_predicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ namespace doris {
using namespace doris::segment_v2;

VMatchPredicate::VMatchPredicate(const TExprNode& node) : VExpr(node) {
const auto resolved = AnalyzerConfigParser::parse(node.match_predicate.analyzer_name,
node.match_predicate.parser_type);
const auto resolved = AnalyzerConfigParser::parse(
node.match_predicate.analyzer_name, node.match_predicate.parser_type,
node.match_predicate.parser_mode, node.match_predicate.parser_lowercase,
node.match_predicate.char_filter_map);

InvertedIndexAnalyzerConfig config;
config.analyzer_name = resolved.provider_name;
Expand All @@ -81,9 +83,29 @@ VMatchPredicate::VMatchPredicate(const TExprNode& node) : VExpr(node) {
_analyzer_ctx->parser_type = resolved.parser_type;

if (_analyzer_ctx->requires_analysis()) {
_analyzer_provider =
inverted_index::InvertedIndexAnalyzer::create_analyzer_provider(&config);
std::string bound_name;
std::string legacy_name;
_analyzer_provider = inverted_index::InvertedIndexAnalyzer::create_analyzer_provider(
&config, &bound_name, &legacy_name);
_analyzer = _analyzer_provider->get_analyzer();
if (!legacy_name.empty() && legacy_name != bound_name) {
_analyzer_ctx->legacy_analyzer_key =
AnalyzerConfigParser::parse(legacy_name, node.match_predicate.parser_type,
node.match_predicate.parser_mode,
node.match_predicate.parser_lowercase,
node.match_predicate.char_filter_map)
.analyzer_key;
}
if (bound_name != resolved.provider_name) {
// Reader selection and query tokenization must use the same policy binding.
_analyzer_ctx->analyzer_key =
AnalyzerConfigParser::parse(bound_name, node.match_predicate.parser_type,
node.match_predicate.parser_mode,
node.match_predicate.parser_lowercase,
node.match_predicate.char_filter_map)
.analyzer_key;
_analyzer_ctx->analyzer_name = std::move(bound_name);
}
}

_analyzer_ctx->char_filter_map = std::move(config.char_filter_map);
Expand Down
Loading
Loading