From 73b11cd8d57388d167c6143301e8183e68b103de Mon Sep 17 00:00:00 2001 From: vng Date: Thu, 16 Aug 2012 18:08:24 +0300 Subject: [PATCH] [search] Implement additional search around locality (like address). --- indexer/feature.cpp | 12 +- indexer/feature.hpp | 8 + search/feature_offset_match.hpp | 70 +++-- search/search_engine.cpp | 2 +- search/search_query.cpp | 455 +++++++++++++++++++++++++++----- search/search_query.hpp | 55 +++- 6 files changed, 506 insertions(+), 96 deletions(-) diff --git a/indexer/feature.cpp b/indexer/feature.cpp index d2c387704e..87b3e6bf8e 100644 --- a/indexer/feature.cpp +++ b/indexer/feature.cpp @@ -290,14 +290,16 @@ void FeatureType::GetPreferredDrawableNames(string & defaultName, string & intNa } } -uint32_t FeatureType::GetPopulation() const +uint8_t FeatureType::GetRank() const { ParseCommon(); + return m_Params.rank; +} - if (m_Params.rank == 0) - return 1; - - return static_cast(pow(1.1, m_Params.rank)); +uint32_t FeatureType::GetPopulation() const +{ + uint8_t const r = GetRank(); + return (r == 0 ? 1 : static_cast(pow(1.1, r))); } double FeatureType::GetPopulationDrawRank() const diff --git a/indexer/feature.hpp b/indexer/feature.hpp index 13fbd1df8f..c4d2e10b78 100644 --- a/indexer/feature.hpp +++ b/indexer/feature.hpp @@ -60,6 +60,8 @@ public: return (Header() & feature::HEADER_HAS_NAME) != 0; } + // Array with 64 strings ??? Use ForEachName instead! + /* class GetNamesFn { public: @@ -75,6 +77,7 @@ public: return true; } }; + */ template inline bool ForEachNameRef(T & functor) const @@ -222,8 +225,13 @@ public: void GetPrefferedNames(string & defaultName, string & intName) const; /// Additional - take into account house number for defaultName void GetPreferredDrawableNames(string & defaultName, string & intName) const; + inline bool GetName(int8_t lang, string & name) const + { + return m_Params.name.GetString(lang, name); + } //@} + uint8_t GetRank() const; uint32_t GetPopulation() const; double GetPopulationDrawRank() const; diff --git a/search/feature_offset_match.hpp b/search/feature_offset_match.hpp index a0a183c631..091a3e4956 100644 --- a/search/feature_offset_match.hpp +++ b/search/feature_offset_match.hpp @@ -236,31 +236,66 @@ struct TrieRootPrefix } }; -template -void MatchFeaturesInTrie(vector > const & tokens, - vector const & prefixTokens, - TrieRootPrefix const & trieRoot, - TrieRootPrefix const & catRoot, - FilterT const & filter, - ToDo & toDo) +/// Return features set for each token. +template +void GetFeaturesInTrie(vector > const & tokens, + vector const & prefixTokens, + TrieRootPrefix const & trieRoot, + HolderT & holder) { - //LOG(LDEBUG, ("Tokens: ", tokens)); - //LOG(LDEBUG, ("Prefix: ", prefixTokens)); - - impl::OffsetIntersecter intersecter(filter); - // Match tokens. - for (size_t i = 0; i < tokens.size(); ++i) + size_t const count = tokens.size(); + holder.Resize(count + 1); + + for (size_t i = 0; i < count; ++i) { + holder.StartNew(i); + for (size_t j = 0; j < tokens[i].size(); ++j) { ASSERT ( !tokens[i][j].empty(), () ); + impl::FullMatchInTrie(trieRoot.m_root, trieRoot.m_prefix, trieRoot.m_prefixSize, + tokens[i][j], holder); + } + } + + // Match prefix. + holder.StartNew(count); + for (size_t i = 0; i < prefixTokens.size(); ++i) + { + ASSERT ( !prefixTokens[i].empty(), () ); + + impl::FullMatchInTrie(trieRoot.m_root, trieRoot.m_prefix, trieRoot.m_prefixSize, + prefixTokens[i], holder); + } +} + +/// Do set intersection of features for each token. +template +void MatchFeaturesInTrie(vector > const & tokens, + vector const & prefixTokens, + TrieRootPrefix const & trieRoot, + FilterT const & filter, + HolderT const & addHolder, + ToDo & toDo) +{ + impl::OffsetIntersecter intersecter(filter); + + // Match tokens. + size_t const count = tokens.size(); + for (size_t i = 0; i < count; ++i) + { + for (size_t j = 0; j < tokens[i].size(); ++j) + { + ASSERT ( !tokens[i][j].empty(), () ); + + // match in trie impl::FullMatchInTrie(trieRoot.m_root, trieRoot.m_prefix, trieRoot.m_prefixSize, tokens[i][j], intersecter); - impl::FullMatchInTrie(catRoot.m_root, catRoot.m_prefix, catRoot.m_prefixSize, - tokens[i][j], intersecter); + // get additional features for token + addHolder.GetValues(i, intersecter); } intersecter.NextStep(); @@ -272,11 +307,12 @@ void MatchFeaturesInTrie(vector > const & tokens, { ASSERT ( !prefixTokens[i].empty(), () ); + // match in trie impl::PrefixMatchInTrie(trieRoot.m_root, trieRoot.m_prefix, trieRoot.m_prefixSize, prefixTokens[i], intersecter); - impl::FullMatchInTrie(catRoot.m_root, catRoot.m_prefix, catRoot.m_prefixSize, - prefixTokens[i], intersecter); + // get additional features for token + addHolder.GetValues(count, intersecter); } if (prefixCount > 0) diff --git a/search/search_engine.cpp b/search/search_engine.cpp index 1a96aceb0c..737f5ee98f 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -123,7 +123,7 @@ namespace else { if (arrRects[VIEWPORT_RECT].IsRectInside(arrRects[NEARME_RECT]) && - (scales::GetScaleLevel(arrRects[VIEWPORT_RECT]) + Query::m_scaleDepthSearch >= scales::GetUpperScale())) + (scales::GetScaleLevel(arrRects[VIEWPORT_RECT]) + Query::SCALE_SEARCH_DEPTH >= scales::GetUpperScale())) { arrRects[NEARME_RECT].MakeEmpty(); } diff --git a/search/search_query.cpp b/search/search_query.cpp index d90a495f69..a0ebdc405b 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -13,6 +13,7 @@ #include "../indexer/search_delimiters.hpp" #include "../indexer/search_string_utils.hpp" #include "../indexer/categories_holder.hpp" +#include "../indexer/classificator.hpp" #include "../coding/multilang_utf8_string.hpp" #include "../coding/reader_wrapper.hpp" @@ -102,19 +103,24 @@ void Query::SetViewport(m2::RectD viewport[], size_t count) m_pIndex->GetMwmInfo(mwmInfo); for (size_t i = 0; i < count; ++i) + SetViewportByIndex(mwmInfo, viewport[i], i); +} + +void Query::SetViewportByIndex(MWMVectorT const & mwmInfo, m2::RectD const & viewport, size_t idx) +{ + ASSERT_LESS_OR_EQUAL(idx, static_cast(RECTSCOUNT), ()); + + if (viewport.IsValid()) { - if (viewport[i].IsValid()) + // Check if viewports are equal (10 meters). + if (!m_viewport[idx].IsValid() || !IsEqualMercator(m_viewport[idx], viewport, 10.0)) { - // Check if viewports are equal (10 meters). - if (!m_viewport[i].IsValid() || !IsEqualMercator(m_viewport[i], viewport[i], 10.0)) - { - m_viewport[i] = viewport[i]; - UpdateViewportOffsets(mwmInfo, viewport[i], m_offsetsInViewport[i]); - } + m_viewport[idx] = viewport; + UpdateViewportOffsets(mwmInfo, viewport, m_offsetsInViewport[idx]); } - else - ClearCache(i); } + else + ClearCache(idx); } void Query::SetPreferredLanguage(string const & lang) @@ -159,7 +165,7 @@ void Query::UpdateViewportOffsets(MWMVectorT const & mwmInfo, m2::RectD const & if (header.GetType() == feature::DataHeader::country) { pair const scaleR = header.GetScaleRange(); - int const scale = min(max(viewScale + m_scaleDepthSearch, scaleR.first), scaleR.second); + int const scale = min(max(viewScale + SCALE_SEARCH_DEPTH, scaleR.first), scaleR.second); covering::IntervalsT const & interval = cov.Get(header.GetLastScale()); @@ -222,8 +228,7 @@ void Query::Search(string const & query, Results & res) InitSearch(query); search::Delimiters delims; - SplitUniString(NormalizeAndSimplifyString(query), - MakeBackInsertFunctor(m_tokens), delims); + SplitUniString(NormalizeAndSimplifyString(query), MakeBackInsertFunctor(m_tokens), delims); if (!m_tokens.empty() && !delims(strings::LastUniChar(query))) { @@ -250,6 +255,9 @@ void Query::Search(string const & query, Results & res) if (m_cancel) return; SuggestStrings(res); + if (m_cancel) return; + SearchAddress(); + if (m_cancel) return; SearchFeatures(); @@ -533,9 +541,10 @@ namespace }; } -void Query::AddResultFromTrie(TrieValueT const & val, size_t mwmID) +void Query::AddResultFromTrie(TrieValueT const & val, size_t mwmID, int viewportID) { - impl::PreResult1 res(val.m_featureId, val.m_rank, val.m_pt, mwmID, m_position, GetViewport()); + impl::PreResult1 res(val.m_featureId, val.m_rank, val.m_pt, mwmID, + GetPosition(viewportID), GetViewport(viewportID)); for (size_t i = 0; i < m_qCount; ++i) { @@ -552,9 +561,9 @@ class BestNameFinder { uint32_t & m_penalty; string & m_name; - LangKeywordsScorer & m_keywordsScorer; + LangKeywordsScorer const & m_keywordsScorer; public: - BestNameFinder(uint32_t & penalty, string & name, LangKeywordsScorer & keywordsScorer) + BestNameFinder(uint32_t & penalty, string & name, LangKeywordsScorer const & keywordsScorer) : m_penalty(penalty), m_name(name), m_keywordsScorer(keywordsScorer) { m_penalty = uint32_t(-1); @@ -574,10 +583,11 @@ public: } // namespace search::impl -void Query::GetBestMatchName(FeatureType const & f, uint32_t & penalty, string & name) +void Query::GetBestMatchName(FeatureType const & f, uint32_t & penalty, string & name) const { impl::BestNameFinder bestNameFinder(penalty, name, *m_pKeywordsScorer); (void)f.ForEachNameRef(bestNameFinder); + /* if (!f.ForEachNameRef(bestNameFinder)) { @@ -594,19 +604,19 @@ namespace impl class FeatureLoader { Query & m_query; - size_t m_mwmID; - size_t m_count; + size_t m_mwmID, m_count; + int m_viewportID; public: - FeatureLoader(Query & query, size_t mwmID) - : m_query(query), m_mwmID(mwmID), m_count(0) + FeatureLoader(Query & query, size_t mwmID, int viewportID) + : m_query(query), m_mwmID(mwmID), m_count(0), m_viewportID(viewportID) { } void operator() (Query::TrieValueT const & value) { ++m_count; - m_query.AddResultFromTrie(value, m_mwmID); + m_query.AddResultFromTrie(value, m_mwmID, m_viewportID); } size_t GetCount() const { return m_count; } @@ -634,7 +644,7 @@ public: } // namespace search::impl -Query::Params::Params(Query & q) +Query::Params::Params(Query const & q, bool isLocalities/* = false*/) { if (!q.m_prefix.empty()) m_prefixTokens.push_back(q.m_prefix); @@ -646,8 +656,8 @@ Query::Params::Params(Query & q) for (size_t i = 0; i < tokensCount; ++i) m_tokens[i].push_back(q.m_tokens[i]); - // Add names of categories. - if (q.m_pCategories) + // Add names of categories (and synonims). + if (q.m_pCategories && !isLocalities) { for (size_t i = 0; i < tokensCount; ++i) q.m_pCategories->ForEachTypeByName(q.m_tokens[i], DoInsertTypeNames(m_tokens[i])); @@ -656,6 +666,44 @@ Query::Params::Params(Query & q) q.m_pCategories->ForEachTypeByName(q.m_prefix, DoInsertTypeNames(m_prefixTokens)); } + FillLanguages(q); +} + +void Query::Params::EraseTokens(vector const & eraseInds) +{ + // fill temporary vector + vector newTokens; + + size_t skipI = 0; + size_t const count = m_tokens.size(); + size_t const eraseCount = eraseInds.size(); + for (size_t i = 0; i < count; ++i) + { + if (skipI < eraseCount && eraseInds[skipI] == i) + { + ++skipI; + } + else + { + newTokens.push_back(TokensVectorT()); + newTokens.back().swap(m_tokens[i]); + } + } + + // assign to m_tokens + newTokens.swap(m_tokens); + + if (skipI < eraseCount) + { + // it means that we need to skip prefix tokens + ASSERT_EQUAL ( skipI+1, eraseCount, (eraseInds) ); + ASSERT_EQUAL ( eraseInds[skipI], count, (eraseInds) ); + m_prefixTokens.clear(); + } +} + +void Query::Params::FillLanguages(Query const & q) +{ m_langs.insert(q.m_currentLang); m_langs.insert(q.m_inputLang); m_langs.insert(StringUtf8Multilang::GetLangIndex("int_name")); @@ -663,13 +711,255 @@ Query::Params::Params(Query & q) m_langs.insert(StringUtf8Multilang::GetLangIndex("default")); } +namespace impl +{ + struct Locality + { + string m_name, m_enName; ///< native name and english name of locality + Query::TrieValueT m_value; + uint32_t m_type; ///< feature type + vector m_matchedTokens; ///< indexes of matched tokens for locality + + Locality() : m_type(0) {} + Locality(Query::TrieValueT const & val, uint32_t type) : m_value(val), m_type(type) {} + + bool operator<(Locality const & rhs) const + { + if (m_matchedTokens.size() != rhs.m_matchedTokens.size()) + return (m_matchedTokens.size() < rhs.m_matchedTokens.size()); + + return (m_value.m_rank < rhs.m_value.m_rank); + + /// @todo Add sorting by type: 'city' is better than 'town' etc ... + } + + private: + class DoCount + { + size_t & m_count; + public: + DoCount(size_t & count) : m_count(count) { m_count = 0; } + template void operator() (T const &) { ++m_count; } + }; + + public: + bool IsFullNameMatched() const + { + size_t count; + SplitUniString(NormalizeAndSimplifyString(m_name), DoCount(count), search::Delimiters()); + return (count <= m_matchedTokens.size()); + } + }; +} + +void Query::SearchAddress() +{ + // Find World.mwm and do special search there. + MWMVectorT mwmInfo; + m_pIndex->GetMwmInfo(mwmInfo); + + for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId) + { + Index::MwmLock mwmLock(*m_pIndex, mwmId); + MwmValue * pMwm = mwmLock.GetValue(); + if (pMwm && + pMwm->m_cont.IsReaderExist(SEARCH_INDEX_FILE_TAG) && + pMwm->GetHeader().GetType() == feature::DataHeader::world) + { + impl::Locality loc; + if (SearchLocality(pMwm, loc)) + { + LOG(LDEBUG, ("Locality = ", loc.m_name)); + + Params params(*this); + params.EraseTokens(loc.m_matchedTokens); + if (!params.IsEmpty()) + { + SetViewportByIndex(mwmInfo, scales::GetRectForLevel(ADDRESS_SCALE, loc.m_value.m_pt, 1.0), ADDRESS_RECT_ID); + + /// @todo Hack - do not search for address in World.mwm; Do it better in future. + bool const b = m_worldSearch; + m_worldSearch = false; + SearchFeatures(params, mwmInfo, ADDRESS_RECT_ID); + m_worldSearch = b; + } + } + return; + } + } +} + +namespace impl +{ + class DoFindLocality + { + class EqualID + { + uint32_t m_id; + public: + EqualID(uint32_t id) : m_id(id) {} + bool operator() (Locality const & l) const { return (l.m_value.m_featureId == m_id); } + }; + + Query const & m_query; + + vector m_localities; + + FeaturesVector m_vector; + size_t m_index; ///< index of processing token + + volatile bool & m_isCancelled; + + class TypeChecker + { + vector m_vec; + + public: + TypeChecker() + { + char const * arr[][2] = { + { "place", "country" }, + { "place", "city" }, + { "place", "town" } + }; + + Classificator const & c = classif(); + for (size_t i = 0; i < ARRAY_SIZE(arr); ++i) + m_vec.push_back(c.GetTypeByPath(vector(arr[i], arr[i] + 2))); + } + + uint32_t GetType(FeatureType const & f) + { + feature::TypesHolder types(f); + for (size_t i = 0; i < types.Size(); ++i) + { + uint32_t t = types[i]; + ftype::TruncValue(t, 2); + if (find(m_vec.begin(), m_vec.end(), t) != m_vec.end()) + return t; + } + + return 0; + } + + bool IsCountry(uint32_t t) const + { + return (t == m_vec[0]); + } + }; + + int8_t m_en, m_int; + void AssignEnglishName(FeatureType const & f, Locality & l) + { + if (!f.GetName(m_en, l.m_enName)) + (void)f.GetName(m_int, l.m_enName); + } + + public: + DoFindLocality(Query const & query, MwmValue * pMwm, volatile bool & isCancelled) + : m_query(query), m_vector(pMwm->m_cont, pMwm->GetHeader()), m_isCancelled(isCancelled) + { + m_en = StringUtf8Multilang::GetLangIndex("en"); + m_int = StringUtf8Multilang::GetLangIndex("int_name"); + } + + void Resize(size_t) {} + void StartNew(size_t ind) { m_index = ind; } + + void operator() (Query::TrieValueT const & v) + { + if (m_isCancelled) + throw Query::CancelException(); + + // find locality in current results + vector::iterator it = find_if(m_localities.begin(), m_localities.end(), + EqualID(v.m_featureId)); + if (it != m_localities.end()) + { + it->m_matchedTokens.push_back(m_index); + return; + } + + // load feature + FeatureType f; + m_vector.Get(v.m_featureId, f); + + // check, if feature is locality + static TypeChecker checker; + uint32_t const t = checker.GetType(f); + /// @todo Process countries too. + if (t > 0 && !checker.IsCountry(t)) + { + m_localities.push_back(Locality(v, t)); + + uint32_t penalty; + m_query.GetBestMatchName(f, penalty, m_localities.back().m_name); + + m_localities.back().m_matchedTokens.push_back(m_index); + + AssignEnglishName(f, m_localities.back()); + } + } + + bool GetBestLocality(Locality & res) + { + sort(m_localities.begin(), m_localities.end()); + + for (vector::const_reverse_iterator i = m_localities.rbegin(); i != m_localities.rend(); ++i) + { + if (i->IsFullNameMatched()) + { + res = *i; + return true; + } + } + + return false; + } + }; +} + +bool Query::SearchLocality(MwmValue * pMwm, impl::Locality & res) +{ + Params params(*this, true); + + serial::CodingParams cp(GetCPForTrie(pMwm->GetHeader().GetDefCodingParams())); + + ModelReaderPtr searchReader = pMwm->m_cont.GetReader(SEARCH_INDEX_FILE_TAG); + scoped_ptr pTrieRoot(::trie::reader::ReadTrie( + SubReaderWrapper(searchReader.GetPtr()), + trie::ValueReader(cp), + trie::EdgeValueReader())); + + for (size_t i = 0; i < pTrieRoot->m_edge.size(); ++i) + { + TrieIterator::Edge::EdgeStrT const & edge = pTrieRoot->m_edge[i].m_str; + if (edge[0] < search::CATEGORIES_LANG && params.IsLangExist(static_cast(edge[0]))) + { + scoped_ptr pLangRoot(pTrieRoot->GoToEdge(i)); + + impl::DoFindLocality doFind(*this, pMwm, m_cancel); + GetFeaturesInTrie(params.m_tokens, params.m_prefixTokens, + TrieRootPrefix(*pLangRoot, edge), doFind); + + // save better locality, if any + impl::Locality loc; + if (doFind.GetBestLocality(loc) && (res < loc)) + res = loc; + } + } + + return (res.m_type != 0); +} + void Query::SearchFeatures() { MWMVectorT mwmInfo; m_pIndex->GetMwmInfo(mwmInfo); + // do usual search in viewport and near me (without last rect) Params params(*this); - for (size_t i = 0; i < RECTSCOUNT; ++i) + for (size_t i = 0; i < RECTSCOUNT-1; ++i) { if (m_viewport[i].IsValid()) SearchFeatures(params, mwmInfo, i); @@ -698,11 +988,33 @@ namespace binary_search(m_offsets->begin(), m_offsets->end(), offset)); } }; + + template class TrieValuesHolder + { + vector > m_holder; + size_t m_ind; + FilterT const & m_filter; + + public: + TrieValuesHolder(FilterT const & filter) : m_filter(filter) {} + + void Resize(size_t count) { m_holder.resize(count); } + void StartNew(size_t ind) { m_ind = ind; } + void operator() (Query::TrieValueT const & v) + { + if (m_filter(v.m_featureId)) + m_holder[m_ind].push_back(v); + } + + template void GetValues(size_t ind, ToDo & toDo) const + { + for (size_t i = 0; i < m_holder[ind].size(); ++i) + toDo(m_holder[ind][i]); + } + }; } -void Query::SearchFeatures(Params const & params, - MWMVectorT const & mwmInfo, - size_t ind) +void Query::SearchFeatures(Params const & params, MWMVectorT const & mwmInfo, int ind) { for (MwmSet::MwmId mwmId = 0; mwmId < mwmInfo.size(); ++mwmId) { @@ -710,21 +1022,21 @@ void Query::SearchFeatures(Params const & params, if (m_viewport[ind].IsIntersect(mwmInfo[mwmId].m_limitRect)) { Index::MwmLock mwmLock(*m_pIndex, mwmId); - SearchInMWM(mwmLock, params, &m_offsetsInViewport[ind]); + SearchInMWM(mwmLock, params, ind); } } } -void Query::SearchInMWM(Index::MwmLock const & mwmLock, Params const & params, - OffsetsVectorT const * offsets) +void Query::SearchInMWM(Index::MwmLock const & mwmLock, Params const & params, int ind) { if (MwmValue * pMwm = mwmLock.GetValue()) { if (pMwm->m_cont.IsReaderExist(SEARCH_INDEX_FILE_TAG)) { feature::DataHeader const & header = pMwm->GetHeader(); - bool const isWorld = (header.GetType() == feature::DataHeader::world); + /// @todo do not process World.mwm here - do it in SearchLocality + bool const isWorld = (header.GetType() == feature::DataHeader::world); if (isWorld && !m_worldSearch) return; @@ -736,42 +1048,48 @@ void Query::SearchInMWM(Index::MwmLock const & mwmLock, Params const & params, trie::ValueReader(cp), trie::EdgeValueReader())); - // Get categories edge root. - scoped_ptr pCategoriesRoot; - TrieIterator::Edge::EdgeStrT categoriesEdge; - - size_t const count = pTrieRoot->m_edge.size(); - for (size_t i = 0; i < count; ++i) - { - TrieIterator::Edge::EdgeStrT const & edge = pTrieRoot->m_edge[i].m_str; - ASSERT_GREATER_OR_EQUAL(edge.size(), 1, ()); - - if (edge[0] == search::CATEGORIES_LANG) - { - categoriesEdge = edge; - pCategoriesRoot.reset(pTrieRoot->GoToEdge(i)); - break; - } - } - ASSERT_NOT_EQUAL(pCategoriesRoot, 0, ()); - MwmSet::MwmId const mwmId = mwmLock.GetID(); - impl::FeatureLoader emitter(*this, mwmId); + FeaturesFilter filter((ind == -1 || isWorld) ? 0 : &m_offsetsInViewport[ind][mwmId], m_cancel); - // Iterate through first language edges. + // Get categories for each token separately - find needed edge with categories. + TrieValuesHolder categoriesHolder(filter); + size_t const count = pTrieRoot->m_edge.size(); + { + scoped_ptr pCategoriesRoot; + TrieIterator::Edge::EdgeStrT categoriesEdge; + + for (size_t i = 0; i < count; ++i) + { + TrieIterator::Edge::EdgeStrT const & edge = pTrieRoot->m_edge[i].m_str; + ASSERT_GREATER_OR_EQUAL(edge.size(), 1, ()); + + if (edge[0] == search::CATEGORIES_LANG) + { + categoriesEdge = edge; + pCategoriesRoot.reset(pTrieRoot->GoToEdge(i)); + break; + } + } + ASSERT_NOT_EQUAL(pCategoriesRoot, 0, ()); + + GetFeaturesInTrie(params.m_tokens, params.m_prefixTokens, + TrieRootPrefix(*pCategoriesRoot, categoriesEdge), + categoriesHolder); + } + + impl::FeatureLoader emitter(*this, mwmId, ind); + + // Match tokens to feature for each language - iterate through first edges. for (size_t i = 0; i < count; ++i) { TrieIterator::Edge::EdgeStrT const & edge = pTrieRoot->m_edge[i].m_str; - if (edge[0] < search::CATEGORIES_LANG && params.m_langs.count(static_cast(edge[0]))) + if (edge[0] < search::CATEGORIES_LANG && params.IsLangExist(static_cast(edge[0]))) { scoped_ptr pLangRoot(pTrieRoot->GoToEdge(i)); MatchFeaturesInTrie(params.m_tokens, params.m_prefixTokens, TrieRootPrefix(*pLangRoot, edge), - TrieRootPrefix(*pCategoriesRoot, categoriesEdge), - FeaturesFilter((isWorld || offsets == 0) ? 0 : &((*offsets)[mwmId]), - m_cancel), - emitter); + filter, categoriesHolder, emitter); LOG(LDEBUG, ("Lang:", StringUtf8Multilang::GetLangByCode(static_cast(edge[0])), @@ -835,8 +1153,15 @@ void Query::MatchForSuggestions(strings::UniString const & token, Results & res) MatchForSuggestionsImpl(token, StringUtf8Multilang::GetLangIndex("en"), res); } -m2::RectD const & Query::GetViewport() const +m2::RectD const & Query::GetViewport(int viewportID/* = -1*/) const { + if (viewportID == ADDRESS_RECT_ID) + { + // special case for search address - return viewport around location + return m_viewport[viewportID]; + } + + // return first valid actual viewport if (m_viewport[0].IsValid()) return m_viewport[0]; else @@ -846,6 +1171,16 @@ m2::RectD const & Query::GetViewport() const } } +m2::PointD Query::GetPosition(int viewportID/* = -1*/) const +{ + if (viewportID == ADDRESS_RECT_ID) + { + // special case for search address - return center of location + return m_viewport[viewportID].Center(); + } + return m_position; +} + void Query::SearchAllInViewport(m2::RectD const & viewport, Results & res, unsigned int resultsNeeded) { ASSERT ( viewport.IsValid(), () ); diff --git a/search/search_query.hpp b/search/search_query.hpp index b0ad0e21f7..cb39d86067 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -34,12 +34,15 @@ namespace impl class FeatureLoader; class BestNameFinder; class PreResult2Maker; + struct Locality; + class DoFindLocality; } class Query { public: - static int const m_scaleDepthSearch = 7; + static int const SCALE_SEARCH_DEPTH = 7; + static int const ADDRESS_SCALE = 10; struct SuggestT { @@ -84,10 +87,13 @@ public: inline bool IsCanceled() const { return m_cancel; } struct CancelException {}; + typedef trie::ValueReader::ValueType TrieValueT; + private: friend class impl::FeatureLoader; friend class impl::BestNameFinder; friend class impl::PreResult2Maker; + friend class impl::DoFindLocality; void InitSearch(string const & query); void InitKeywordsScorer(); @@ -96,12 +102,12 @@ private: typedef vector MWMVectorT; typedef vector > OffsetsVectorT; + void SetViewportByIndex(MWMVectorT const & mwmInfo, m2::RectD const & viewport, size_t idx); void UpdateViewportOffsets(MWMVectorT const & mwmInfo, m2::RectD const & rect, OffsetsVectorT & offsets); void ClearCache(size_t ind); - typedef trie::ValueReader::ValueType TrieValueT; - void AddResultFromTrie(TrieValueT const & val, size_t mwmID); + void AddResultFromTrie(TrieValueT const & val, size_t mwmID, int viewportID); void FlushResults(Results & res, void (Results::*pAddFn)(Result const &)); @@ -114,22 +120,36 @@ private: TokensVectorT m_prefixTokens; LangsSetT m_langs; - Params(Query & q); + /// Initialize search params (tokens, languages). + /// @param[in] isLocalities Use true when search for locality in World. + Params(Query const & q, bool isLocalities = false); + + /// @param[in] eraseInds Sorted vector of token's indexes. + void EraseTokens(vector const & eraseInds); + + bool IsEmpty() const { return (m_tokens.empty() && m_prefixTokens.empty()); } + bool IsLangExist(uint8_t l) const { return (m_langs.count(l) > 0); } + + private: + void AddSynonims(Query const & q, bool isLocalities); + void FillLanguages(Query const & q); }; + void SearchAddress(); + bool SearchLocality(MwmValue * pMwm, impl::Locality & res); + void SearchFeatures(); - void SearchFeatures(Params const & params, - MWMVectorT const & mwmInfo, - size_t ind); - void SearchInMWM(Index::MwmLock const & mwmLock, - Params const & params, - OffsetsVectorT const * offsets); + void SearchFeatures(Params const & params, MWMVectorT const & mwmInfo, int ind); + + /// Do search in particular map. Pass offsets == 0 if you don't want + /// results set to be intersected with source feature's offsets. + void SearchInMWM(Index::MwmLock const & mwmLock, Params const & params, int ind); void SuggestStrings(Results & res); bool MatchForSuggestionsImpl(strings::UniString const & token, int8_t lang, Results & res); void MatchForSuggestions(strings::UniString const & token, Results & res); - void GetBestMatchName(FeatureType const & f, uint32_t & penalty, string & name); + void GetBestMatchName(FeatureType const & f, uint32_t & penalty, string & name) const; inline Result MakeResult(impl::PreResult2 const & r, set const * pPrefferedTypes = 0) const { @@ -147,13 +167,22 @@ private: buffer_vector m_tokens; strings::UniString m_prefix; - static size_t const RECTSCOUNT = 2; + /// 0 - current viewport rect + /// 1 - near me rect + /// 2 - around city rect + static size_t const RECTSCOUNT = 3; + static int const ADDRESS_RECT_ID = RECTSCOUNT-1; m2::RectD m_viewport[RECTSCOUNT]; bool m_worldSearch; + /// @name Get ranking params. + /// @param[in] viewportID Index of search viewport (@see comments above); -1 means default viewport. + //@{ /// @return Rect for viewport-distance calculation. - m2::RectD const & GetViewport() const; + m2::RectD const & GetViewport(int viewportID = -1) const; + m2::PointD GetPosition(int viewportID = -1) const; + //@} m2::PointD m_position;