diff --git a/components/core/src/clp/DictionaryReader.hpp b/components/core/src/clp/DictionaryReader.hpp index 694240ad51..4c1d841d3c 100644 --- a/components/core/src/clp/DictionaryReader.hpp +++ b/components/core/src/clp/DictionaryReader.hpp @@ -79,12 +79,12 @@ class DictionaryReader { */ std::string const& get_value(DictionaryIdType id) const; /** - * Gets the entry exactly matching the given search string + * Gets the entries matching the given search string * @param search_string * @param ignore_case - * @return nullptr if an exact match is not found, the entry otherwise + * @return a vector of matching entries, or an empty vector if no entry matches. */ - EntryType const* + std::vector get_entry_matching_value(std::string const& search_string, bool ignore_case) const; /** * Gets the entries that match a given wildcard string @@ -233,26 +233,32 @@ std::string const& DictionaryReader::get_value(Dict } template -EntryType const* DictionaryReader::get_entry_matching_value( +std::vector +DictionaryReader::get_entry_matching_value( std::string const& search_string, bool ignore_case ) const { if (false == ignore_case) { - for (auto const& entry : m_entries) { - if (entry.get_value() == search_string) { - return &entry; - } - } - } else { - auto const& search_string_uppercase = boost::algorithm::to_upper_copy(search_string); - for (auto const& entry : m_entries) { - if (boost::algorithm::to_upper_copy(entry.get_value()) == search_string_uppercase) { - return &entry; - } + // In case-sensitive match, there can be only one matched entry. + if (auto const it = std::ranges::find_if( + m_entries, + [&](auto const& entry) { return entry.get_value() == search_string; } + ); + m_entries.cend() != it) + { + return {&(*it)}; } + return {}; } - return nullptr; + std::vector entries; + auto const search_string_uppercase = boost::algorithm::to_upper_copy(search_string); + for (auto const& entry : m_entries) { + if (boost::algorithm::to_upper_copy(entry.get_value()) == search_string_uppercase) { + entries.push_back(&entry); + } + } + return entries; } template diff --git a/components/core/src/clp/EncodedVariableInterpreter.cpp b/components/core/src/clp/EncodedVariableInterpreter.cpp index 8170f2ddc3..6d9bb14f12 100644 --- a/components/core/src/clp/EncodedVariableInterpreter.cpp +++ b/components/core/src/clp/EncodedVariableInterpreter.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -386,15 +387,30 @@ bool EncodedVariableInterpreter::encode_and_search_dictionary( LogTypeDictionaryEntry::add_float_var(logtype); sub_query.add_non_dict_var(encoded_var); } else { - auto entry = var_dict.get_entry_matching_value(var_str, ignore_case); - if (nullptr == entry) { + auto const entries = var_dict.get_entry_matching_value(var_str, ignore_case); + if (entries.empty()) { // Not in dictionary return false; } - encoded_var = encode_var_dict_id(entry->get_id()); LogTypeDictionaryEntry::add_dict_var(logtype); - sub_query.add_dict_var(encoded_var, entry); + + if (entries.size() == 1) { + auto const* entry = entries.at(0); + sub_query.add_dict_var(encode_var_dict_id(entry->get_id()), entry); + return true; + } + + std::unordered_set const entries_set{ + entries.cbegin(), + entries.cend() + }; + std::unordered_set encoded_vars; + encoded_vars.reserve(entries.size()); + for (auto const* entry : entries) { + encoded_vars.emplace(encode_var_dict_id(entry->get_id())); + } + sub_query.add_imprecise_dict_var(encoded_vars, entries_set); } return true; diff --git a/components/core/src/clp_s/DictionaryReader.hpp b/components/core/src/clp_s/DictionaryReader.hpp index a5f1582ccb..29854c5d2f 100644 --- a/components/core/src/clp_s/DictionaryReader.hpp +++ b/components/core/src/clp_s/DictionaryReader.hpp @@ -61,12 +61,12 @@ class DictionaryReader { std::string const& get_value(DictionaryIdType id) const; /** - * Gets the entry exactly matching the given search string + * Gets the entries matching the given search string * @param search_string * @param ignore_case - * @return nullptr if an exact match is not found, the entry otherwise + * @return a vector of matching entries, or an empty vector if no entry matches. */ - EntryType const* + std::vector get_entry_matching_value(std::string const& search_string, bool ignore_case) const; /** @@ -156,26 +156,32 @@ std::string const& DictionaryReader::get_value(Dict } template -EntryType const* DictionaryReader::get_entry_matching_value( +std::vector +DictionaryReader::get_entry_matching_value( std::string const& search_string, bool ignore_case ) const { if (false == ignore_case) { - for (auto const& entry : m_entries) { - if (entry.get_value() == search_string) { - return &entry; - } - } - } else { - auto const& search_string_uppercase = boost::algorithm::to_upper_copy(search_string); - for (auto const& entry : m_entries) { - if (boost::algorithm::to_upper_copy(entry.get_value()) == search_string_uppercase) { - return &entry; - } + // In case-sensitive match, there can be only one matched entry. + if (auto const it = std::ranges::find_if( + m_entries, + [&](auto const& entry) { return entry.get_value() == search_string; } + ); + m_entries.cend() != it) + { + return {&(*it)}; } + return {}; } - return nullptr; + std::vector entries; + auto const search_string_uppercase = boost::algorithm::to_upper_copy(search_string); + for (auto const& entry : m_entries) { + if (boost::algorithm::to_upper_copy(entry.get_value()) == search_string_uppercase) { + entries.push_back(&entry); + } + } + return entries; } template diff --git a/components/core/src/clp_s/search/Output.cpp b/components/core/src/clp_s/search/Output.cpp index 4833fab94a..6276c346ba 100644 --- a/components/core/src/clp_s/search/Output.cpp +++ b/components/core/src/clp_s/search/Output.cpp @@ -932,12 +932,12 @@ void Output::populate_string_queries(std::shared_ptr const& expr) { } } - auto const* entry = m_var_dict->get_entry_matching_value( + auto const entries = m_var_dict->get_entry_matching_value( unescaped_query_string, m_ignore_case ); - if (entry != nullptr) { + for (auto const& entry : entries) { matching_vars.insert(entry->get_id()); } } else if (EncodedVariableInterpreter:: diff --git a/components/core/src/clp_s/search/clp_search/EncodedVariableInterpreter.cpp b/components/core/src/clp_s/search/clp_search/EncodedVariableInterpreter.cpp index 241f3dde71..7e83e0073c 100644 --- a/components/core/src/clp_s/search/clp_search/EncodedVariableInterpreter.cpp +++ b/components/core/src/clp_s/search/clp_search/EncodedVariableInterpreter.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -34,15 +35,30 @@ bool EncodedVariableInterpreter::encode_and_search_dictionary( LogTypeDictionaryEntry::add_double_var(logtype); sub_query.add_non_dict_var(encoded_var); } else { - auto entry = var_dict.get_entry_matching_value(var_str, ignore_case); - if (nullptr == entry) { + auto const entries = var_dict.get_entry_matching_value(var_str, ignore_case); + if (entries.empty()) { // Not in dictionary return false; } - encoded_var = VariableEncoder::encode_var_dict_id(entry->get_id()); LogTypeDictionaryEntry::add_non_double_var(logtype); - sub_query.add_dict_var(encoded_var, entry); + + if (entries.size() == 1) { + auto const* entry = entries.at(0); + sub_query.add_dict_var(VariableEncoder::encode_var_dict_id(entry->get_id()), entry); + return true; + } + + std::unordered_set const entries_set{ + entries.cbegin(), + entries.cend() + }; + std::unordered_set encoded_vars; + encoded_vars.reserve(entries.size()); + for (auto const* entry : entries) { + encoded_vars.emplace(VariableEncoder::encode_var_dict_id(entry->get_id())); + } + sub_query.add_imprecise_dict_var(encoded_vars, entries_set); } return true; diff --git a/components/core/tests/test-EncodedVariableInterpreter.cpp b/components/core/tests/test-EncodedVariableInterpreter.cpp index 6ab0687a97..856b367851 100644 --- a/components/core/tests/test-EncodedVariableInterpreter.cpp +++ b/components/core/tests/test-EncodedVariableInterpreter.cpp @@ -376,6 +376,51 @@ TEST_CASE("EncodedVariableInterpreter", "[EncodedVariableInterpreter]") { )); } + SECTION("Test multiple metching values") { + constexpr std::string_view cVarDictPath{"var.dict"}; + constexpr std::string_view cVarSegmentIndexPath{"var.segindex"}; + constexpr std::array var_strs + = {"python2.7.3", "Python2.7.3", "PyThOn2.7.3", "PYTHON2.7.3"}; + clp::VariableDictionaryWriter var_dict_writer; + + var_dict_writer.open( + std::string{cVarDictPath}, + std::string{cVarSegmentIndexPath}, + cVariableDictionaryIdMax + ); + + std::vector encoded_vars; + std::vector var_ids; + clp::LogTypeDictionaryEntry logtype_dict_entry; + std::string const msg_template{"here is a string with a dictionary var: "}; + + for (auto const& var_str : var_strs) { + EncodedVariableInterpreter::encode_and_add_to_dictionary( + msg_template + std::string{var_str}, + logtype_dict_entry, + var_dict_writer, + encoded_vars, + var_ids + ); + } + var_dict_writer.close(); + + clp::VariableDictionaryReader var_dict_reader; + var_dict_reader.open(std::string{cVarDictPath}, std::string{cVarSegmentIndexPath}); + var_dict_reader.read_new_entries(); + + REQUIRE(var_dict_reader.get_entry_matching_value(std::string{var_strs.at(0)}, true).size() + == var_strs.size()); + REQUIRE(var_dict_reader.get_entry_matching_value(std::string{var_strs.at(0)}, false).size() + == 1); + + var_dict_reader.close(); + + // Clean-up + REQUIRE(0 == unlink(cVarDictPath.data())); + REQUIRE(0 == unlink(cVarSegmentIndexPath.data())); + } + SECTION("Test encoding and decoding") { string msg;