6565#include " storage/index/inverted/inverted_index_profile.h"
6666#include " storage/index/inverted/similarity/collection_statistics.h"
6767#include " storage/iterator/block_reader.h"
68+ #include " storage/itoken_extractor.h"
6869#include " storage/olap_common.h"
6970#include " storage/olap_tuple.h"
7071#include " storage/olap_utils.h"
@@ -344,6 +345,7 @@ void OlapScanner::_merge_seq_map_candidate_stats(const OlapReaderStatistics& can
344345 total_stats->seq_map_candidate_scan_bytes += candidate_stats.uncompressed_bytes_read ;
345346 total_stats->seq_map_candidate_index_filtered_rows +=
346347 candidate_stats.rows_inverted_index_filtered ;
348+ total_stats->seq_map_candidate_bloom_filter_filtered_rows += candidate_stats.rows_bf_filtered ;
347349 total_stats->seq_map_candidate_index_downgrades +=
348350 candidate_stats.inverted_index_downgrade_count ;
349351 total_stats->seq_map_candidate_index_lookup_ns += candidate_stats.inverted_index_lookup_timer ;
@@ -363,6 +365,7 @@ void OlapScanner::_merge_seq_map_candidate_stats(const OlapReaderStatistics& can
363365
364366Status OlapScanner::_collect_seq_map_candidate_keys (
365367 const std::vector<std::shared_ptr<ColumnPredicate>>& driver_predicates,
368+ const std::vector<FunctionFilter>& driver_function_filters,
366369 const std::vector<std::shared_ptr<ColumnPredicate>>& key_predicates,
367370 int64_t previous_candidate_scan_rows, bool price_point_lookups, int64_t max_candidate_keys,
368371 size_t max_candidate_bytes, size_t candidate_workspace_bytes,
@@ -391,7 +394,7 @@ Status OlapScanner::_collect_seq_map_candidate_keys(
391394 for (const auto & predicate : driver_predicates) {
392395 candidate_params.predicates .emplace_back (predicate->clone (predicate->column_id ()));
393396 }
394- candidate_params.function_filters . clear () ;
397+ candidate_params.function_filters = driver_function_filters ;
395398 candidate_params.all_access_paths .clear ();
396399 candidate_params.predicate_access_paths .clear ();
397400 candidate_params.output_columns .clear ();
@@ -422,7 +425,7 @@ Status OlapScanner::_collect_seq_map_candidate_keys(
422425
423426 std::vector<ColumnId> candidate_columns;
424427 candidate_columns.reserve (_tablet_reader_params.tablet_schema ->num_key_columns () +
425- driver_predicates.size ());
428+ driver_predicates.size () + driver_function_filters. size () );
426429 for (uint32_t cid = 0 ; cid < _tablet_reader_params.tablet_schema ->num_key_columns (); ++cid) {
427430 candidate_columns.push_back (cid);
428431 }
@@ -432,6 +435,14 @@ Status OlapScanner::_collect_seq_map_candidate_keys(
432435 candidate_columns.push_back (predicate->column_id ());
433436 }
434437 }
438+ for (const auto & filter : driver_function_filters) {
439+ const int32_t cid = candidate_params.tablet_schema ->field_index (filter._col_name );
440+ DORIS_CHECK_GE (cid, 0 );
441+ if (std::find (candidate_columns.begin (), candidate_columns.end (), cid) ==
442+ candidate_columns.end ()) {
443+ candidate_columns.push_back (cid);
444+ }
445+ }
435446 candidate_params.return_columns = candidate_columns;
436447 candidate_params.origin_return_columns = &candidate_columns;
437448 candidate_params.tablet_columns_convert_to_null_set = nullptr ;
@@ -534,9 +545,18 @@ void OlapScanner::_record_seq_map_candidate_fallback_reason(RuntimeProfile* prof
534545 profile->add_info_string (" SeqMapCandidateFallbackReason." + fallback_reason, fallback_reason);
535546}
536547
548+ bool OlapScanner::_has_usable_ngram_bf_pattern (const FunctionFilter& filter, size_t gram_size) {
549+ NgramTokenExtractor extractor (gram_size);
550+ size_t position = 0 ;
551+ std::string token;
552+ return extractor.next_in_string_like (filter._string_param .data , filter._string_param .size ,
553+ &position, token);
554+ }
555+
537556Status OlapScanner::_build_seq_map_candidate_keys (
538557 const std::vector<std::shared_ptr<ColumnPredicate>>& key_predicates,
539558 const std::map<uint32_t , std::vector<std::shared_ptr<ColumnPredicate>>>& group_drivers,
559+ const std::map<uint32_t , std::vector<FunctionFilter>>& group_function_drivers,
540560 int64_t max_candidate_keys, const CandidateMemoryBudget& memory_budget,
541561 const CandidateScanCostLimit& cost_limit) {
542562 auto & params = _tablet_reader_params;
@@ -560,6 +580,11 @@ Status OlapScanner::_build_seq_map_candidate_keys(
560580 size_t final_key_bytes = 0 ;
561581 bool first_group = true ;
562582 for (const auto & [seq_col, predicates] : group_drivers) {
583+ const auto function_it = group_function_drivers.find (seq_col);
584+ const std::vector<FunctionFilter> empty_function_filters;
585+ const auto & function_filters = function_it == group_function_drivers.end ()
586+ ? empty_function_filters
587+ : function_it->second ;
563588 CandidateKeyMap group_keys;
564589 size_t group_key_bytes = 0 ;
565590 bool limit_exceeded = false ;
@@ -568,7 +593,7 @@ Status OlapScanner::_build_seq_map_candidate_keys(
568593 bool cost_exceeded = false ;
569594 const size_t remaining_candidate_bytes = memory_budget.key_bytes - final_key_bytes;
570595 RETURN_IF_ERROR (_collect_seq_map_candidate_keys (
571- predicates, key_predicates, stats->seq_map_candidate_scan_rows ,
596+ predicates, function_filters, key_predicates, stats->seq_map_candidate_scan_rows ,
572597 group_drivers.size () == 1 , max_candidate_keys, remaining_candidate_bytes,
573598 memory_budget.workspace_bytes , cost_limit, &group_keys, &group_key_bytes,
574599 &limit_exceeded, &bytes_exceeded, &reservation_exceeded, &cost_exceeded));
@@ -671,14 +696,10 @@ Status OlapScanner::_prepare_seq_map_candidate_keys() {
671696 _seq_map_candidate_fallback_reason = " candidate_key_bytes_limit" ;
672697 return Status::OK ();
673698 }
674- if (!query_options.enable_inverted_index_query ) {
675- ++stats->seq_map_candidate_fallbacks ;
676- _seq_map_candidate_fallback_reason = " inverted_index_query_disabled" ;
677- return Status::OK ();
678- }
679-
680699 std::vector<std::shared_ptr<ColumnPredicate>> key_predicates;
681700 std::map<uint32_t , std::vector<std::shared_ptr<ColumnPredicate>>> group_drivers;
701+ std::map<uint32_t , std::vector<FunctionFilter>> group_function_drivers;
702+ bool has_disabled_inverted_driver = false ;
682703 const auto & value_to_seq = schema->value_col_idx_to_seq_col_idx ();
683704 for (const auto & predicate : params.predicates ) {
684705 const auto cid = predicate->column_id ();
@@ -697,17 +718,55 @@ Status OlapScanner::_prepare_seq_map_candidate_keys() {
697718 if (!positive_driver || schema->inverted_indexs (column).empty ()) {
698719 continue ;
699720 }
721+ if (!query_options.enable_inverted_index_query ) {
722+ has_disabled_inverted_driver = true ;
723+ continue ;
724+ }
700725 group_drivers[seq_it->second ].push_back (predicate);
701726 ++stats->seq_map_candidate_driver_predicates ;
702727 }
703728
729+ for (const auto & filter : params.function_filters ) {
730+ if (filter._opposite || !config::enable_query_like_bloom_filter) {
731+ continue ;
732+ }
733+ const int32_t cid = schema->field_index (filter._col_name );
734+ DORIS_CHECK_GE (cid, 0 );
735+ const auto & column = schema->column (cid);
736+ if (column.is_key ()) {
737+ continue ;
738+ }
739+ const auto seq_it = value_to_seq.find (cid);
740+ if (seq_it == value_to_seq.end ()) {
741+ continue ;
742+ }
743+ const auto * ngram_index = schema->get_ngram_bf_index (column.unique_id ());
744+ if (ngram_index == nullptr ||
745+ !_has_usable_ngram_bf_pattern (filter, ngram_index->get_gram_size ())) {
746+ continue ;
747+ }
748+ group_drivers.try_emplace (seq_it->second );
749+ group_function_drivers[seq_it->second ].push_back (filter);
750+ ++stats->seq_map_candidate_driver_predicates ;
751+ }
752+
704753 if (group_drivers.empty ()) {
705754 ++stats->seq_map_candidate_fallbacks ;
706- _seq_map_candidate_fallback_reason = " no_indexed_positive_driver" ;
755+ _seq_map_candidate_fallback_reason = has_disabled_inverted_driver
756+ ? " inverted_index_query_disabled"
757+ : " no_indexed_positive_driver" ;
707758 return Status::OK ();
708759 }
709760 stats->seq_map_candidate_driver_groups = group_drivers.size ();
710- if (!params.start_key .empty () || !params.end_key .empty ()) {
761+ // Full-key ranges are already selective. Prefix ranges such as advertiser_id = ?
762+ // still leave enough rows for a value-column candidate scan to be useful.
763+ const auto range_uses_full_key =
764+ [key_column_count = schema->num_key_columns ()](const std::vector<OlapTuple>& keys) {
765+ return std::ranges::any_of (keys, [key_column_count](const OlapTuple& key) {
766+ return key.size () >= key_column_count;
767+ });
768+ };
769+ if (range_uses_full_key (params.start_key ) || range_uses_full_key (params.end_key )) {
711770 ++stats->seq_map_candidate_fallbacks ;
712771 _seq_map_candidate_fallback_reason = " key_range_present" ;
713772 return Status::OK ();
@@ -725,8 +784,9 @@ Status OlapScanner::_prepare_seq_map_candidate_keys() {
725784 try {
726785 ++enable_thread_catch_bad_alloc;
727786 Defer restore_bad_alloc_catch {[&] { --enable_thread_catch_bad_alloc; }};
728- build_status = _build_seq_map_candidate_keys (key_predicates, group_drivers,
729- max_candidate_keys, memory_budget, cost_limit);
787+ build_status =
788+ _build_seq_map_candidate_keys (key_predicates, group_drivers, group_function_drivers,
789+ max_candidate_keys, memory_budget, cost_limit);
730790 } catch (const Exception& exception) {
731791 build_status = exception.code () == ErrorCode::MEM_ALLOC_FAILED
732792 ? Status::MemoryLimitExceeded (exception.to_string ())
@@ -1635,6 +1695,8 @@ void OlapScanner::_collect_profile_before_close() {
16351695 stats.seq_map_candidate_scan_bytes );
16361696 COUNTER_UPDATE (local_state->_seq_map_candidate_index_filtered_rows_counter ,
16371697 stats.seq_map_candidate_index_filtered_rows );
1698+ COUNTER_UPDATE (local_state->_seq_map_candidate_bloom_filter_filtered_rows_counter ,
1699+ stats.seq_map_candidate_bloom_filter_filtered_rows );
16381700 COUNTER_UPDATE (local_state->_seq_map_candidate_index_downgrades_counter ,
16391701 stats.seq_map_candidate_index_downgrades );
16401702 COUNTER_UPDATE (local_state->_seq_map_candidate_index_lookup_timer ,
0 commit comments