From a79ba7c17162957a5de9cfcb991b15794103e6b4 Mon Sep 17 00:00:00 2001 From: vng Date: Thu, 3 Apr 2014 18:46:17 +0300 Subject: [PATCH] [search] - Fixed duplicate checking logic. - Fixed addresses sorting. - Fixed addresses cache clearing. - Limit all results count to 15 (main search) + 30 (additional mom search) --- qt/search_panel.cpp | 29 ++- qt/search_panel.hpp | 1 + search/geometry_utils.cpp | 35 +++ search/geometry_utils.hpp | 14 ++ search/house_detector.cpp | 22 +- search/house_detector.hpp | 41 ++-- search/indexed_value.hpp | 73 +++++++ search/intermediate_result.cpp | 105 ++------- search/intermediate_result.hpp | 1 + search/result.cpp | 18 +- search/search.pro | 3 + search/search_engine.cpp | 38 ++-- search/search_engine.hpp | 2 + search/search_query.cpp | 217 +++++++++++-------- search/search_query.hpp | 28 +-- search/search_tests/house_detector_tests.cpp | 4 +- 16 files changed, 362 insertions(+), 269 deletions(-) create mode 100644 search/geometry_utils.cpp create mode 100644 search/geometry_utils.hpp create mode 100644 search/indexed_value.hpp diff --git a/qt/search_panel.cpp b/qt/search_panel.cpp index 484756b41e..7fa69e4ae4 100644 --- a/qt/search_panel.cpp +++ b/qt/search_panel.cpp @@ -87,6 +87,15 @@ namespace } } +void SearchPanel::ClearResults() +{ + m_pTable->clear(); + m_pTable->setRowCount(0); + m_results.clear(); + + m_pDrawWidget->GetFramework().AdditionalPoiLayerClear(); +} + void SearchPanel::OnSearchResult(ResultsT * res) { scoped_ptr guard(res); @@ -102,13 +111,9 @@ void SearchPanel::OnSearchResult(ResultsT * res) } else { - // clear old results - m_pTable->clear(); - m_pTable->setRowCount(0); - m_results.clear(); + ClearResults(); Framework & frm = m_pDrawWidget->GetFramework(); - frm.AdditionalPoiLayerClear(); for (ResultsT::IterT i = res->Begin(); i != res->End(); ++i) { @@ -140,7 +145,7 @@ void SearchPanel::OnSearchTextChanged(QString const & str) QString const normalized = str.normalized(QString::NormalizationForm_KC); // search even with empty query - //if (!normalized.isEmpty()) + if (!normalized.isEmpty()) { m_params.m_query = normalized.toUtf8().constData(); if (m_pDrawWidget->Search(m_params)) @@ -153,11 +158,13 @@ void SearchPanel::OnSearchTextChanged(QString const & str) m_pClearButton->setVisible(true); } } - //else - //{ - // // hide X button - // m_pClearButton->setVisible(false); - //} + else + { + ClearResults(); + + // hide X button + m_pClearButton->setVisible(false); + } } void SearchPanel::OnSearchPanelItemClicked(int row, int) diff --git a/qt/search_panel.hpp b/qt/search_panel.hpp index 816ad54306..bc8605bbed 100644 --- a/qt/search_panel.hpp +++ b/qt/search_panel.hpp @@ -50,6 +50,7 @@ private: virtual void hideEvent(QHideEvent *); void SearchResultThreadFunc(ResultsT const & result); + void ClearResults(); signals: void SearchResultSignal(ResultsT * result); diff --git a/search/geometry_utils.cpp b/search/geometry_utils.cpp new file mode 100644 index 0000000000..148dd289a9 --- /dev/null +++ b/search/geometry_utils.cpp @@ -0,0 +1,35 @@ +#include "geometry_utils.hpp" + +#include "../indexer/mercator.hpp" + +#include "../geometry/distance_on_sphere.hpp" + + +namespace search +{ + +double PointDistance(m2::PointD const & a, m2::PointD const & b) +{ + return ms::DistanceOnEarth(MercatorBounds::YToLat(a.y), MercatorBounds::XToLon(a.x), + MercatorBounds::YToLat(b.y), MercatorBounds::XToLon(b.x)); +} + +uint8_t ViewportDistance(m2::RectD const & viewport, m2::PointD const & p) +{ + if (viewport.IsPointInside(p)) + return 0; + + m2::RectD r = viewport; + r.Scale(3); + if (r.IsPointInside(p)) + return 1; + + r = viewport; + r.Scale(5); + if (r.IsPointInside(p)) + return 2; + + return 3; +} + +} diff --git a/search/geometry_utils.hpp b/search/geometry_utils.hpp new file mode 100644 index 0000000000..f8531c223d --- /dev/null +++ b/search/geometry_utils.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "../geometry/point2d.hpp" +#include "../geometry/rect2d.hpp" + + +namespace search +{ + +double PointDistance(m2::PointD const & a, m2::PointD const & b); + +uint8_t ViewportDistance(m2::RectD const & viewport, m2::PointD const & p); + +} diff --git a/search/house_detector.cpp b/search/house_detector.cpp index 0f48a3e7e0..cfd6701151 100644 --- a/search/house_detector.cpp +++ b/search/house_detector.cpp @@ -498,13 +498,17 @@ int HouseDetector::LoadStreets(vector const & ids) typedef pair ValueT; function f = bind(&ValueT::first, _1); - CounterIterator it = set_difference(ids.begin(), ids.end(), - make_transform_iterator(m_id2st.begin(), f), - make_transform_iterator(m_id2st.end(), f), - CounterIterator()); - if (it.GetCount() > ids.size() / 2) + // Do clear cache if we have elements that are present in the one set, + // but not in the other one (set's order is irrelevant). + size_t const count = set_intersection(make_transform_iterator(m_id2st.begin(), f), + make_transform_iterator(m_id2st.end(), f), + ids.begin(), ids.end(), + CounterIterator()).GetCount(); + + if (count < min(ids.size(), m_id2st.size())) { - LOG(LDEBUG, ("Clear HouseDetector cache: missed", it.GetCount(), "of", ids.size(), "elements.")); + LOG(LDEBUG, ("Clear HouseDetector cache: " + "Common =", count, "Cache =", m_id2st.size(), "Input =", ids.size())); ClearCaches(); } } @@ -1284,7 +1288,7 @@ struct GreaterSecond }; void ProduceVoting(vector const & acc, - vector & res, + vector & res, MergedStreet const & st) { buffer_vector, 4> voting; @@ -1304,7 +1308,7 @@ void ProduceVoting(vector const & acc, for (size_t i = 0; i < voting.size(); ++i) { if (score == voting[i].second) - res.push_back(AddressSearchResult(voting[i].first, &st)); + res.push_back(HouseResult(voting[i].first, &st)); else break; } @@ -1312,7 +1316,7 @@ void ProduceVoting(vector const & acc, } -void HouseDetector::GetHouseForName(string const & houseNumber, vector & res) +void HouseDetector::GetHouseForName(string const & houseNumber, vector & res) { size_t const count = m_streets.size(); res.reserve(count); diff --git a/search/house_detector.hpp b/search/house_detector.hpp index 75886f14ba..25cbb8975c 100644 --- a/search/house_detector.hpp +++ b/search/house_detector.hpp @@ -1,4 +1,5 @@ #pragma once +#include "indexed_value.hpp" #include "../indexer/feature_decl.hpp" #include "../indexer/index.hpp" @@ -206,7 +207,25 @@ inline void swap(MergedStreet & s1, MergedStreet & s2) s1.Swap(s2); } -struct AddressSearchResult; +struct HouseResult : public IndexedValueBase<2> +{ + House const * m_house; + MergedStreet const * m_street; + + HouseResult(House const * house, MergedStreet const * street) + : m_house(house), m_street(street) + {} + + inline bool operator<(HouseResult const & a) const { return m_house < a.m_house; } + inline bool operator==(HouseResult const & a) const { return m_house == a.m_house; } + + m2::PointD const & GetOrg() const { return m_house->GetPosition(); } +}; + +inline string DebugPrint(HouseResult const & r) +{ + return r.m_house->GetNumber() + ", " + r.m_street->GetName(); +} class HouseDetector { @@ -246,27 +265,9 @@ public: static int const DEFAULT_OFFSET_M = 200; void ReadAllHouses(double offsetMeters = DEFAULT_OFFSET_M); - void GetHouseForName(string const & houseNumber, vector & res); + void GetHouseForName(string const & houseNumber, vector & res); void ClearCaches(); }; -struct AddressSearchResult -{ - House const * m_house; - MergedStreet const * m_street; - - AddressSearchResult(House const * house, MergedStreet const * street) - : m_house(house), m_street(street) - {} - - inline bool operator<(AddressSearchResult const & a) const { return m_house < a.m_house; } - inline bool operator==(AddressSearchResult const & a) const { return m_house == a.m_house; } -}; - -inline string DebugPrint(AddressSearchResult const & r) -{ - return r.m_house->GetNumber() + ", " + r.m_street->GetName(); -} - } diff --git a/search/indexed_value.hpp b/search/indexed_value.hpp new file mode 100644 index 0000000000..87ae4ef9e5 --- /dev/null +++ b/search/indexed_value.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "../std/bind.hpp" +#include "../std/algorithm.hpp" +#include "../std/vector.hpp" +#include "../std/limits.hpp" + + +namespace search +{ + +/// Intrusive class to implement multi-index for some user-defined type. +template class IndexedValueBase +{ +protected: + static size_t const SIZE = N; + size_t m_ind[N]; + +public: + IndexedValueBase() + { + for (size_t i = 0; i < N; ++i) + m_ind[i] = numeric_limits::max(); + } + + void SetIndex(size_t i, size_t v) { m_ind[i] = v; } + + void SortIndex() + { + sort(m_ind, m_ind + N); + } + + static bool Less(IndexedValueBase const & r1, IndexedValueBase const & r2) + { + for (size_t i = 0; i < N; ++i) + { + if (r1.m_ind[i] != r2.m_ind[i]) + return (r1.m_ind[i] < r2.m_ind[i]); + } + + return false; + } +}; + +template +void SortByIndexedValue(vector & vec, CompFactory factory) +{ + for (size_t i = 0; i < CompFactory::SIZE; ++i) + { + typename CompFactory::CompT comp = factory.Get(i); + + // sort by needed criteria + sort(vec.begin(), vec.end(), comp); + + // assign ranks + size_t rank = 0; + for (size_t j = 0; j < vec.size(); ++j) + { + if (j > 0 && comp(vec[j-1], vec[j])) + ++rank; + + vec[j].SetIndex(i, rank); + } + } + + // prepare combined criteria + for_each(vec.begin(), vec.end(), bind(&IndexedValueBase::SortIndex, _1)); + + // sort results according to combined criteria + sort(vec.begin(), vec.end(), &IndexedValueBase::Less); +} + +} diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index 5e289b171d..c63ace3c5e 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -1,16 +1,15 @@ #include "intermediate_result.hpp" #include "ftypes_matcher.hpp" +#include "geometry_utils.hpp" #include "../storage/country_info.hpp" #include "../indexer/classificator.hpp" #include "../indexer/feature.hpp" -#include "../indexer/mercator.hpp" #include "../indexer/scales.hpp" #include "../indexer/categories_holder.hpp" #include "../geometry/angles.hpp" -#include "../geometry/distance_on_sphere.hpp" #include "../base/string_utils.hpp" #include "../base/logging.hpp" @@ -18,40 +17,11 @@ namespace search { -namespace -{ -/// All constants in meters (call ResultDistance for center points). +/// All constants in meters. double const DIST_EQUAL_RESULTS = 100.0; double const DIST_SAME_STREET = 5000.0; -double ResultDistance(m2::PointD const & a, m2::PointD const & b) -{ - return ms::DistanceOnEarth(MercatorBounds::YToLat(a.y), MercatorBounds::XToLon(a.x), - MercatorBounds::YToLat(b.y), MercatorBounds::XToLon(b.x)); -} - -uint8_t ViewportDistance(m2::RectD const & viewport, m2::PointD const & p) -{ - if (viewport.IsPointInside(p)) - return 0; - - m2::RectD r = viewport; - r.Scale(3); - if (r.IsPointInside(p)) - return 1; - - r = viewport; - r.Scale(5); - if (r.IsPointInside(p)) - return 2; - - return 3; -} - -} - - namespace impl { @@ -128,7 +98,7 @@ void PreResult1::CalcParams(m2::RectD const & viewport, m2::PointD const & pos) if (pos.x > -500 && pos.y > -500) { AssertValid(pos); - m_distance = ResultDistance(m_center, pos); + m_distance = PointDistance(m_center, pos); } else { @@ -137,7 +107,7 @@ void PreResult1::CalcParams(m2::RectD const & viewport, m2::PointD const & pos) } m_viewportDistance = ViewportDistance(viewport, m_center); - m_distanceFromViewportCenter = ResultDistance(m_center, viewport.Center()); + m_distanceFromViewportCenter = PointDistance(m_center, viewport.Center()); } bool PreResult1::LessRank(PreResult1 const & r1, PreResult1 const & r2) @@ -172,7 +142,8 @@ PreResult2::PreResult2(FeatureType const & f, PreResult1 const * p, : m_id(f.GetID()), m_types(f), m_str(displayName), - m_resultType(RESULT_FEATURE) + m_resultType(RESULT_FEATURE), + m_geomType(f.GetFeatureType()) { ASSERT(m_id.IsValid(), ()); ASSERT(!m_types.Empty(), ()); @@ -197,7 +168,8 @@ PreResult2::PreResult2(FeatureType const & f, PreResult1 const * p, PreResult2::PreResult2(m2::RectD const & viewport, m2::PointD const & pos, double lat, double lon) : m_str("(" + strings::to_string_dac(lat, 5) + ", " + strings::to_string_dac(lon, 5) + ")"), m_resultType(RESULT_LATLON), - m_rank(255) + m_rank(255), + m_geomType(feature::GEOM_UNDEFINED) { m2::PointD const fCenter(MercatorBounds::LonToX(lon), MercatorBounds::LatToY(lat)); m_region.SetParams(string(), fCenter); @@ -212,7 +184,8 @@ PreResult2::PreResult2(string const & name, int penalty) m_distanceFromViewportCenter(-1000.0), m_resultType(RESULT_CATEGORY), m_rank(255), // best rank - m_viewportDistance(0) // closest to viewport + m_viewportDistance(0), // closest to viewport + m_geomType(feature::GEOM_UNDEFINED) { } @@ -299,50 +272,16 @@ bool PreResult2::StrictEqualF::operator() (PreResult2 const & r) const if (m_r.m_resultType == r.m_resultType && m_r.m_resultType == RESULT_FEATURE) { if (m_r.m_str == r.m_str && m_r.GetBestType() == r.GetBestType()) - return (ResultDistance(m_r.GetCenter(), r.GetCenter()) < DIST_EQUAL_RESULTS); + return (PointDistance(m_r.GetCenter(), r.GetCenter()) < DIST_EQUAL_RESULTS); } return false; } -namespace -{ - /// @todo Using the criteria that may be inappropriate in some cases - /// ("highway" may be point and area objects - "bus_stop"). - class IsLinearChecker - { - uint8_t m_index[2]; - - static uint8_t FirstLevelIndex(uint32_t t) - { - uint8_t v; - VERIFY ( ftype::GetValue(t, 0, v), (t) ); - return v; - } - - public: - IsLinearChecker() - { - char const * arr[] = { "highway", "waterway" }; - STATIC_ASSERT ( ARRAY_SIZE(arr) == ARRAY_SIZE(m_index) ); - - ClassifObject const * c = classif().GetRoot(); - for (size_t i = 0; i < ARRAY_SIZE(m_index); ++i) - m_index[i] = static_cast(c->BinaryFind(arr[i]).GetIndex()); - } - - bool IsMy(uint32_t type) const - { - uint8_t const * e = m_index + ARRAY_SIZE(m_index); - return (find(m_index, e, FirstLevelIndex(type)) != e); - } - }; -} - bool PreResult2::LessLinearTypesF::operator() (PreResult2 const & r1, PreResult2 const & r2) const { - if (r1.m_resultType != r2.m_resultType) - return (r1.m_resultType < r2.m_resultType); + if (r1.m_geomType != r2.m_geomType) + return (r1.m_geomType < r2.m_geomType); if (r1.m_str != r2.m_str) return (r1.m_str < r2.m_str); @@ -362,24 +301,16 @@ bool PreResult2::EqualLinearTypesF::operator() (PreResult2 const & r1, PreResult { // Note! Do compare for distance when filtering linear objects. // Otherwise we will skip the results for different parts of the map. - if (r1.m_resultType == r2.m_resultType && r1.m_str == r2.m_str && - //r1.m_viewportDistance == r2.m_viewportDistance && - ResultDistance(r1.GetCenter(), r2.GetCenter()) < DIST_SAME_STREET) - { - // filter equal linear features - static IsLinearChecker checker; - - uint32_t const t1 = r1.GetBestType(); - return (t1 == r2.GetBestType() && checker.IsMy(t1)); - } - - return false; + return (r1.m_geomType == r2.m_geomType && + r1.m_geomType == feature::GEOM_LINE && + r1.m_str == r2.m_str && + PointDistance(r1.GetCenter(), r2.GetCenter()) < DIST_SAME_STREET); } bool PreResult2::IsStreet() const { static ftypes::IsStreetChecker checker; - return checker(m_types); + return (m_geomType == feature::GEOM_LINE && checker(m_types)); } string PreResult2::DebugPrint() const diff --git a/search/intermediate_result.hpp b/search/intermediate_result.hpp index e1bbe3961c..6586a59ef0 100644 --- a/search/intermediate_result.hpp +++ b/search/intermediate_result.hpp @@ -159,6 +159,7 @@ private: ResultType m_resultType; uint8_t m_rank; uint8_t m_viewportDistance; + feature::EGeomType m_geomType; }; inline string DebugPrint(PreResult2 const & t) diff --git a/search/result.cpp b/search/result.cpp index 949dfedd32..d1b3249185 100644 --- a/search/result.cpp +++ b/search/result.cpp @@ -1,4 +1,5 @@ #include "result.hpp" +#include "geometry_utils.hpp" namespace search @@ -23,7 +24,7 @@ Result::Result(m2::PointD const & fCenter, string const & str, string const & region, string const & flag, double distance) : m_center(fCenter), m_str(str), m_region(region), - m_flag(flag), m_distance(distance) + m_flag(flag), m_featureType(0), m_distance(distance) { } @@ -68,9 +69,18 @@ char const * Result::GetSuggestionString() const bool Result::operator== (Result const & r) const { - return (m_str == r.m_str && m_region == r.m_region && m_featureType == r.m_featureType && - GetResultType() == r.GetResultType() && - my::AlmostEqual(m_distance, r.m_distance)); + ResultType const type = GetResultType(); + if (type == r.GetResultType() && type != RESULT_SUGGESTION) + { + // This function is used to filter duplicate results in cases: + // - emitted Wrold.mwm and Country.mwm + // - after additional search in all mwm + // so it's suitable here to test for 500m + return (m_str == r.m_str && m_region == r.m_region && + m_featureType == r.m_featureType && + PointDistance(m_center, r.m_center) < 500.0); + } + return false; } void Results::AddResultCheckExisting(Result const & r) diff --git a/search/search.pro b/search/search.pro index d3f6d21900..c242b44aad 100644 --- a/search/search.pro +++ b/search/search.pro @@ -23,6 +23,8 @@ HEADERS += \ house_detector.hpp \ ftypes_matcher.hpp \ algos.hpp \ + indexed_value.hpp \ + geometry_utils.hpp \ SOURCES += \ search_engine.cpp \ @@ -36,3 +38,4 @@ SOURCES += \ params.cpp \ house_detector.cpp \ ftypes_matcher.cpp \ + geometry_utils.cpp \ diff --git a/search/search_engine.cpp b/search/search_engine.cpp index 915680ef6f..a52f9344b8 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -84,8 +84,7 @@ Engine::Engine(IndexType const * pIndex, Reader * pCategoriesR, m_pQuery.reset(new Query(pIndex, &m_pData->m_categories, &m_pData->m_stringsToSuggest, - &m_pData->m_infoGetter, - 100)); /// @todo temporary solution for house search, we should increase size, because for one street we can have a lot of features. + &m_pData->m_infoGetter)); m_pQuery->SetPreferredLanguage(lang); } @@ -214,20 +213,20 @@ namespace else return false; } +} - /// @todo Temporary solution to ensure that results are sorted by distance only for AROUND_POSITION mode. - void EmitResults(SearchParams const & params, Results & res) +void Engine::EmitResults(SearchParams const & params, Results & res) +{ + if (params.IsValidPosition() && + params.NeedSearch(SearchParams::AROUND_POSITION) && + !params.NeedSearch(SearchParams::IN_VIEWPORT) && + !params.NeedSearch(SearchParams::SEARCH_WORLD)) { - if (params.IsValidPosition() && - params.NeedSearch(SearchParams::AROUND_POSITION) && - !params.NeedSearch(SearchParams::IN_VIEWPORT) && - !params.NeedSearch(SearchParams::SEARCH_WORLD)) - { - res.Sort(&LessByDistance); - } - - params.m_callback(res); + res.Sort(&LessByDistance); } + + m_searchResults = res; + params.m_callback(res); } void Engine::SearchAsync() @@ -301,6 +300,7 @@ void Engine::SearchAsync() try { + /* if (emptyQuery) { // Search for empty query only around viewport. @@ -319,10 +319,11 @@ void Engine::SearchAsync() } } else + */ { // Do search for address in all modes. // params.NeedSearch(SearchParams::SEARCH_ADDRESS) - m_pQuery->Search(res, true); + m_pQuery->Search(res, RESULTS_COUNT); } } catch (Query::CancelException const &) @@ -332,10 +333,7 @@ void Engine::SearchAsync() // Emit results even if search was canceled and we have something. size_t const count = res.GetCount(); if (!m_pQuery->IsCanceled() || count > 0) - { - m_searchResults = res; EmitResults(params, res); - } // Make additional search in whole mwm when not enough results (only for non-empty query). if (!emptyQuery && !m_pQuery->IsCanceled() && count < RESULTS_COUNT) @@ -344,7 +342,8 @@ void Engine::SearchAsync() { m_pQuery->SearchAdditional(res, params.NeedSearch(SearchParams::AROUND_POSITION), - params.NeedSearch(SearchParams::IN_VIEWPORT)); + params.NeedSearch(SearchParams::IN_VIEWPORT), + 2 * RESULTS_COUNT); } catch (Query::CancelException const &) { @@ -352,10 +351,7 @@ void Engine::SearchAsync() // Emit if we have more results. if (res.GetCount() > count) - { - m_searchResults = res; EmitResults(params, res); - } } // Emit finish marker to client. diff --git a/search/search_engine.hpp b/search/search_engine.hpp index 50195b1842..7855007402 100644 --- a/search/search_engine.hpp +++ b/search/search_engine.hpp @@ -69,6 +69,8 @@ private: void SetViewportAsync(m2::RectD const & viewport, m2::RectD const & nearby); void SearchAsync(); + void EmitResults(SearchParams const & params, Results & res); + threads::Mutex m_searchMutex, m_updateMutex, m_readyMutex; volatile bool m_readyThread; diff --git a/search/search_query.cpp b/search/search_query.cpp index 966e84cf45..e65b06e3de 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -2,6 +2,8 @@ #include "feature_offset_match.hpp" #include "latlon_match.hpp" #include "search_common.hpp" +#include "indexed_value.hpp" +#include "geometry_utils.hpp" #include "../storage/country_info.hpp" @@ -23,8 +25,6 @@ #include "../base/stl_add.hpp" #include "../std/algorithm.hpp" -#include "../std/array.hpp" -#include "../std/bind.hpp" namespace search @@ -70,8 +70,7 @@ namespace Query::Query(Index const * pIndex, CategoriesHolder const * pCategories, StringsToSuggestVectorT const * pStringsToSuggest, - storage::CountryInfoGetter const * pInfoGetter, - size_t resultsNeeded /*= 10*/) + storage::CountryInfoGetter const * pInfoGetter) : m_pIndex(pIndex), m_pCategories(pCategories), m_pStringsToSuggest(pStringsToSuggest), @@ -88,10 +87,13 @@ Query::Query(Index const * pIndex, STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare1) ); STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare2) ); + // Maximum result candidates count for each viewport/criteria. + size_t const PRE_RESULTS_COUNT = 200; + for (size_t i = 0; i < m_qCount; ++i) { - m_results[i] = QueueT(2 * resultsNeeded, QueueCompareT(g_arrCompare1[i])); - m_results[i].reserve(2 * resultsNeeded); + m_results[i] = QueueT(PRE_RESULTS_COUNT, QueueCompareT(g_arrCompare1[i])); + m_results[i].reserve(PRE_RESULTS_COUNT); } // Initialize keywords scorer. @@ -328,7 +330,7 @@ void Query::SearchCoordinates(string const & query, Results & res) const } } -void Query::Search(Results & res, bool searchAddress) +void Query::Search(Results & res, size_t resCount) { ClearQueues(); @@ -336,14 +338,13 @@ void Query::Search(Results & res, bool searchAddress) SuggestStrings(res); if (m_cancel) return; - if (searchAddress) - SearchAddress(); + SearchAddress(); if (m_cancel) return; SearchFeatures(); if (m_cancel) return; - FlushResults(res, &Results::AddResult); + FlushResults(res, false, resCount); } namespace @@ -370,53 +371,27 @@ namespace }; //@} - class IndexedValue + class IndexedValue : public search::IndexedValueBase { - public: - typedef impl::PreResult2 element_type; - - private: - array m_ind; + typedef impl::PreResult2 ValueT; /// @todo Do not use shared_ptr for optimization issues. /// Need to rewrite std::unique algorithm. - shared_ptr m_val; + shared_ptr m_val; public: - explicit IndexedValue(element_type * v) : m_val(v) - { - for (size_t i = 0; i < m_ind.size(); ++i) - m_ind[i] = numeric_limits::max(); - } + explicit IndexedValue(ValueT * v) : m_val(v) {} - element_type const & operator*() const { return *m_val; } - - void SetIndex(size_t i, size_t v) { m_ind[i] = v; } - - void SortIndex() - { - sort(m_ind.begin(), m_ind.end()); - } + ValueT const & operator*() const { return *m_val; } string DebugPrint() const { string index; - for (size_t i = 0; i < m_ind.size(); ++i) + for (size_t i = 0; i < SIZE; ++i) index = index + " " + strings::to_string(m_ind[i]); return impl::DebugPrint(*m_val) + "; Index:" + index; } - - bool operator < (IndexedValue const & r) const - { - for (size_t i = 0; i < m_ind.size(); ++i) - { - if (m_ind[i] != r.m_ind[i]) - return (m_ind[i] < r.m_ind[i]); - } - - return false; - } }; inline string DebugPrint(IndexedValue const & t) @@ -424,7 +399,24 @@ namespace return t.DebugPrint(); } - struct LessByFeatureID + struct CompFactory2 + { + struct CompT + { + CompareFunctionT2 m_fn; + explicit CompT(CompareFunctionT2 fn) : m_fn(fn) {} + template bool operator() (T const & r1, T const & r2) const + { + return m_fn(*r1, *r2); + } + }; + + static size_t const SIZE = 3; + + CompT Get(size_t i) { return CompT(g_arrCompare2[i]); } + }; + + struct LessFeatureID { typedef impl::PreResult1 ValueT; bool operator() (ValueT const & r1, ValueT const & r2) const @@ -433,6 +425,18 @@ namespace } }; + class EqualFeatureID + { + typedef impl::PreResult1 ValueT; + ValueT const & m_val; + public: + EqualFeatureID(ValueT const & v) : m_val(v) {} + bool operator() (ValueT const & r) const + { + return (m_val.GetID() == r.GetID()); + } + }; + template void RemoveDuplicatingLinear(vector & indV) { @@ -510,16 +514,64 @@ namespace impl return 0; } }; + + class HouseCompFactory + { + Query & m_query; + + bool LessViewport(HouseResult const & r1, HouseResult const & r2) const + { + m2::RectD const & v = m_query.GetViewport(); + + uint8_t const d1 = ViewportDistance(v, r1.GetOrg()); + uint8_t const d2 = ViewportDistance(v, r2.GetOrg()); + + return (d1 != d2 ? d1 < d2 : LessDistance(r1, r2)); + } + + bool LessDistance(HouseResult const & r1, HouseResult const & r2) const + { + if (m_query.IsValidPosition()) + { + return (PointDistance(m_query.m_position, r1.GetOrg()) < + PointDistance(m_query.m_position, r2.GetOrg())); + } + else + return false; + } + + public: + HouseCompFactory(Query & q) : m_query(q) {} + + struct CompT + { + HouseCompFactory * m_parent; + size_t m_alg; + + CompT(HouseCompFactory * parent, size_t alg) : m_parent(parent), m_alg(alg) {} + bool operator() (HouseResult const & r1, HouseResult const & r2) const + { + if (m_alg == 0) + return m_parent->LessViewport(r1, r2); + else + return m_parent->LessDistance(r1, r2); + } + }; + + static size_t const SIZE = 2; + + CompT Get(size_t i) { return CompT(this, i); } + }; } -void Query::FlushResults(Results & res, void (Results::*pAddFn)(Result const &)) +void Query::FlushResults(Results & res, bool allMWMs, size_t resCount) { vector indV; vector streets; { // make unique set of PreResult1 - typedef set PreResultSetT; + typedef set PreResultSetT; PreResultSetT theSet; for (size_t i = 0; i < m_qCount; ++i) @@ -549,6 +601,10 @@ void Query::FlushResults(Results & res, void (Results::*pAddFn)(Result const &)) if (indV.empty()) return; + void (Results::*addFn)(Result const &) = allMWMs ? + &Results::AddResultCheckExisting : + &Results::AddResult; + #ifdef HOUSE_SEARCH_TEST if (!m_house.empty() && !streets.empty()) { @@ -557,44 +613,29 @@ void Query::FlushResults(Results & res, void (Results::*pAddFn)(Result const &)) m_houseDetector.ReadAllHouses(); - vector houses; + vector houses; m_houseDetector.GetHouseForName(strings::ToUtf8(m_house), houses); - for (size_t i = 0; i < houses.size(); ++i) + SortByIndexedValue(houses, impl::HouseCompFactory(*this)); + + // Limit address results when searching in first pass (position, viewport, locality). + size_t count = houses.size(); + if (!allMWMs) + count = min(count, size_t(3)); + + for (size_t i = 0; i < count; ++i) { House const * h = houses[i].m_house; - (res.*pAddFn)(Result(h->GetPosition(), h->GetNumber() + ", " + houses[i].m_street->GetName(), - string(), string(), - IsValidPosition() ? h->GetPosition().Length(m_position) : -1.0)); + (res.*addFn)(Result(h->GetPosition(), h->GetNumber() + ", " + houses[i].m_street->GetName(), + string(), string(), + IsValidPosition() ? h->GetPosition().Length(m_position) : -1.0)); } } #endif RemoveDuplicatingLinear(indV); - for (size_t i = 0; i < m_qCount; ++i) - { - CompareT comp(g_arrCompare2[i]); - - // sort by needed criteria - sort(indV.begin(), indV.end(), comp); - - // assign ranks - size_t rank = 0; - for (size_t j = 0; j < indV.size(); ++j) - { - if (j > 0 && comp(indV[j-1], indV[j])) - ++rank; - - indV[j].SetIndex(i, rank); - } - } - - // prepare combined criteria - for_each(indV.begin(), indV.end(), bind(&IndexedValue::SortIndex, _1)); - - // sort results according to combined criteria - sort(indV.begin(), indV.end()); + SortByIndexedValue(indV, CompFactory2()); // get preffered types to show in results set prefferedTypes; @@ -608,32 +649,17 @@ void Query::FlushResults(Results & res, void (Results::*pAddFn)(Result const &)) } // emit feature results - for (size_t i = 0; i < indV.size(); ++i) + size_t count = res.GetCount(); + for (size_t i = 0; i < indV.size() && count < resCount; ++i, ++count) { if (m_cancel) break; LOG(LDEBUG, (indV[i])); - (res.*pAddFn)(MakeResult(*(indV[i]), &prefferedTypes)); + (res.*addFn)(MakeResult(*(indV[i]), &prefferedTypes)); } } -namespace -{ - class EqualFeature - { - typedef impl::PreResult1 ValueT; - ValueT const & m_val; - - public: - EqualFeature(ValueT const & v) : m_val(v) {} - bool operator() (ValueT const & r) const - { - return (m_val.GetID() == r.GetID()); - } - }; -} - void Query::AddResultFromTrie(TrieValueT const & val, size_t mwmID, int8_t viewportID) { impl::PreResult1 res(FeatureID(mwmID, val.m_featureId), val.m_rank, val.m_pt, @@ -642,7 +668,7 @@ void Query::AddResultFromTrie(TrieValueT const & val, size_t mwmID, int8_t viewp for (size_t i = 0; i < m_qCount; ++i) { // here can be the duplicates because of different language match (for suggest token) - if (m_results[i].end() == find_if(m_results[i].begin(), m_results[i].end(), EqualFeature(res))) + if (m_results[i].end() == find_if(m_results[i].begin(), m_results[i].end(), EqualFeatureID(res))) m_results[i].push(res); } } @@ -1899,8 +1925,7 @@ void Query::SearchAllInViewport(m2::RectD const & viewport, Results & res, unsig RemoveDuplicatingLinear(indV); // sort by distance from m_position - sort(indV.begin(), indV.end(), - CompareT(&impl::PreResult2::LessDistance)); + sort(indV.begin(), indV.end(), CompFactory2::CompT(&impl::PreResult2::LessDistance)); // emit results size_t const count = min(indV.size(), static_cast(resultsNeeded)); @@ -1918,7 +1943,7 @@ bool Query::IsValidPosition() const return (m_position.x > empty_pos_value && m_position.y > empty_pos_value); } -void Query::SearchAdditional(Results & res, bool nearMe, bool inViewport) +void Query::SearchAdditional(Results & res, bool nearMe, bool inViewport, size_t resCount) { ClearQueues(); @@ -1950,7 +1975,7 @@ void Query::SearchAdditional(Results & res, bool nearMe, bool inViewport) SearchInMWM(mwmLock, params); } - FlushResults(res, &Results::AddResultCheckExisting); + FlushResults(res, true, resCount); } } diff --git a/search/search_query.hpp b/search/search_query.hpp index 922bad1e47..203afeffec 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -41,6 +41,7 @@ namespace impl struct Locality; struct Region; class DoFindLocality; + class HouseCompFactory; } class Query @@ -67,8 +68,7 @@ public: Query(Index const * pIndex, CategoriesHolder const * pCategories, StringsToSuggestVectorT const * pStringsToSuggest, - storage::CountryInfoGetter const * pInfoGetter, - size_t resultsNeeded = 10); + storage::CountryInfoGetter const * pInfoGetter); ~Query(); inline void SupportOldFormat(bool b) { m_supportOldFormat = b; } @@ -93,9 +93,9 @@ public: /// @name Different search functions. //@{ void SearchCoordinates(string const & query, Results & res) const; - void Search(Results & res, bool searchAddress); + void Search(Results & res, size_t resCount); void SearchAllInViewport(m2::RectD const & viewport, Results & res, unsigned int resultsNeeded = 30); - void SearchAdditional(Results & res, bool nearMe, bool inViewport); + void SearchAdditional(Results & res, bool nearMe, bool inViewport, size_t resCount); //@} void ClearCaches(); @@ -143,6 +143,7 @@ private: friend class impl::BestNameFinder; friend class impl::PreResult2Maker; friend class impl::DoFindLocality; + friend class impl::HouseCompFactory; void ClearQueues(); @@ -158,7 +159,7 @@ private: /// @param[in] viewportID @see m_viewport void AddResultFromTrie(TrieValueT const & val, size_t mwmID, int8_t viewportID = -1); - void FlushResults(Results & res, void (Results::*pAddFn)(Result const &)); + void FlushResults(Results & res, bool allMWMs, size_t resCount); void SearchAddress(); @@ -231,7 +232,7 @@ private: OffsetsVectorT m_offsetsInViewport[RECTSCOUNT]; bool m_supportOldFormat; - template class CompareT + template class CompareT { typedef bool (*FunctionT) (ParamT const &, ParamT const &); FunctionT m_fn; @@ -242,22 +243,11 @@ private: template bool operator() (T const & v1, T const & v2) const { - RefT getR; - return m_fn(getR(v1), getR(v2)); + return m_fn(v1, v2); } }; - struct NothingRef - { - template T const & operator() (T const & t) const { return t; } - }; - struct RefPointer - { - template typename T::element_type const & operator() (T const & t) const { return *t; } - template T const & operator() (T const * t) const { return *t; } - }; - - typedef CompareT QueueCompareT; + typedef CompareT QueueCompareT; typedef my::limited_priority_queue QueueT; public: diff --git a/search/search_tests/house_detector_tests.cpp b/search/search_tests/house_detector_tests.cpp index ec6c4423bf..d663c6fdde 100644 --- a/search/search_tests/house_detector_tests.cpp +++ b/search/search_tests/house_detector_tests.cpp @@ -259,7 +259,7 @@ m2::PointD FindHouse(Index & index, vector const & streets, houser.ReadAllHouses(offset); - vector houses; + vector houses; houser.GetHouseForName(houseName, houses); TEST_EQUAL(houses.size(), 1, (houses)); @@ -442,7 +442,7 @@ UNIT_TEST(HS_MWMSearch) detector.MergeStreets(); detector.ReadAllHouses(); - vector houses; + vector houses; detector.GetHouseForName(a.m_house, houses); if (houses.empty()) {