diff --git a/map/framework.cpp b/map/framework.cpp index 81a8bc104b..383a7a4363 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -625,19 +625,29 @@ void Framework::StopScale(ScaleEvent const & e) m_renderPolicy->StopScale(pt1, pt2, m_timer.ElapsedSeconds()); } +template +search::Engine * Framework::GetSearchEngine() +{ + // Classical "double check" synchronization pattern. + if (!m_pSearchEngine) + { + threads::MutexGuard lock(m_modelSyn); + if (!m_pSearchEngine) + { + scoped_ptr pReader(GetPlatform().GetReader(SEARCH_CATEGORIES_FILE_NAME)); + m_pSearchEngine.reset( + new search::Engine(&m_model.GetIndex(), new search::CategoriesHolder(*pReader))); + } + } + return m_pSearchEngine.get(); +} + template void Framework::Search(string const & text, SearchCallbackT callback) { - threads::MutexGuard lock(m_modelSyn); - - if (!m_pSearchEngine.get()) - { - scoped_ptr pReader(GetPlatform().GetReader(SEARCH_CATEGORIES_FILE_NAME)); - m_pSearchEngine.reset( - new search::Engine(&m_model.GetIndex(), new search::CategoriesHolder(*pReader))); - } - - m_pSearchEngine->Search(text, m_navigator.Screen().GlobalRect(), callback); + search::Engine * pSearchEngine = GetSearchEngine(); + pSearchEngine->SetViewport(m_navigator.Screen().GlobalRect()); + pSearchEngine->Search(text, callback); } template diff --git a/map/framework.hpp b/map/framework.hpp index 3db098a0c0..2f3f711532 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -189,6 +189,7 @@ public: bool isTiling); void Search(string const & text, SearchCallbackT callback); + search::Engine * GetSearchEngine(); void SetMaxWorldRect(); diff --git a/search/feature_match.hpp b/search/feature_match.hpp index dc0e5ba681..e183a31d7a 100644 --- a/search/feature_match.hpp +++ b/search/feature_match.hpp @@ -7,6 +7,7 @@ #include "../std/queue.hpp" #include "../std/scoped_ptr.hpp" #include "../std/unordered_map.hpp" +#include "../std/unordered_set.hpp" #include "../std/utility.hpp" #include "../std/vector.hpp" @@ -106,14 +107,19 @@ struct OffsetIntersecter { typedef unordered_map MapType; + unordered_set const * m_pOffsetFilter; MapType m_prevMap; MapType m_map; bool m_bFirstStep; - OffsetIntersecter() : m_bFirstStep(true) {} + explicit OffsetIntersecter(unordered_set const * pOffsetFilter) + : m_pOffsetFilter(pOffsetFilter), m_bFirstStep(true) {} void operator() (uint32_t offset, uint8_t rank) { + if (m_pOffsetFilter && !m_pOffsetFilter->count(offset)) + return; + uint16_t prevRankSum = 0; if (!m_bFirstStep) { @@ -141,10 +147,11 @@ template void MatchFeaturesInTrie(strings::UniString const * tokens, size_t tokenCount, strings::UniString const & prefix, TrieIterator const & trieRoot, + unordered_set const * pOffsetsFilter, F & f, size_t resultsNeeded) { - impl::OffsetIntersecter intersecter; + impl::OffsetIntersecter intersecter(pOffsetsFilter); // Match tokens. for (size_t i = 0; i < tokenCount; ++i) diff --git a/search/search_engine.cpp b/search/search_engine.cpp index fe8b5cb374..7de7e88ff1 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -16,19 +16,22 @@ namespace search Engine::Engine(IndexType const * pIndex, CategoriesHolder * pCategories) : m_pIndex(pIndex), m_pCategories(pCategories) { + m_pQuery.reset(new Query(pIndex, pCategories)); } Engine::~Engine() { } -void Engine::Search(string const & queryText, - m2::RectD const & viewport, - function const & f) +void Engine::SetViewport(m2::RectD const & viewport) { - LOG(LDEBUG, (queryText, viewport)); - search::Query query(m_pIndex, m_pCategories.get()); - query.Search(queryText, viewport, f); + m_pQuery->SetViewport(viewport); +} + +void Engine::Search(string const & queryText, function const & f) +{ + LOG(LDEBUG, (queryText)); + m_pQuery->Search(queryText, f); f(Result::GetEndResult()); } diff --git a/search/search_engine.hpp b/search/search_engine.hpp index c76b0b500b..c8de163620 100644 --- a/search/search_engine.hpp +++ b/search/search_engine.hpp @@ -16,6 +16,7 @@ namespace search { class CategoriesHolder; +class Query; class Result; class Engine @@ -27,13 +28,13 @@ public: Engine(IndexType const * pIndex, CategoriesHolder * pCategories); ~Engine(); - void Search(string const & query, - m2::RectD const & rect, - function const & f); + void SetViewport(m2::RectD const & viewport); + void Search(string const & query, function const & f); private: Index const * m_pIndex; scoped_ptr m_pCategories; + scoped_ptr m_pQuery; }; } // namespace search diff --git a/search/search_query.cpp b/search/search_query.cpp index 164d0b16a7..adfdab9f30 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -3,8 +3,10 @@ #include "feature_match.hpp" #include "latlon_match.hpp" #include "result.hpp" +#include "../indexer/feature_covering.hpp" #include "../indexer/features_vector.hpp" #include "../indexer/index.hpp" +#include "../indexer/scales.hpp" #include "../indexer/search_delimiters.hpp" #include "../indexer/search_string_utils.hpp" #include "../base/logging.hpp" @@ -16,23 +18,88 @@ namespace search { Query::Query(Index const * pIndex, search::CategoriesHolder const * pCategories) - : m_pIndex(pIndex), m_pCategories(pCategories) + : m_pIndex(pIndex), m_pCategories(pCategories), m_viewport(m2::RectD::GetEmptyRect()), + m_viewportExtended(m2::RectD::GetEmptyRect()) { } Query::~Query() -{} +{ +} + +void Query::SetViewport(m2::RectD const & viewport) +{ + // TODO: Clear m_viewportExtended when mwm index is added or removed! + + if (m_viewport != viewport) + { + m_viewport = viewport; + m_viewportExtended = m_viewport; + m_viewportExtended.Scale(3); + + UpdateViewportOffsets(); + } +} + +void Query::UpdateViewportOffsets() +{ + vector mwmInfo; + m_pIndex->GetMwmInfo(mwmInfo); + m_offsetsInViewport.resize(mwmInfo.size()); + + int const scale = min(max(scales::GetScaleLevel(m_viewport) + 7, 10), scales::GetUpperScale()); + covering::IntervalsT intervals[2]; + bool intervalCovered[2] = {false, false}; + + for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId) + { + // Search only mwms that intersect with viewport (world always does). + if (m_viewportExtended.IsIntersect(mwmInfo[mwmId].m_limitRect)) + { + Index::MwmLock mwmLock(*m_pIndex, mwmId); + if (MwmValue * pMwmValue = mwmLock.GetValue()) + { + feature::DataHeader const & header = pMwmValue->GetHeader(); + + // TODO: Refactor me! + // prepare needed covering + int const cellDepth = covering::GetCodingDepth(header.GetScaleRange()); + int const ind = (cellDepth == RectId::DEPTH_LEVELS ? 0 : 1); + + if (!intervalCovered[ind]) + { + covering::CoverViewportAndAppendLowerLevels(m_viewport, cellDepth, intervals[ind]); + intervalCovered[ind] = true; + } + + ScaleIndex index(pMwmValue->m_cont.GetReader(INDEX_FILE_TAG), + pMwmValue->m_factory); + + for (size_t i = 0; i < intervals[ind].size(); ++i) + { + index.ForEachInIntervalAndScale(MakeInsertFunctor(m_offsetsInViewport[mwmId]), + intervals[ind][i].first, intervals[ind][i].second, + scale); + } + } + } + } + + size_t offsetsCached = 0; + for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId) + offsetsCached += m_offsetsInViewport[mwmId].size(); + LOG(LINFO, ("For search in viewport cached ", + "mwms:", mwmInfo.size(), + "offsets:", offsetsCached)); +} void Query::Search(string const & query, - m2::RectD const & viewport, function const & f, unsigned int resultsNeeded) { // Initialize. { m_rawQuery = query; - m_viewport = viewport; - m_uniQuery = NormalizeAndSimplifyString(m_rawQuery); search::Delimiters delims; @@ -109,13 +176,10 @@ void Query::SearchFeatures() vector mwmInfo; m_pIndex->GetMwmInfo(mwmInfo); - m2::RectD extendedViewport = m_viewport; - extendedViewport.Scale(3); - for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId) { // Search only mwms that intersect with viewport (world always does). - if (extendedViewport.IsIntersect(mwmInfo[mwmId].m_limitRect)) + if (m_viewportExtended.IsIntersect(mwmInfo[mwmId].m_limitRect)) { Index::MwmLock mwmLock(*m_pIndex, mwmId); if (MwmValue * pMwmValue = mwmLock.GetValue()) @@ -128,8 +192,8 @@ void Query::SearchFeatures() { FeaturesVector featuresVector(pMwmValue->m_cont, pMwmValue->GetHeader()); impl::FeatureLoader f(featuresVector, *this); - MatchFeaturesInTrie(m_tokens.data(), m_tokens.size(), m_prefix, *pTrieRoot, f, - m_results.max_size() * 10); + MatchFeaturesInTrie(m_tokens.data(), m_tokens.size(), m_prefix, *pTrieRoot, + &m_offsetsInViewport[mwmId], f, m_results.max_size() * 10); LOG(LINFO, ("Matched: ", f.m_count)); } } diff --git a/search/search_query.hpp b/search/search_query.hpp index 6db12c5090..2452b3c58e 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -5,6 +5,8 @@ #include "../base/limited_priority_queue.hpp" #include "../std/function.hpp" #include "../std/string.hpp" +#include "../std/unordered_set.hpp" +#include "../std/vector.hpp" class Index; @@ -20,8 +22,8 @@ public: Query(Index const * pIndex, CategoriesHolder const * pCategories); ~Query(); + void SetViewport(m2::RectD const & viewport); void Search(string const & query, - m2::RectD const & viewport, function const & f, unsigned int resultsNeeded = 10); @@ -31,6 +33,7 @@ private: void AddResult(impl::IntermediateResult const & result); void FlushResults(function const & f); + void UpdateViewportOffsets(); void SearchFeatures(); Index const * m_pIndex; @@ -41,6 +44,9 @@ private: buffer_vector m_tokens; strings::UniString m_prefix; m2::RectD m_viewport; + m2::RectD m_viewportExtended; + + vector > m_offsetsInViewport; my::limited_priority_queue m_results; };