diff --git a/search/geocoder.cpp b/search/geocoder.cpp index 5db6ec2b62..5ae222e507 100644 --- a/search/geocoder.cpp +++ b/search/geocoder.cpp @@ -6,6 +6,7 @@ #include "search/features_layer_matcher.hpp" #include "search/house_numbers_matcher.hpp" #include "search/locality_scorer.hpp" +#include "search/pre_ranker.hpp" #include "search/processor.hpp" #include "search/retrieval.hpp" #include "search/token_slice.hpp" diff --git a/search/geocoder.hpp b/search/geocoder.hpp index db8e905b98..0e6dd0786f 100644 --- a/search/geocoder.hpp +++ b/search/geocoder.hpp @@ -36,11 +36,6 @@ class MwmInfo; class MwmValue; -namespace coding -{ -class CompressedBitVector; -} - namespace storage { class CountryInfoGetter; diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index d07cb514a0..dc17072036 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -58,8 +58,6 @@ void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) meta.m_isInitialized = true; } -namespace impl -{ PreResult1::PreResult1(FeatureID const & fID, PreRankingInfo const & info) : m_id(fID), m_info(info) { ASSERT(m_id.IsValid(), ()); @@ -302,5 +300,4 @@ void PreResult2::RegionInfo::GetRegion(storage::CountryInfoGetter const & infoGe else infoGetter.GetRegionInfo(m_point, info); } -} // namespace search::impl } // namespace search diff --git a/search/intermediate_result.hpp b/search/intermediate_result.hpp index 259abfbb67..76bb97ff43 100644 --- a/search/intermediate_result.hpp +++ b/search/intermediate_result.hpp @@ -20,8 +20,6 @@ namespace search { class ReverseGeocoder; -namespace impl -{ /// First pass results class. Objects are creating during search in trie. /// Works fast without feature loading and provide ranking. class PreResult1 @@ -153,7 +151,45 @@ inline string DebugPrint(PreResult2 const & t) { return t.DebugPrint(); } -} // namespace impl void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta); + +class IndexedValue +{ + /// @todo Do not use shared_ptr for optimization issues. + /// Need to rewrite std::unique algorithm. + unique_ptr m_value; + + double m_rank; + double m_distanceToPivot; + + friend string DebugPrint(IndexedValue const & value) + { + ostringstream os; + os << "IndexedValue ["; + if (value.m_value) + os << DebugPrint(*value.m_value); + os << "]"; + return os.str(); + } + +public: + explicit IndexedValue(unique_ptr value) + : m_value(move(value)), m_rank(0.0), m_distanceToPivot(numeric_limits::max()) + { + if (!m_value) + return; + + auto const & info = m_value->GetRankingInfo(); + m_rank = info.GetLinearModelRank(); + m_distanceToPivot = info.m_distanceToPivot; + } + + PreResult2 const & operator*() const { return *m_value; } + + inline double GetRank() const { return m_rank; } + + inline double GetDistanceToPivot() const { return m_distanceToPivot; } +}; + } // namespace search diff --git a/search/pre_ranker.cpp b/search/pre_ranker.cpp index 54e6f628fc..b5782fbbfb 100644 --- a/search/pre_ranker.cpp +++ b/search/pre_ranker.cpp @@ -14,7 +14,7 @@ namespace { struct LessFeatureID { - using TValue = impl::PreResult1; + using TValue = PreResult1; inline bool operator()(TValue const & lhs, TValue const & rhs) const { @@ -28,7 +28,7 @@ struct LessFeatureID // 3. Index of the first matched token from the query (increasing). struct ComparePreResult1 { - bool operator()(impl::PreResult1 const & lhs, impl::PreResult1 const & rhs) const + bool operator()(PreResult1 const & lhs, PreResult1 const & rhs) const { if (lhs.GetId() != rhs.GetId()) return lhs.GetId() < rhs.GetId(); @@ -44,19 +44,18 @@ struct ComparePreResult1 PreRanker::PreRanker(size_t limit) : m_limit(limit) {} -void PreRanker::Add(impl::PreResult1 const & result) { m_results.push_back(result); } +void PreRanker::Add(PreResult1 const & result) { m_results.push_back(result); } void PreRanker::Filter(bool viewportSearch) { - using TSet = set; + using TSet = set; TSet theSet; sort(m_results.begin(), m_results.end(), ComparePreResult1()); - m_results.erase( - unique(m_results.begin(), m_results.end(), my::EqualsBy(&impl::PreResult1::GetId)), - m_results.end()); + m_results.erase(unique(m_results.begin(), m_results.end(), my::EqualsBy(&PreResult1::GetId)), + m_results.end()); - sort(m_results.begin(), m_results.end(), &impl::PreResult1::LessDistance); + sort(m_results.begin(), m_results.end(), &PreResult1::LessDistance); if (m_limit != 0 && m_results.size() > m_limit) { @@ -96,8 +95,7 @@ void PreRanker::Filter(bool viewportSearch) if (!viewportSearch) { size_t n = min(m_results.size(), m_limit); - nth_element(m_results.begin(), m_results.begin() + n, m_results.end(), - &impl::PreResult1::LessRank); + nth_element(m_results.begin(), m_results.begin() + n, m_results.end(), &PreResult1::LessRank); theSet.insert(m_results.begin(), m_results.begin() + n); } diff --git a/search/pre_ranker.hpp b/search/pre_ranker.hpp index 3e7f25f591..2bb61f6901 100644 --- a/search/pre_ranker.hpp +++ b/search/pre_ranker.hpp @@ -17,7 +17,7 @@ class PreRanker public: explicit PreRanker(size_t limit); - void Add(impl::PreResult1 const & result); + void Add(PreResult1 const & result); template void Emplace(TArgs &&... args) @@ -46,7 +46,7 @@ public: } private: - vector m_results; + vector m_results; size_t const m_limit; DISALLOW_COPY_AND_MOVE(PreRanker); diff --git a/search/processor.cpp b/search/processor.cpp index d51bc74261..cfcc509822 100644 --- a/search/processor.cpp +++ b/search/processor.cpp @@ -102,62 +102,6 @@ ftypes::Type GetLocalityIndex(feature::TypesHolder const & types) } } -class IndexedValue -{ - /// @todo Do not use shared_ptr for optimization issues. - /// Need to rewrite std::unique algorithm. - unique_ptr m_value; - - double m_rank; - double m_distanceToPivot; - - friend string DebugPrint(IndexedValue const & value) - { - ostringstream os; - os << "IndexedValue ["; - if (value.m_value) - os << impl::DebugPrint(*value.m_value); - os << "]"; - return os.str(); - } - -public: - explicit IndexedValue(unique_ptr value) - : m_value(move(value)), m_rank(0.0), m_distanceToPivot(numeric_limits::max()) - { - if (!m_value) - return; - - auto const & info = m_value->GetRankingInfo(); - m_rank = info.GetLinearModelRank(); - m_distanceToPivot = info.m_distanceToPivot; - } - - impl::PreResult2 const & operator*() const { return *m_value; } - - inline double GetRank() const { return m_rank; } - - inline double GetDistanceToPivot() const { return m_distanceToPivot; } -}; - -void RemoveDuplicatingLinear(vector & indV) -{ - impl::PreResult2::LessLinearTypesF lessCmp; - impl::PreResult2::EqualLinearTypesF equalCmp; - - sort(indV.begin(), indV.end(), [&lessCmp](IndexedValue const & lhs, IndexedValue const & rhs) - { - return lessCmp(*lhs, *rhs); - }); - - indV.erase(unique(indV.begin(), indV.end(), - [&equalCmp](IndexedValue const & lhs, IndexedValue const & rhs) - { - return equalCmp(*lhs, *rhs); - }), - indV.end()); -} - m2::RectD NormalizeViewport(m2::RectD viewport) { m2::RectD minViewport = MercatorBounds::RectByCenterXYAndSizeInMeters( @@ -176,23 +120,6 @@ m2::RectD GetRectAroundPosition(m2::PointD const & position) return MercatorBounds::RectByCenterXYAndSizeInMeters(position, kMaxPositionRadiusM); } -template -void UpdateNameScore(string const & name, TSlice const & slice, NameScore & bestScore) -{ - auto const score = GetNameScore(name, slice); - if (score > bestScore) - bestScore = score; -} - -template -void UpdateNameScore(vector const & tokens, TSlice const & slice, - NameScore & bestScore) -{ - auto const score = GetNameScore(tokens, slice); - if (score > bestScore) - bestScore = score; -} - inline bool IsHashtagged(strings::UniString const & s) { return !s.empty() && s[0] == '#'; } inline strings::UniString RemoveHashtag(strings::UniString const & s) @@ -224,8 +151,6 @@ Processor::Processor(Index & index, CategoriesHolder const & categories, , m_mode(Mode::Everywhere) , m_worldSearch(true) , m_suggestsEnabled(true) - , m_viewportSearch(false) - , m_keepHouseNumberInQuery(true) , m_preRanker(kPreResultsCount) , m_geocoder(index, infoGetter, static_cast(*this)) , m_reverseGeocoder(index) @@ -241,6 +166,8 @@ Processor::Processor(Index & index, CategoriesHolder const & categories, m_keywordsScorer.SetLanguages(langPriorities); SetPreferredLocale("en"); + + m_ranker = make_unique(m_preRanker, *this); } void Processor::Init(bool viewportSearch) @@ -248,7 +175,7 @@ void Processor::Init(bool viewportSearch) m_tokens.clear(); m_prefix.clear(); m_preRanker.Clear(); - m_viewportSearch = viewportSearch; + m_ranker->Init(viewportSearch); } void Processor::SetViewport(m2::RectD const & viewport, bool forceUpdate) @@ -347,7 +274,7 @@ void Processor::SetQuery(string const & query) // get preffered types to show in results m_prefferedTypes.clear(); - ForEachCategoryTypes(QuerySliceOnRawStrings(m_tokens, m_prefix), + ForEachCategoryType(QuerySliceOnRawStrings(m_tokens, m_prefix), [&](size_t, uint32_t t) { m_prefferedTypes.insert(t); @@ -450,8 +377,8 @@ int Processor::GetCategoryLocales(int8_t(&arr)[3]) const return count; } -template -void Processor::ForEachCategoryTypes(StringSliceBase const & slice, ToDo toDo) const +void Processor::ForEachCategoryType(StringSliceBase const & slice, + function const & fn) const { int8_t arrLocales[3]; int const localesCount = GetCategoryLocales(arrLocales); @@ -460,14 +387,14 @@ void Processor::ForEachCategoryTypes(StringSliceBase const & slice, ToDo toDo) c { auto token = RemoveHashtag(slice.Get(i)); for (int j = 0; j < localesCount; ++j) - m_categories.ForEachTypeByName(arrLocales[j], token, bind(ref(toDo), i, _1)); - ProcessEmojiIfNeeded(token, i, toDo); + m_categories.ForEachTypeByName(arrLocales[j], token, bind(fn, i, _1)); + ProcessEmojiIfNeeded(token, i, fn); } } -template +// template void Processor::ProcessEmojiIfNeeded(strings::UniString const & token, size_t ind, - ToDo & toDo) const + function const & fn) const { // Special process of 2 codepoints emoji (e.g. black guy on a bike). // Only emoji synonyms can have one codepoint. @@ -476,7 +403,7 @@ void Processor::ProcessEmojiIfNeeded(strings::UniString const & token, size_t in static int8_t const enLocaleCode = CategoriesHolder::MapLocaleToInteger("en"); m_categories.ForEachTypeByName(enLocaleCode, strings::UniString(1, token[0]), - bind(ref(toDo), ind, _1)); + bind(fn, ind, _1)); } } @@ -495,7 +422,7 @@ void Processor::Search(Results & results, size_t limit) m_geocoder.GoEverywhere(m_preRanker); - FlushResults(params, results, limit); + m_ranker->FlushResults(params, results, limit); } void Processor::SearchViewportPoints(Results & results) @@ -509,7 +436,7 @@ void Processor::SearchViewportPoints(Results & results) m_geocoder.GoInViewport(m_preRanker); - FlushViewportResults(params, results); + m_ranker->FlushViewportResults(params, results); } void Processor::SearchCoordinates(Results & res) const @@ -518,234 +445,7 @@ void Processor::SearchCoordinates(Results & res) const if (MatchLatLonDegree(m_query, lat, lon)) { ASSERT_EQUAL(res.GetCount(), 0, ()); - res.AddResultNoChecks(MakeResult(impl::PreResult2(lat, lon))); - } -} - -namespace -{ -bool IsResultExists(impl::PreResult2 const & p, vector const & indV) -{ - impl::PreResult2::StrictEqualF equalCmp(p); - // Do not insert duplicating results. - return indV.end() != find_if(indV.begin(), indV.end(), [&equalCmp](IndexedValue const & iv) - { - return equalCmp(*iv); - }); -} -} // namespace - -namespace impl -{ -class PreResult2Maker -{ - Processor & m_processor; - Geocoder::Params const & m_params; - - unique_ptr m_pFV; - - // For the best performance, incoming id's should be sorted by id.first (mwm file id). - void LoadFeature(FeatureID const & id, FeatureType & f, m2::PointD & center, string & name, - string & country) - { - if (m_pFV.get() == 0 || m_pFV->GetId() != id.m_mwmId) - m_pFV.reset(new Index::FeaturesLoaderGuard(m_processor.m_index, id.m_mwmId)); - - m_pFV->GetFeatureByIndex(id.m_index, f); - f.SetID(id); - - center = feature::GetCenter(f); - - m_processor.GetBestMatchName(f, name); - - // country (region) name is a file name if feature isn't from World.mwm - if (m_pFV->IsWorld()) - country.clear(); - else - country = m_pFV->GetCountryFileName(); - } - - void InitRankingInfo(FeatureType const & ft, m2::PointD const & center, - impl::PreResult1 const & res, search::RankingInfo & info) - { - auto const & preInfo = res.GetInfo(); - - auto const & pivot = m_params.m_accuratePivotCenter; - - info.m_distanceToPivot = MercatorBounds::DistanceOnEarth(center, pivot); - info.m_rank = preInfo.m_rank; - info.m_searchType = preInfo.m_searchType; - info.m_nameScore = NAME_SCORE_ZERO; - - TokenSlice slice(m_params, preInfo.m_startToken, preInfo.m_endToken); - TokenSliceNoCategories sliceNoCategories(m_params, preInfo.m_startToken, preInfo.m_endToken); - - for (auto const & lang : m_params.m_langs) - { - string name; - if (!ft.GetName(lang, name)) - continue; - vector tokens; - SplitUniString(NormalizeAndSimplifyString(name), MakeBackInsertFunctor(tokens), Delimiters()); - - UpdateNameScore(tokens, slice, info.m_nameScore); - UpdateNameScore(tokens, sliceNoCategories, info.m_nameScore); - } - - if (info.m_searchType == SearchModel::SEARCH_TYPE_BUILDING) - UpdateNameScore(ft.GetHouseNumber(), sliceNoCategories, info.m_nameScore); - - feature::TypesHolder holder(ft); - vector> matched(slice.Size()); - m_processor.ForEachCategoryTypes(QuerySlice(slice), [&](size_t i, uint32_t t) - { - ++matched[i].second; - if (holder.Has(t)) - ++matched[i].first; - }); - - info.m_pureCats = all_of(matched.begin(), matched.end(), [](pair const & m) - { - return m.first != 0; - }); - info.m_falseCats = all_of(matched.begin(), matched.end(), [](pair const & m) - { - return m.first == 0 && m.second != 0; - }); - } - - uint8_t NormalizeRank(uint8_t rank, SearchModel::SearchType type, m2::PointD const & center, - string const & country) - { - switch (type) - { - case SearchModel::SEARCH_TYPE_VILLAGE: return rank /= 1.5; - case SearchModel::SEARCH_TYPE_CITY: - { - if (m_processor.GetViewport(Processor::CURRENT_V).IsPointInside(center)) - return rank * 2; - - storage::CountryInfo info; - if (country.empty()) - m_processor.m_infoGetter.GetRegionInfo(center, info); - else - m_processor.m_infoGetter.GetRegionInfo(country, info); - if (info.IsNotEmpty() && info.m_name == m_processor.GetPivotRegion()) - return rank *= 1.7; - } - case SearchModel::SEARCH_TYPE_COUNTRY: - return rank /= 1.5; - - // For all other search types, rank should be zero for now. - default: return 0; - } - } - -public: - explicit PreResult2Maker(Processor & q, Geocoder::Params const & params) - : m_processor(q), m_params(params) - { - } - - unique_ptr operator()(impl::PreResult1 const & res1) - { - FeatureType ft; - m2::PointD center; - string name; - string country; - - LoadFeature(res1.GetId(), ft, center, name, country); - - auto res2 = make_unique(ft, &res1, center, - m_processor.GetPosition() /* pivot */, name, country); - - search::RankingInfo info; - InitRankingInfo(ft, center, res1, info); - info.m_rank = NormalizeRank(info.m_rank, info.m_searchType, center, country); - res2->SetRankingInfo(move(info)); - - return res2; - } -}; -} // namespace impl - -template -void Processor::MakePreResult2(Geocoder::Params const & params, vector & cont, - vector & streets) -{ - m_preRanker.Filter(m_viewportSearch); - - // Makes PreResult2 vector. - impl::PreResult2Maker maker(*this, params); - m_preRanker.ForEach( - [&](impl::PreResult1 const & r) - { - auto p = maker(r); - if (!p) - return; - - if (params.m_mode == Mode::Viewport && !params.m_pivot.IsPointInside(p->GetCenter())) - return; - - if (p->IsStreet()) - streets.push_back(p->GetID()); - - if (!IsResultExists(*p, cont)) - cont.push_back(IndexedValue(move(p))); - }); -} - -void Processor::FlushResults(Geocoder::Params const & params, Results & res, size_t resCount) -{ - vector indV; - vector streets; - - MakePreResult2(params, indV, streets); - RemoveDuplicatingLinear(indV); - if (indV.empty()) - return; - - sort(indV.rbegin(), indV.rend(), my::LessBy(&IndexedValue::GetRank)); - - ProcessSuggestions(indV, res); - - // Emit feature results. - size_t count = res.GetCount(); - for (size_t i = 0; i < indV.size() && count < resCount; ++i) - { - if (IsCancelled()) - break; - - LOG(LDEBUG, (indV[i])); - - auto const & preResult2 = *indV[i]; - if (res.AddResult(MakeResult(preResult2))) - ++count; - } -} - -void Processor::FlushViewportResults(Geocoder::Params const & params, Results & res) -{ - vector indV; - vector streets; - - MakePreResult2(params, indV, streets); - RemoveDuplicatingLinear(indV); - if (indV.empty()) - return; - - sort(indV.begin(), indV.end(), my::LessBy(&IndexedValue::GetDistanceToPivot)); - - for (size_t i = 0; i < indV.size(); ++i) - { - if (IsCancelled()) - break; - - res.AddResultNoChecks( - (*(indV[i])) - .GenerateFinalResult(m_infoGetter, &m_categories, &m_prefferedTypes, - m_currentLocaleCode, - nullptr /* Viewport results don't need calculated address */)); + res.AddResultNoChecks(MakeResult(PreResult2(lat, lon))); } } @@ -815,8 +515,7 @@ void Processor::GetSuggestion(string const & name, string & suggest) const } } -template -void Processor::ProcessSuggestions(vector & vec, Results & res) const +void Processor::ProcessSuggestions(vector & vec, Results & res) const { if (m_prefix.empty() || !m_suggestsEnabled) return; @@ -824,7 +523,7 @@ void Processor::ProcessSuggestions(vector & vec, Results & res) const int added = 0; for (auto i = vec.begin(); i != vec.end();) { - impl::PreResult2 const & r = **i; + PreResult2 const & r = **i; ftypes::Type const type = GetLocalityIndex(r.GetTypes()); if ((type == ftypes::COUNTRY || type == ftypes::CITY) || r.IsStreet()) @@ -844,8 +543,6 @@ void Processor::ProcessSuggestions(vector & vec, Results & res) const } } -namespace impl -{ class BestNameFinder { KeywordLangMatcher::ScoreT m_score; @@ -869,11 +566,10 @@ public: return true; } }; -} // namespace impl void Processor::GetBestMatchName(FeatureType const & f, string & name) const { - impl::BestNameFinder finder(name, m_keywordsScorer); + BestNameFinder finder(name, m_keywordsScorer); UNUSED_VALUE(f.ForEachName(finder)); } @@ -926,12 +622,11 @@ public: void operator()(pair const & range) { m_res.AddHighlightRange(range); } }; -Result Processor::MakeResult(impl::PreResult2 const & r) const +Result Processor::MakeResult(PreResult2 const & r) const { Result res = r.GenerateFinalResult(m_infoGetter, &m_categories, &m_prefferedTypes, m_currentLocaleCode, &m_reverseGeocoder); MakeResultHighlight(res); - #ifdef FIND_LOCALITY_TEST if (ftypes::IsLocalityChecker::Instance().GetType(r.GetTypes()) == ftypes::NONE) { @@ -1144,7 +839,7 @@ void Processor::InitParams(QueryParams & params) } } }; - ForEachCategoryTypes(QuerySliceOnRawStrings(m_tokens, m_prefix), addSyms); + ForEachCategoryType(QuerySliceOnRawStrings(m_tokens, m_prefix), addSyms); for (auto & tokens : params.m_tokens) { diff --git a/search/processor.hpp b/search/processor.hpp index a568e43991..1cdadb22f1 100644 --- a/search/processor.hpp +++ b/search/processor.hpp @@ -3,6 +3,7 @@ #include "search/keyword_lang_matcher.hpp" #include "search/mode.hpp" #include "search/pre_ranker.hpp" +#include "search/ranker.hpp" #include "search/rank_table_cache.hpp" #include "search/reverse_geocoder.hpp" #include "search/search_trie.hpp" @@ -21,9 +22,11 @@ #include "base/limited_priority_queue.hpp" #include "base/string_utils.hpp" +#include "std/function.hpp" #include "std/map.hpp" #include "std/string.hpp" #include "std/unordered_set.hpp" +#include "std/unique_ptr.hpp" #include "std/vector.hpp" #define FIND_LOCALITY_TEST @@ -53,15 +56,14 @@ struct QueryParams; class ReverseGeocoder; class Geocoder; +class Ranker; +// todo(@m) Merge with Ranker. +class PreResult2Maker; -namespace impl -{ class FeatureLoader; class BestNameFinder; -class PreResult2Maker; class DoFindLocality; class HouseCompFactory; -} class Processor : public my::Cancellable { @@ -128,17 +130,22 @@ protected: friend string DebugPrint(ViewportID viewportId); - friend class impl::FeatureLoader; - friend class impl::BestNameFinder; - friend class impl::PreResult2Maker; - friend class impl::DoFindLocality; - friend class impl::HouseCompFactory; + friend class FeatureLoader; + friend class BestNameFinder; + friend class DoFindLocality; + friend class HouseCompFactory; + friend class Ranker; + friend class PreResult2Maker; int GetCategoryLocales(int8_t(&arr)[3]) const; - template - void ForEachCategoryTypes(StringSliceBase const & slice, ToDo toDo) const; - template - void ProcessEmojiIfNeeded(strings::UniString const & token, size_t ind, ToDo & toDo) const; + + void ForEachCategoryType( + StringSliceBase const & slice, + function const & fn) const; + + void ProcessEmojiIfNeeded( + strings::UniString const & token, size_t ind, + function const & fn) const; using TMWMVector = vector>; using TOffsetsVector = map>; @@ -150,17 +157,10 @@ protected: void SetViewportByIndex(m2::RectD const & viewport, size_t idx, bool forceUpdate); void ClearCache(size_t ind); - template - void MakePreResult2(Geocoder::Params const & params, vector & cont, - vector & streets); - - void FlushResults(Geocoder::Params const & params, Results & res, size_t resCount); - void FlushViewportResults(Geocoder::Params const & params, Results & res); - void RemoveStringPrefix(string const & str, string & res) const; void GetSuggestion(string const & name, string & suggest) const; - template - void ProcessSuggestions(vector & vec, Results & res) const; + + void ProcessSuggestions(vector & vec, Results & res) const; void SuggestStrings(Results & res); void MatchForSuggestionsImpl(strings::UniString const & token, int8_t locale, @@ -168,7 +168,7 @@ protected: void GetBestMatchName(FeatureType const & f, string & name) const; - Result MakeResult(impl::PreResult2 const & r) const; + Result MakeResult(PreResult2 const & r) const; void MakeResultHighlight(Result & res) const; Index & m_index; @@ -208,9 +208,9 @@ protected: protected: bool m_viewportSearch; - bool m_keepHouseNumberInQuery; PreRanker m_preRanker; + unique_ptr m_ranker; Geocoder m_geocoder; ReverseGeocoder const m_reverseGeocoder; }; diff --git a/search/ranker.cpp b/search/ranker.cpp new file mode 100644 index 0000000000..801c373dbf --- /dev/null +++ b/search/ranker.cpp @@ -0,0 +1,270 @@ +#include "search/ranker.hpp" +#include "search/processor.hpp" +#include "search/token_slice.hpp" + +#include "indexer/feature_algo.hpp" + +#include "base/logging.hpp" + +#include "std/algorithm.hpp" + +namespace search +{ +namespace +{ +template +void UpdateNameScore(string const & name, TSlice const & slice, NameScore & bestScore) +{ + auto const score = GetNameScore(name, slice); + if (score > bestScore) + bestScore = score; +} + +template +void UpdateNameScore(vector const & tokens, TSlice const & slice, + NameScore & bestScore) +{ + auto const score = GetNameScore(tokens, slice); + if (score > bestScore) + bestScore = score; +} + +void RemoveDuplicatingLinear(vector & indV) +{ + PreResult2::LessLinearTypesF lessCmp; + PreResult2::EqualLinearTypesF equalCmp; + + sort(indV.begin(), indV.end(), [&lessCmp](IndexedValue const & lhs, IndexedValue const & rhs) + { + return lessCmp(*lhs, *rhs); + }); + + indV.erase(unique(indV.begin(), indV.end(), + [&equalCmp](IndexedValue const & lhs, IndexedValue const & rhs) + { + return equalCmp(*lhs, *rhs); + }), + indV.end()); +} +} // namespace + +class PreResult2Maker +{ + Processor & m_processor; + Geocoder::Params const & m_params; + + unique_ptr m_pFV; + + // For the best performance, incoming id's should be sorted by id.first (mwm file id). + void LoadFeature(FeatureID const & id, FeatureType & f, m2::PointD & center, string & name, + string & country) + { + if (m_pFV.get() == 0 || m_pFV->GetId() != id.m_mwmId) + m_pFV.reset(new Index::FeaturesLoaderGuard(m_processor.m_index, id.m_mwmId)); + + m_pFV->GetFeatureByIndex(id.m_index, f); + f.SetID(id); + + center = feature::GetCenter(f); + + m_processor.GetBestMatchName(f, name); + + // country (region) name is a file name if feature isn't from World.mwm + if (m_pFV->IsWorld()) + country.clear(); + else + country = m_pFV->GetCountryFileName(); + } + + void InitRankingInfo(FeatureType const & ft, m2::PointD const & center, PreResult1 const & res, + search::RankingInfo & info) + { + auto const & preInfo = res.GetInfo(); + + auto const & pivot = m_params.m_accuratePivotCenter; + + info.m_distanceToPivot = MercatorBounds::DistanceOnEarth(center, pivot); + info.m_rank = preInfo.m_rank; + info.m_searchType = preInfo.m_searchType; + info.m_nameScore = NAME_SCORE_ZERO; + + TokenSlice slice(m_params, preInfo.m_startToken, preInfo.m_endToken); + TokenSliceNoCategories sliceNoCategories(m_params, preInfo.m_startToken, preInfo.m_endToken); + + for (auto const & lang : m_params.m_langs) + { + string name; + if (!ft.GetName(lang, name)) + continue; + vector tokens; + SplitUniString(NormalizeAndSimplifyString(name), MakeBackInsertFunctor(tokens), Delimiters()); + + UpdateNameScore(tokens, slice, info.m_nameScore); + UpdateNameScore(tokens, sliceNoCategories, info.m_nameScore); + } + + if (info.m_searchType == SearchModel::SEARCH_TYPE_BUILDING) + UpdateNameScore(ft.GetHouseNumber(), sliceNoCategories, info.m_nameScore); + + feature::TypesHolder holder(ft); + vector> matched(slice.Size()); + m_processor.ForEachCategoryType(QuerySlice(slice), [&](size_t i, uint32_t t) + { + ++matched[i].second; + if (holder.Has(t)) + ++matched[i].first; + }); + + info.m_pureCats = all_of(matched.begin(), matched.end(), [](pair const & m) + { + return m.first != 0; + }); + info.m_falseCats = all_of(matched.begin(), matched.end(), [](pair const & m) + { + return m.first == 0 && m.second != 0; + }); + } + + uint8_t NormalizeRank(uint8_t rank, SearchModel::SearchType type, m2::PointD const & center, + string const & country) + { + switch (type) + { + case SearchModel::SEARCH_TYPE_VILLAGE: return rank /= 1.5; + case SearchModel::SEARCH_TYPE_CITY: + { + if (m_processor.GetViewport(Processor::CURRENT_V).IsPointInside(center)) + return rank * 2; + + storage::CountryInfo info; + if (country.empty()) + m_processor.m_infoGetter.GetRegionInfo(center, info); + else + m_processor.m_infoGetter.GetRegionInfo(country, info); + if (info.IsNotEmpty() && info.m_name == m_processor.GetPivotRegion()) + return rank *= 1.7; + } + case SearchModel::SEARCH_TYPE_COUNTRY: + return rank /= 1.5; + + // For all other search types, rank should be zero for now. + default: return 0; + } + } + +public: + explicit PreResult2Maker(Processor & q, Geocoder::Params const & params) + : m_processor(q), m_params(params) + { + } + + unique_ptr operator()(PreResult1 const & res1) + { + FeatureType ft; + m2::PointD center; + string name; + string country; + + LoadFeature(res1.GetId(), ft, center, name, country); + + auto res2 = make_unique(ft, &res1, center, m_processor.GetPosition() /* pivot */, + name, country); + + search::RankingInfo info; + InitRankingInfo(ft, center, res1, info); + info.m_rank = NormalizeRank(info.m_rank, info.m_searchType, center, country); + res2->SetRankingInfo(move(info)); + + return res2; + } +}; + +bool Ranker::IsResultExists(PreResult2 const & p, vector const & indV) +{ + PreResult2::StrictEqualF equalCmp(p); + // Do not insert duplicating results. + return indV.end() != find_if(indV.begin(), indV.end(), [&equalCmp](IndexedValue const & iv) + { + return equalCmp(*iv); + }); +} + +void Ranker::MakePreResult2(Geocoder::Params const & params, vector & cont, + vector & streets) +{ + m_preRanker.Filter(m_viewportSearch); + + // Makes PreResult2 vector. + PreResult2Maker maker(m_processor, params); + m_preRanker.ForEach( + [&](PreResult1 const & r) + { + auto p = maker(r); + if (!p) + return; + + if (params.m_mode == Mode::Viewport && !params.m_pivot.IsPointInside(p->GetCenter())) + return; + + if (p->IsStreet()) + streets.push_back(p->GetID()); + + if (!IsResultExists(*p, cont)) + cont.push_back(IndexedValue(move(p))); + }); +} + +void Ranker::FlushResults(Geocoder::Params const & params, Results & res, size_t resCount) +{ + vector indV; + vector streets; + + MakePreResult2(params, indV, streets); + RemoveDuplicatingLinear(indV); + if (indV.empty()) + return; + + sort(indV.rbegin(), indV.rend(), my::LessBy(&IndexedValue::GetRank)); + + m_processor.ProcessSuggestions(indV, res); + + // Emit feature results. + size_t count = res.GetCount(); + for (size_t i = 0; i < indV.size() && count < resCount; ++i) + { + if (m_processor.IsCancelled()) + break; + + LOG(LDEBUG, (indV[i])); + + auto const & preResult2 = *indV[i]; + if (res.AddResult(m_processor.MakeResult(preResult2))) + ++count; + } +} + +void Ranker::FlushViewportResults(Geocoder::Params const & params, Results & res) +{ + vector indV; + vector streets; + + MakePreResult2(params, indV, streets); + RemoveDuplicatingLinear(indV); + if (indV.empty()) + return; + + sort(indV.begin(), indV.end(), my::LessBy(&IndexedValue::GetDistanceToPivot)); + + for (size_t i = 0; i < indV.size(); ++i) + { + if (m_processor.IsCancelled()) + break; + + res.AddResultNoChecks( + (*(indV[i])) + .GenerateFinalResult(m_processor.m_infoGetter, &m_processor.m_categories, + &m_processor.m_prefferedTypes, m_processor.m_currentLocaleCode, + nullptr /* Viewport results don't need calculated address */)); + } +} +} // namespace search diff --git a/search/ranker.hpp b/search/ranker.hpp new file mode 100644 index 0000000000..c01dacb958 --- /dev/null +++ b/search/ranker.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "search/geocoder.hpp" +#include "search/intermediate_result.hpp" +#include "search/mode.hpp" +#include "search/processor.hpp" + +#include "indexer/feature_decl.hpp" + +#include "std/vector.hpp" + +namespace search +{ +class PreResult2Maker; +class Processor; + +class Ranker +{ +public: + Ranker(PreRanker & preRanker, Processor & processor) + : m_viewportSearch(false), m_preRanker(preRanker), m_processor(processor) + { + } + + void Init(bool viewportSearch) { m_viewportSearch = viewportSearch; } + + bool IsResultExists(PreResult2 const & p, vector const & indV); + + void MakePreResult2(Geocoder::Params const & params, vector & cont, + vector & streets); + + Result MakeResult(PreResult2 const & r) const; + + void FlushResults(Geocoder::Params const & params, Results & res, size_t resCount); + void FlushViewportResults(Geocoder::Params const & params, Results & res); + +private: + bool m_viewportSearch; + PreRanker & m_preRanker; + + // todo(@m) Remove. + Processor & m_processor; +}; + +} // namespace search diff --git a/search/search.pro b/search/search.pro index 96405af26a..6be1bd7a85 100644 --- a/search/search.pro +++ b/search/search.pro @@ -49,6 +49,7 @@ HEADERS += \ query_params.hpp \ query_saver.hpp \ rank_table_cache.hpp \ + ranker.hpp \ ranking_info.hpp \ ranking_utils.hpp \ region.hpp \ @@ -99,6 +100,7 @@ SOURCES += \ query_params.cpp \ query_saver.cpp \ rank_table_cache.cpp \ + ranker.cpp \ ranking_info.cpp \ ranking_utils.cpp \ region.cpp \