diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index 70dbe51058..fd88356342 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -55,8 +55,9 @@ void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) namespace impl { -PreResult1::PreResult1(FeatureID const & fID, uint8_t rank, double priority, int8_t viewportID) - : m_id(fID), m_priority(priority), m_rank(rank), m_viewportID(viewportID) +PreResult1::PreResult1(FeatureID const & fID, double priority, int8_t viewportID, + v2::PreRankingInfo const & info) + : m_id(fID), m_priority(priority), m_viewportID(viewportID), m_info(info) { ASSERT(m_id.IsValid(), ()); } @@ -66,8 +67,8 @@ PreResult1::PreResult1(double priority) : m_priority(priority) {} // static bool PreResult1::LessRank(PreResult1 const & r1, PreResult1 const & r2) { - if (r1.m_rank != r2.m_rank) - return r1.m_rank > r2.m_rank; + if (r1.m_info.m_rank != r2.m_info.m_rank) + return r1.m_info.m_rank > r2.m_info.m_rank; return r1.m_priority < r2.m_priority; } @@ -76,7 +77,7 @@ bool PreResult1::LessPriority(PreResult1 const & r1, PreResult1 const & r2) { if (r1.m_priority != r2.m_priority) return r1.m_priority < r2.m_priority; - return r1.m_rank > r2.m_rank; + return r1.m_info.m_rank > r2.m_info.m_rank; } // static @@ -98,8 +99,6 @@ PreResult2::PreResult2(FeatureType const & f, PreResult1 const * p, m2::PointD c m_types.SortBySpec(); - m_rank = p ? p->GetRank() : 0; - m2::PointD fCenter; fCenter = f.GetLimitRect(FeatureType::WORST_GEOMETRY).Center(); @@ -181,7 +180,7 @@ Result PreResult2::GenerateFinalResult(storage::CountryInfoGetter const & infoGe case RESULT_FEATURE: return Result(m_id, GetCenter(), m_str, regionName, pCat->GetReadableFeatureType(type, locale) #ifdef DEBUG - + ' ' + strings::to_string(int(m_rank)) + + ' ' + strings::to_string(int(m_info.m_rank)) #endif , type, m_metadata); @@ -206,8 +205,8 @@ Result PreResult2::GeneratePointResult(storage::CountryInfoGetter const & infoGe // static bool PreResult2::LessRank(PreResult2 const & r1, PreResult2 const & r2) { - if (r1.m_rank != r2.m_rank) - return r1.m_rank > r2.m_rank; + if (r1.m_info.m_rank != r2.m_info.m_rank) + return r1.m_info.m_rank > r2.m_info.m_rank; return r1.m_distance < r2.m_distance; } @@ -216,7 +215,7 @@ bool PreResult2::LessDistance(PreResult2 const & r1, PreResult2 const & r2) { if (r1.m_distance != r2.m_distance) return r1.m_distance < r2.m_distance; - return r1.m_rank > r2.m_rank; + return r1.m_info.m_rank > r2.m_info.m_rank; } bool PreResult2::StrictEqualF::operator() (PreResult2 const & r) const @@ -275,7 +274,7 @@ string PreResult2::DebugPrint() const ss << "{ IntermediateResult: " << "Name: " << m_str << "; Type: " << GetBestType() << - "; Rank: " << int(m_rank) << + "; Rank: " << static_cast(m_info.m_rank) << "; Distance: " << m_distance << " }"; return ss.str(); } diff --git a/search/intermediate_result.hpp b/search/intermediate_result.hpp index 00105836b6..28bd8a1acd 100644 --- a/search/intermediate_result.hpp +++ b/search/intermediate_result.hpp @@ -1,9 +1,11 @@ #pragma once -#include "result.hpp" +#include "search/result.hpp" +#include "search/v2/pre_ranking_info.hpp" +#include "search/v2/ranking_info.hpp" +#include "search/v2/ranking_utils.hpp" #include "indexer/feature_data.hpp" - class FeatureType; class CategoriesHolder; @@ -25,11 +27,13 @@ class PreResult1 FeatureID m_id; double m_priority; - uint8_t m_rank; int8_t m_viewportID; + v2::PreRankingInfo m_info; + public: - PreResult1(FeatureID const & fID, uint8_t rank, double priority, int8_t viewportID); + PreResult1(FeatureID const & fID, double priority, int8_t viewportID, + v2::PreRankingInfo const & info); explicit PreResult1(double priority); @@ -38,8 +42,9 @@ public: static bool LessPointsForViewport(PreResult1 const & r1, PreResult1 const & r2); inline FeatureID GetID() const { return m_id; } - inline uint8_t GetRank() const { return m_rank; } + inline uint8_t GetRank() const { return m_info.m_rank; } inline int8_t GetViewportID() const { return m_viewportID; } + inline v2::PreRankingInfo const & GetInfo() const { return m_info; } }; @@ -67,6 +72,14 @@ public: /// For RESULT_BUILDING. PreResult2(m2::PointD const & pt, string const & str, uint32_t type); + inline search::v2::RankingInfo const & GetRankingInfo() const { return m_info; } + + template + inline void SetRankingInfo(TInfo && info) + { + m_info = forward(info); + } + /// @param[in] infoGetter Need to get region for result. /// @param[in] pCat Categories need to display readable type string. /// @param[in] pTypes Set of preffered types that match input tokens by categories. @@ -143,7 +156,7 @@ private: double m_distance; ResultType m_resultType; - uint8_t m_rank; + v2::RankingInfo m_info; feature::EGeomType m_geomType; Result::Metadata m_metadata; diff --git a/search/result.hpp b/search/result.hpp index 3c61b4d2b6..3d3f226066 100644 --- a/search/result.hpp +++ b/search/result.hpp @@ -1,4 +1,6 @@ #pragma once +#include "search/v2/ranking_info.hpp" + #include "indexer/feature_decl.hpp" #include "editor/yes_no_unknown.hpp" @@ -97,6 +99,14 @@ public: int32_t GetPositionInResults() const { return m_positionInResults; } void SetPositionInResults(int32_t pos) { m_positionInResults = pos; } + inline v2::RankingInfo const & GetRankingInfo() const { return m_info; } + + template + inline void SetRankingInfo(TInfo && info) + { + m_info = forward(info); + } + // Returns a representation of this result that is // sent to the statistics servers and later used to measure // the quality of our search engine. @@ -112,6 +122,8 @@ private: string m_suggestionStr; buffer_vector, 4> m_hightlightRanges; + v2::RankingInfo m_info; + // The position that this result occupied in the vector returned // by a search query. -1 if undefined. int32_t m_positionInResults = -1; diff --git a/search/search.pro b/search/search.pro index 83fbce598c..930b8517ea 100644 --- a/search/search.pro +++ b/search/search.pro @@ -52,7 +52,9 @@ HEADERS += \ v2/intersection_result.hpp \ v2/locality_scorer.hpp \ v2/mwm_context.hpp \ + v2/pre_ranking_info.hpp \ v2/rank_table_cache.hpp \ + v2/ranking_info.hpp \ v2/ranking_utils.hpp \ v2/search_model.hpp \ v2/search_query_v2.hpp \ @@ -92,7 +94,9 @@ SOURCES += \ v2/intersection_result.cpp \ v2/locality_scorer.cpp \ v2/mwm_context.cpp \ + v2/pre_ranking_info.cpp \ v2/rank_table_cache.cpp \ + v2/ranking_info.cpp \ v2/ranking_utils.cpp \ v2/search_model.cpp \ v2/search_query_v2.cpp \ diff --git a/search/search_quality/search_quality_tool/search_quality_tool.cpp b/search/search_quality/search_quality_tool/search_quality_tool.cpp index 556e174a28..8e711b1161 100644 --- a/search/search_quality/search_quality_tool/search_quality_tool.cpp +++ b/search/search_quality/search_quality_tool/search_quality_tool.cpp @@ -14,6 +14,7 @@ #include "search/result.hpp" #include "search/search_tests_support/test_search_engine.hpp" #include "search/search_tests_support/test_search_request.hpp" +#include "search/v2/ranking_info.hpp" #include "platform/country_file.hpp" #include "platform/local_country_file.hpp" @@ -59,6 +60,7 @@ DEFINE_string(queries_path, "", "Path to the file with queries"); DEFINE_int32(top, 1, "Number of top results to show for every query"); DEFINE_string(viewport, "", "Viewport to use when searching (default, moscow, london, zurich)"); DEFINE_string(check_completeness, "", "Path to the file with completeness data"); +DEFINE_string(ranking_csv_file, "", "File ranking info will be exported to"); map const kViewports = { {"default", m2::RectD(m2::PointD(0.0, 0.0), m2::PointD(1.0, 1.0))}, @@ -440,6 +442,22 @@ int main(int argc, char * argv[]) engine, MakePrefixFree(queries[i]), FLAGS_locale, search::Mode::Everywhere, viewport)); } + ofstream csv; + bool dumpCSV = false; + if (!FLAGS_ranking_csv_file.empty()) + { + ofstream os(FLAGS_ranking_csv_file); + csv.swap(os); + if (csv.is_open()) + dumpCSV = true; + } + + if (dumpCSV) + { + search::v2::RankingInfo::PrintCSVHeader(csv); + csv << endl; + } + vector responseTimes(queries.size()); for (size_t i = 0; i < queries.size(); ++i) { @@ -448,6 +466,15 @@ int main(int argc, char * argv[]) responseTimes[i] = static_cast(rt) / 1000; PrintTopResults(MakePrefixFree(queries[i]), requests[i]->Results(), FLAGS_top, responseTimes[i]); + + if (dumpCSV) + { + for (auto const & result : requests[i]->Results()) + { + result.GetRankingInfo().ToCSV(csv); + csv << endl; + } + } } double averageTime; diff --git a/search/search_query.cpp b/search/search_query.cpp index 1f274d89d0..95a4098629 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -11,8 +11,12 @@ #include "search/search_index_values.hpp" #include "search/search_query_params.hpp" #include "search/search_string_intersection.hpp" +#include "search/v2/pre_ranking_info.hpp" +#include "search/v2/ranking_info.hpp" +#include "search/v2/ranking_utils.hpp" #include "storage/country_info_getter.hpp" +#include "storage/index.hpp" #include "indexer/categories_holder.hpp" #include "indexer/classificator.hpp" @@ -129,7 +133,7 @@ class IndexedValue : public search::IndexedValueBase shared_ptr m_val; public: - explicit IndexedValue(impl::PreResult2 * v) : m_val(v) {} + explicit IndexedValue(unique_ptr v) : m_val(move(v)) {} impl::PreResult2 const & operator*() const { return *m_val; } }; @@ -500,7 +504,10 @@ void Query::Search(Results & res, size_t resCount) LONG_OP(SearchAddress(res)); LONG_OP(SearchFeatures()); - LONG_OP(FlushResults(res, false /* allMWMs */, resCount, true /* oldHouseSearch */)); + + // TODO (@y): this code is not working and will gone away. + v2::Geocoder::Params params; + LONG_OP(FlushResults(params, res, false /* allMWMs */, resCount, true /* oldHouseSearch */)); } void Query::SearchViewportPoints(Results & res) @@ -508,15 +515,18 @@ void Query::SearchViewportPoints(Results & res) LONG_OP(SearchAddress(res)); LONG_OP(SearchFeaturesInViewport(CURRENT_V)); - FlushViewportResults(res, true /* oldHouseSearch */); + // TODO (@y): this code is not working and will gone away. + v2::Geocoder::Params params; + FlushViewportResults(params, res, true /* oldHouseSearch */); } -void Query::FlushViewportResults(Results & res, bool oldHouseSearch) +void Query::FlushViewportResults(v2::Geocoder::Params const & params, Results & res, + bool oldHouseSearch) { vector indV; vector streets; - MakePreResult2(indV, streets); + MakePreResult2(params, indV, streets); if (indV.empty()) return; @@ -583,9 +593,9 @@ public: bool operator()(ValueT const & r) const { return (m_val.GetID() == r.GetID()); } }; -bool IsResultExists(impl::PreResult2 const * p, vector const & indV) +bool IsResultExists(impl::PreResult2 const & p, vector const & indV) { - impl::PreResult2::StrictEqualF equalCmp(*p); + impl::PreResult2::StrictEqualF equalCmp(p); // Do not insert duplicating results. return indV.end() != find_if(indV.begin(), indV.end(), [&equalCmp](IndexedValue const & iv) { @@ -599,6 +609,9 @@ namespace impl class PreResult2Maker { Query & m_query; + storage::CountryInfoGetter const & m_infoGetter; + v2::Geocoder::Params const & m_params; + string m_positionCountry; unique_ptr m_pFV; @@ -628,18 +641,71 @@ class PreResult2Maker country = m_pFV->GetCountryFileName(); } -public: - explicit PreResult2Maker(Query & q) : m_query(q) {} + void InitRankingInfo(FeatureType const & ft, impl::PreResult1 const & result, + search::v2::RankingInfo & info) + { + auto const & preInfo = result.GetInfo(); + auto const & viewport = m_params.m_viewport; + auto const & position = m_params.m_position; - impl::PreResult2 * operator()(impl::PreResult1 const & res) + info.m_distanceToViewport = viewport.IsEmptyInterior() + ? v2::PreRankingInfo::kMaxDistMeters + : feature::GetMinDistanceMeters(ft, viewport.Center()); + info.m_distanceToPosition = feature::GetMinDistanceMeters(ft, position); + + info.m_rank = preInfo.m_rank; + + info.m_searchType = v2::SearchModel::Instance().GetSearchType(ft); + + info.m_nameScore = v2::NAME_SCORE_ZERO; + for (auto const & lang : m_params.m_langs) + { + string name; + if (!ft.GetName(lang, name)) + continue; + auto score = GetNameScore(name, m_params, preInfo.m_startToken, preInfo.m_endToken); + if (score > info.m_nameScore) + info.m_nameScore = score; + } + + if (info.m_searchType == v2::SearchModel::SEARCH_TYPE_BUILDING) + { + string houseNumber = ft.GetHouseNumber(); + auto score = GetNameScore(houseNumber, m_params, preInfo.m_startToken, preInfo.m_endToken); + if (score > info.m_nameScore) + info.m_nameScore = score; + } + + auto const featureCountry = m_infoGetter.GetRegionCountryId(feature::GetCenter(ft)); + // TODO (@y, @m, @vng): exact check is too restrictive, as we + // switched to small mwms. Probably it's worth here to find a + // Least-Common-Ancestor for feature center and user position in + // the country tree, and use level (distance to root) of the + // result here. + info.m_sameCountry = (featureCountry == m_positionCountry); + + info.m_positionInViewport = viewport.IsPointInside(position); + } + +public: + explicit PreResult2Maker(Query & q, v2::Geocoder::Params const & params) + : m_query(q), m_infoGetter(m_query.GetCountryInfoGetter()), m_params(params) + { + m_positionCountry = m_infoGetter.GetRegionCountryId(m_params.m_position); + } + + unique_ptr operator()(impl::PreResult1 const & res) { FeatureType feature; string name, country; LoadFeature(res.GetID(), feature, name, country); Query::ViewportID const viewportID = static_cast(res.GetViewportID()); - impl::PreResult2 * res2 = - new impl::PreResult2(feature, &res, m_query.GetPosition(viewportID), name, country); + auto res2 = make_unique(feature, &res, m_query.GetPosition(viewportID), name, + country); + search::v2::RankingInfo info; + InitRankingInfo(feature, res, info); + res2->SetRankingInfo(move(info)); /// @todo: add exluding of states (without USA states), continents using namespace ftypes; @@ -647,22 +713,22 @@ public: switch (tp) { case COUNTRY: - res2->m_rank /= 1.5; + res2->m_info.m_rank /= 1.5; break; case CITY: case TOWN: if (m_query.GetViewport(Query::CURRENT_V).IsPointInside(res2->GetCenter())) - res2->m_rank *= 2; + res2->m_info.m_rank *= 2; else { storage::CountryInfo ci; res2->m_region.GetRegion(m_query.m_infoGetter, ci); if (ci.IsNotEmpty() && ci.m_name == m_query.GetPivotRegion()) - res2->m_rank *= 1.7; + res2->m_info.m_rank *= 1.7; } break; case VILLAGE: - res2->m_rank /= 1.5; + res2->m_info.m_rank /= 1.5; break; default: break; @@ -670,18 +736,6 @@ public: return res2; } - - impl::PreResult2 * operator()(FeatureID const & id) - { - FeatureType feature; - string name, country; - LoadFeature(id, feature, name, country); - - if (!name.empty() && !country.empty()) - return new impl::PreResult2(feature, 0, m_query.GetPosition(), name, country); - else - return 0; - } }; class HouseCompFactory @@ -715,7 +769,8 @@ public: } // namespace impl template -void Query::MakePreResult2(vector & cont, vector & streets) +void Query::MakePreResult2(v2::Geocoder::Params const & params, vector & cont, + vector & streets) { // make unique set of PreResult1 using TPreResultSet = set; @@ -727,21 +782,19 @@ void Query::MakePreResult2(vector & cont, vector & streets) m_results[i].clear(); } - // make PreResult2 vector - impl::PreResult2Maker maker(*this); + // Makes PreResult2 vector. + impl::PreResult2Maker maker(*this, params); for (auto const & r : theSet) { - impl::PreResult2 * p = maker(r); - if (p == 0) + auto p = maker(r); + if (!p) continue; if (p->IsStreet()) streets.push_back(p->GetID()); - if (IsResultExists(p, cont)) - delete p; - else - cont.push_back(IndexedValue(p)); + if (!IsResultExists(*p, cont)) + cont.push_back(IndexedValue(move(p))); } } @@ -774,12 +827,13 @@ void Query::FlushHouses(Results & res, bool allMWMs, vector const & s } } -void Query::FlushResults(Results & res, bool allMWMs, size_t resCount, bool oldHouseSearch) +void Query::FlushResults(v2::Geocoder::Params const & params, Results & res, bool allMWMs, + size_t resCount, bool oldHouseSearch) { vector indV; vector streets; - MakePreResult2(indV, streets); + MakePreResult2(params, indV, streets); if (indV.empty()) return; @@ -806,7 +860,8 @@ void Query::FlushResults(Results & res, bool allMWMs, size_t resCount, bool oldH LOG(LDEBUG, (indV[i])); - if (res.AddResult(MakeResult(*(indV[i])))) + auto const & preResult2 = *indV[i]; + if (res.AddResult(MakeResult(preResult2))) ++count; } } @@ -907,10 +962,10 @@ void Query::ProcessSuggestions(vector & vec, Results & res) const } } -void Query::AddPreResult1(MwmSet::MwmId const & mwmId, uint32_t featureId, uint8_t rank, - double priority, ViewportID viewportId /*= DEFAULT_V*/) +void Query::AddPreResult1(MwmSet::MwmId const & mwmId, uint32_t featureId, double priority, + v2::PreRankingInfo const & info, ViewportID viewportId /* = DEFAULT_V */) { - impl::PreResult1 res(FeatureID(mwmId, featureId), rank, priority, viewportId); + impl::PreResult1 res(FeatureID(mwmId, featureId), priority, viewportId, info); for (size_t i = 0; i < m_queuesCount; ++i) { @@ -1026,6 +1081,8 @@ Result Query::MakeResult(impl::PreResult2 const & r) const } #endif + res.SetRankingInfo(r.GetRankingInfo()); + return res; } @@ -1294,7 +1351,13 @@ void Query::SearchAddress(Results & res) else { // Add found locality as a result if nothing left to match. - AddPreResult1(mwmId, city.m_featureId, city.m_rank, 1.0 /* priority */); + + // TODO (@y): for backward compatibility with old search + // algorithm carefully fill |info| here. + v2::PreRankingInfo info; + info.m_rank = city.m_rank; + + AddPreResult1(mwmId, city.m_featureId, 1.0 /* priority */, info); } } else if (region.IsValid()) diff --git a/search/search_query.hpp b/search/search_query.hpp index 948cfd0fd2..0510f5c46c 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -4,6 +4,7 @@ #include "search/mode.hpp" #include "search/search_trie.hpp" #include "search/suggest.hpp" +#include "search/v2/geocoder.hpp" #include "search/v2/rank_table_cache.hpp" #include "indexer/ftypes_matcher.hpp" @@ -63,6 +64,11 @@ namespace impl class HouseCompFactory; } +namespace v2 +{ +struct PreRankingInfo; +} + // TODO (@y): rename this class to QueryProcessor. class Query : public my::Cancellable { @@ -97,6 +103,8 @@ public: void SetQuery(string const & query); inline bool IsEmptyQuery() const { return (m_prefix.empty() && m_tokens.empty()); } + inline storage::CountryInfoGetter const & GetCountryInfoGetter() const { return m_infoGetter; } + /// @name Different search functions. //@{ virtual void Search(Results & res, size_t resCount); @@ -151,18 +159,21 @@ protected: bool forceUpdate); void ClearCache(size_t ind); - void AddPreResult1(MwmSet::MwmId const & mwmId, uint32_t featureId, uint8_t rank, double priority, - ViewportID viewportId = DEFAULT_V); + void AddPreResult1(MwmSet::MwmId const & mwmId, uint32_t featureId, double priority, + v2::PreRankingInfo const & info, ViewportID viewportId = DEFAULT_V); - template void MakePreResult2(vector & cont, vector & streets); + template + void MakePreResult2(v2::Geocoder::Params const & params, vector & cont, + vector & streets); /// @param allMWMs Deprecated, need to support old search algorithm. /// @param oldHouseSearch Deprecated, need to support old search algorithm. //@{ void FlushHouses(Results & res, bool allMWMs, vector const & streets); - void FlushResults(Results & res, bool allMWMs, size_t resCount, bool oldHouseSearch); - void FlushViewportResults(Results & res, bool oldHouseSearch); + void FlushResults(v2::Geocoder::Params const & params, Results & res, bool allMWMs, size_t resCount, + bool oldHouseSearch); + void FlushViewportResults(v2::Geocoder::Params const & params, Results & res, bool oldHouseSearch); //@} void RemoveStringPrefix(string const & str, string & res) const; diff --git a/search/v2/geocoder.cpp b/search/v2/geocoder.cpp index 4d18f1dfe1..2c0c9dc1e7 100644 --- a/search/v2/geocoder.cpp +++ b/search/v2/geocoder.cpp @@ -12,7 +12,6 @@ #include "indexer/feature_impl.hpp" #include "indexer/ftypes_matcher.hpp" #include "indexer/index.hpp" -#include "indexer/mwm_set.hpp" #include "indexer/rank_table.hpp" #include "indexer/search_delimiters.hpp" #include "indexer/search_string_utils.hpp" @@ -387,6 +386,11 @@ void UniteCBVs(vector> & cbvs) cbvs.resize(i); } } + +bool SameMwm(Geocoder::TResult const & lhs, Geocoder::TResult const & rhs) +{ + return lhs.first.m_mwmId == rhs.first.m_mwmId; +} } // namespace // Geocoder::Params -------------------------------------------------------------------------------- @@ -453,7 +457,7 @@ void Geocoder::SetParams(Params const & params) LOG(LDEBUG, ("Languages =", m_params.m_langs)); } -void Geocoder::GoEverywhere(vector & results) +void Geocoder::GoEverywhere(TResultList & results) { // TODO (@y): remove following code as soon as Geocoder::Go() will // work fast for most cases (significantly less than 1 second). @@ -480,7 +484,7 @@ void Geocoder::GoEverywhere(vector & results) GoImpl(infos, false /* inViewport */); } -void Geocoder::GoInViewport(vector & results) +void Geocoder::GoInViewport(TResultList & results) { if (m_numTokens == 0) return; @@ -608,6 +612,9 @@ void Geocoder::GoImpl(vector> & infos, bool inViewport) catch (CancelException & e) { } + + // Fill results ranks, as they were missed. + FillResultRanks(); } void Geocoder::ClearCaches() @@ -895,7 +902,7 @@ void Geocoder::MatchRegions(RegionType type) if (AllTokensUsed()) { // Region matches to search query, we need to emit it as is. - EmitResult(m_worldId, region.m_featureId); + EmitResult(m_worldId, region.m_featureId, startToken, endToken); continue; } @@ -941,7 +948,7 @@ void Geocoder::MatchCities() if (AllTokensUsed()) { // City matches to search query. - EmitResult(city.m_countryId, city.m_featureId); + EmitResult(city.m_countryId, city.m_featureId, startToken, endToken); continue; } @@ -1236,19 +1243,68 @@ void Geocoder::FindPaths() sortedLayers.push_back(&layer); sort(sortedLayers.begin(), sortedLayers.end(), my::CompareBy(&FeaturesLayer::m_type)); + auto startToken = sortedLayers.front()->m_startToken; + auto endToken = sortedLayers.front()->m_endToken; + m_finder.ForEachReachableVertex(*m_matcher, sortedLayers, - [this](IntersectionResult const & result) - { + [this, startToken, endToken](IntersectionResult const & result) + { ASSERT(result.IsValid(), ()); // TODO(@y, @m, @vng): use rest fields of IntersectionResult for // better scoring. - EmitResult(m_context->GetId(), result.InnermostResult()); + EmitResult(m_context->GetId(), result.InnermostResult(), startToken, endToken); }); } -void Geocoder::EmitResult(MwmSet::MwmId const & mwmId, uint32_t featureId) +void Geocoder::EmitResult(MwmSet::MwmId const & mwmId, uint32_t ftId, size_t startToken, + size_t endToken) { - m_results->emplace_back(mwmId, featureId); + FeatureID id(mwmId, ftId); + + PreRankingInfo info; + info.m_startToken = startToken; + info.m_endToken = endToken; + if (auto const & mwmInfo = mwmId.GetInfo()) + { + auto const center = mwmInfo->m_limitRect.Center(); + info.m_mwmDistanceToViewport = + MercatorBounds::DistanceOnEarth(center, m_params.m_viewport.Center()); + info.m_mwmDistanceToPosition = MercatorBounds::DistanceOnEarth(center, m_params.m_position); + } + // info.m_ranks will be filled at the end, at once. + + m_results->emplace_back(move(id), move(info)); +} + +void Geocoder::FillResultRanks() +{ + sort(m_results->begin(), m_results->end(), my::CompareBy(&TResult::first)); + + auto ib = m_results->begin(); + while (ib != m_results->end()) + { + auto ie = ib; + while (ie != m_results->end() && SameMwm(*ib, *ie)) + ++ie; + + /// @todo Add RankTableCache here? + MwmSet::MwmHandle handle = m_index.GetMwmHandleById(ib->first.m_mwmId); + if (handle.IsAlive()) + { + auto rankTable = RankTable::Load(handle.GetValue()->m_cont); + if (!rankTable.get()) + rankTable.reset(new DummyRankTable()); + + for (auto ii = ib; ii != ie; ++ii) + { + auto const & id = ii->first; + auto & info = ii->second; + + info.m_rank = rankTable->Get(id.m_index); + } + } + ib = ie; + } } void Geocoder::MatchUnclassified(size_t curToken) @@ -1268,6 +1324,7 @@ void Geocoder::MatchUnclassified(size_t curToken) CBVPtr allFeatures; allFeatures.SetFull(); + auto startToken = curToken; for (curToken = SkipUsedTokens(curToken); curToken < m_numTokens && !m_usedTokens[curToken]; ++curToken) { @@ -1283,7 +1340,7 @@ void Geocoder::MatchUnclassified(size_t curToken) auto emitUnclassified = [&](uint32_t featureId) { if (GetSearchTypeInGeocoding(featureId) == SearchModel::SEARCH_TYPE_UNCLASSIFIED) - EmitResult(m_context->GetId(), featureId); + EmitResult(m_context->GetId(), featureId, startToken, curToken); }; coding::CompressedBitVectorEnumerator::ForEach(*allFeatures, emitUnclassified); } diff --git a/search/v2/geocoder.hpp b/search/v2/geocoder.hpp index b139414a90..850b4c8083 100644 --- a/search/v2/geocoder.hpp +++ b/search/v2/geocoder.hpp @@ -6,9 +6,12 @@ #include "search/v2/features_layer.hpp" #include "search/v2/features_layer_path_finder.hpp" #include "search/v2/mwm_context.hpp" +#include "search/v2/pre_ranking_info.hpp" +#include "search/v2/ranking_utils.hpp" #include "search/v2/search_model.hpp" #include "indexer/index.hpp" +#include "indexer/mwm_set.hpp" #include "storage/country_info_getter.hpp" @@ -21,6 +24,7 @@ #include "base/macros.hpp" #include "base/string_utils.hpp" +#include "std/limits.hpp" #include "std/set.hpp" #include "std/string.hpp" #include "std/unique_ptr.hpp" @@ -123,17 +127,20 @@ public: m2::RectD m_rect; }; + using TResult = pair; + using TResultList = vector>; + Geocoder(Index & index, storage::CountryInfoGetter const & infoGetter); ~Geocoder() override; - // Sets search query params. + // Sets/Gets search query params. void SetParams(Params const & params); // Starts geocoding, retrieved features will be appended to // |results|. - void GoEverywhere(vector & results); - void GoInViewport(vector & results); + void GoEverywhere(TResultList & results); + void GoInViewport(TResultList & results); void ClearCaches(); @@ -215,7 +222,9 @@ private: // the lowest layer. void FindPaths(); - void EmitResult(MwmSet::MwmId const & mwmId, uint32_t featureId); + void EmitResult(MwmSet::MwmId const & mwmId, uint32_t ftId, size_t startToken, size_t endToken); + + void FillResultRanks(); // Tries to match unclassified objects from lower layers, like // parks, forests, lakes, rivers, etc. This method finds all @@ -326,7 +335,7 @@ private: vector m_layers; // Non-owning pointer to a vector of results. - vector * m_results; + TResultList * m_results; }; string DebugPrint(Geocoder::Locality const & locality); diff --git a/search/v2/pre_ranking_info.cpp b/search/v2/pre_ranking_info.cpp new file mode 100644 index 0000000000..fee59350f6 --- /dev/null +++ b/search/v2/pre_ranking_info.cpp @@ -0,0 +1,10 @@ +#include "search/v2/pre_ranking_info.hpp" + +namespace search +{ +namespace v2 +{ +// static +double const PreRankingInfo::kMaxDistMeters = 1000000000; +} // namespace v2 +} // namespace search diff --git a/search/v2/pre_ranking_info.hpp b/search/v2/pre_ranking_info.hpp new file mode 100644 index 0000000000..f5dcae32a3 --- /dev/null +++ b/search/v2/pre_ranking_info.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "std/cstdint.hpp" + +namespace search +{ +namespace v2 +{ +struct PreRankingInfo +{ + static double const kMaxDistMeters; + + // Distance from the mwm center to the current viewport's center. + double m_mwmDistanceToViewport = kMaxDistMeters; + + // Distance from the feature to the current user's position. + double m_mwmDistanceToPosition = kMaxDistMeters; + + // Tokens [m_startToken, m_endToken) match to the query. + size_t m_startToken = 0; + size_t m_endToken = 0; + + // Rank of the feature. + uint8_t m_rank = 0; +}; +} // namespace v2 +} // namespace search diff --git a/search/v2/ranking_info.cpp b/search/v2/ranking_info.cpp new file mode 100644 index 0000000000..96f033dac7 --- /dev/null +++ b/search/v2/ranking_info.cpp @@ -0,0 +1,42 @@ +#include "search/v2/ranking_info.hpp" + +namespace search +{ +namespace v2 +{ +// static +void RankingInfo::PrintCSVHeader(ostream & os) +{ + os << "DistanceToViewport" + << ",DistanceToPosition" + << ",Rank" + << ",NameScore" + << ",SearchType" + << ",SameCountry" + << ",PositionInViewport"; +} + +string DebugPrint(RankingInfo const & info) +{ + ostringstream os; + os << "RankingInfo ["; + os << "m_distanceToViewport:" << info.m_distanceToViewport << ","; + os << "m_distanceToPosition:" << info.m_distanceToPosition << ","; + os << "m_rank:" << static_cast(info.m_rank) << ","; + os << "m_nameScore:" << DebugPrint(info.m_nameScore) << ","; + os << "m_searchType:" << DebugPrint(info.m_searchType) << ","; + os << "m_sameCountry:" << info.m_sameCountry << ","; + os << "m_positionInViewport:" << info.m_positionInViewport; + os << "]"; + return os.str(); +} + +void RankingInfo::ToCSV(ostream & os) const +{ + os << fixed; + os << m_distanceToViewport << "," << m_distanceToPosition << "," << static_cast(m_rank) + << "," << DebugPrint(m_nameScore) << "," << DebugPrint(m_searchType) << "," << m_sameCountry + << "," << m_positionInViewport; +} +} // namespace v2 +} // namespace search diff --git a/search/v2/ranking_info.hpp b/search/v2/ranking_info.hpp new file mode 100644 index 0000000000..651fbe32f8 --- /dev/null +++ b/search/v2/ranking_info.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "search/v2/geocoder.hpp" +#include "search/v2/pre_ranking_info.hpp" +#include "search/v2/ranking_utils.hpp" +#include "search/v2/search_model.hpp" + +class FeatureType; + +namespace search +{ +namespace v2 +{ +struct RankingInfo +{ + // Distance from the feature to the current viewport's center. + double m_distanceToViewport = PreRankingInfo::kMaxDistMeters; + + // Distance from the feature to the current user's position. + double m_distanceToPosition = PreRankingInfo::kMaxDistMeters; + + // Rank of the feature. + uint8_t m_rank = 0; + + // Score for the feature's name. + NameScore m_nameScore = NAME_SCORE_ZERO; + + // Search type for the feature. + SearchModel::SearchType m_searchType = SearchModel::SEARCH_TYPE_COUNT; + + // True if feature is located in the same country as the user. + bool m_sameCountry = false; + + // True if user's position is in viewport. + bool m_positionInViewport = false; + + static void PrintCSVHeader(ostream & os); + + void ToCSV(ostream & os) const; +}; + +string DebugPrint(RankingInfo const & info); +} // namespace v2 +} // namespace search diff --git a/search/v2/ranking_utils.hpp b/search/v2/ranking_utils.hpp index 028f120566..5b6ccf21cb 100644 --- a/search/v2/ranking_utils.hpp +++ b/search/v2/ranking_utils.hpp @@ -1,6 +1,10 @@ #pragma once +#include "search/v2/geocoder.hpp" +#include "search/v2/search_model.hpp" + #include "std/cstdint.hpp" +#include "std/limits.hpp" #include "std/string.hpp" namespace search diff --git a/search/v2/search_query_v2.cpp b/search/v2/search_query_v2.cpp index 797e57da5a..906d5dc0de 100644 --- a/search/v2/search_query_v2.cpp +++ b/search/v2/search_query_v2.cpp @@ -43,11 +43,11 @@ void SearchQueryV2::Search(Results & res, size_t resCount) params.m_maxNumResults = max(resCount, kPreResultsCount); m_geocoder.SetParams(params); - vector results; + Geocoder::TResultList results; m_geocoder.GoEverywhere(results); - AddPreResults1(results); + AddPreResults1(results, false /* viewportSearch */); - FlushResults(res, false /* allMWMs */, resCount, false /* oldHouseSearch */); + FlushResults(params, res, false /* allMWMs */, resCount, false /* oldHouseSearch */); } void SearchQueryV2::SearchViewportPoints(Results & res) @@ -59,11 +59,11 @@ void SearchQueryV2::SearchViewportPoints(Results & res) params.m_maxNumResults = kPreResultsCount; m_geocoder.SetParams(params); - vector results; + Geocoder::TResultList results; m_geocoder.GoInViewport(results); - AddPreResults1(results); + AddPreResults1(results, true /* viewportSearch */); - FlushViewportResults(res, false /* oldHouseSearch */); + FlushViewportResults(params, res, false /* oldHouseSearch */); } void SearchQueryV2::ClearCaches() @@ -72,32 +72,16 @@ void SearchQueryV2::ClearCaches() m_geocoder.ClearCaches(); } -void SearchQueryV2::AddPreResults1(vector & results) +void SearchQueryV2::AddPreResults1(Geocoder::TResultList & results, bool viewportSearch) { - // Group all features by MwmId and add them as PreResult1. - sort(results.begin(), results.end()); - - auto ib = results.begin(); - while (ib != results.end()) + for (auto const & result : results) { - auto ie = ib; - while (ie != results.end() && ie->m_mwmId == ib->m_mwmId) - ++ie; - - /// @todo Add RankTableCache here? - MwmSet::MwmHandle handle = m_index.GetMwmHandleById(ib->m_mwmId); - if (handle.IsAlive()) - { - auto rankTable = RankTable::Load(handle.GetValue()->m_cont); - if (!rankTable.get()) - rankTable.reset(new DummyRankTable()); - - /// @todo Do final results processing without Query::AddPreResult1. - /// At least, priority and it's queue is an overhead now. - for (auto ii = ib; ii != ie; ++ii) - AddPreResult1(ii->m_mwmId, ii->m_index, rankTable->Get(ii->m_index), 0.0 /* priority */); - } - ib = ie; + auto const & id = result.first; + auto const & info = result.second; + if (viewportSearch) + AddPreResult1(id.m_mwmId, id.m_index, info.m_mwmDistanceToViewport /* priority */, info); + else + AddPreResult1(id.m_mwmId, id.m_index, info.m_mwmDistanceToPosition /* priority */, info); } } } // namespace v2 diff --git a/search/v2/search_query_v2.hpp b/search/v2/search_query_v2.hpp index 83233a44b7..12e18c1387 100644 --- a/search/v2/search_query_v2.hpp +++ b/search/v2/search_query_v2.hpp @@ -24,7 +24,8 @@ public: protected: // Adds a bunch of features as PreResult1. - void AddPreResults1(vector & results); + void AddPreResults1(Geocoder::TResultList & results, + bool viewportSearch); Geocoder m_geocoder; };