diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index c4175da738..2a234d92ab 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -391,6 +391,8 @@ extern "C" vparams.m_inputLocale = ReplaceDeprecatedLanguageCode(jni::ToNativeString(env, lang)); vparams.m_hotelsFilter = g_hotelsFilterBuilder.Build(env, hotelsFilter); + // TODO (@alexzatsepin): set up vparams.m_onCompleted here and use + // HotelsClassifier for hotel queries detection. g_framework->NativeFramework()->SearchInViewport(vparams); if (isMapAndTable) diff --git a/iphone/Maps/Classes/Search/MWMSearch.mm b/iphone/Maps/Classes/Search/MWMSearch.mm index 8f4e5f7585..746b88b804 100644 --- a/iphone/Maps/Classes/Search/MWMSearch.mm +++ b/iphone/Maps/Classes/Search/MWMSearch.mm @@ -99,7 +99,8 @@ using TObservers = NSHashTable<__kindof TObserver>; } { __weak auto weakSelf = self; - m_viewportParams.m_onCompleted = [weakSelf] { + m_viewportParams.m_onCompleted = [weakSelf](search::Results const & results) { + // TODO (@igrechuhin): do something useful with |results|. __strong auto self = weakSelf; if (!self) return; diff --git a/map/framework.cpp b/map/framework.cpp index 95449f880c..10e33394c7 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1154,7 +1154,7 @@ bool Framework::SearchInViewport(search::ViewportSearchParams const & params) static_cast(*this), [params](search::Results const & results) { if (results.IsEndMarker() && params.m_onCompleted) - GetPlatform().RunOnGuiThread([params]() { params.m_onCompleted(); }); + GetPlatform().RunOnGuiThread([params, results]() { params.m_onCompleted(results); }); }); SetCurrentPositionIfPossible(p); @@ -2673,7 +2673,9 @@ bool Framework::ParseEditorDebugCommand(search::SearchParams const & params) DebugPrint(types), types.GetBestType(), smd)); } params.m_onResults(results); - params.m_onResults(search::Results::GetEndMarker(false)); + + results.SetEndMarker(false /* isCancelled */); + params.m_onResults(results); return true; } else if (params.m_query == "?eclear") diff --git a/search/emitter.hpp b/search/emitter.hpp index eb51542932..dc4d239d4a 100644 --- a/search/emitter.hpp +++ b/search/emitter.hpp @@ -31,8 +31,9 @@ public: inline void Finish(bool cancelled) { + m_results.SetEndMarker(cancelled); if (m_onResults) - m_onResults(Results::GetEndMarker(cancelled)); + m_onResults(m_results); else LOG(LERROR, ("OnResults is not set.")); } diff --git a/search/engine.cpp b/search/engine.cpp index 7e1e0f473a..484271acc8 100644 --- a/search/engine.cpp +++ b/search/engine.cpp @@ -240,7 +240,9 @@ void Engine::DoSearch(SearchParams const & params, m2::RectD const & viewport, // Early exit when query processing is cancelled. if (processor.IsCancelled()) { - params.m_onResults(Results::GetEndMarker(true /* isCancelled */)); + Results results; + results.SetEndMarker(true /* isCancelled */); + params.m_onResults(results); return; } diff --git a/search/hotels_classifier.cpp b/search/hotels_classifier.cpp index 4a21d3cb67..0fa7d4250e 100644 --- a/search/hotels_classifier.cpp +++ b/search/hotels_classifier.cpp @@ -1,23 +1,27 @@ #include "search/hotels_classifier.hpp" -#include "search/result.hpp" +#include "std/cstdint.hpp" namespace search { -void HotelsClassifier::AddBatch(Results const & results) +// static +bool HotelsClassifier::IsHotelResults(Results const & results) { - if (results.IsEndMarker()) - return; + HotelsClassifier classifier; + classifier.Add(results.begin(), results.end()); + return classifier.IsHotelResults(); +} - for (auto const & result : results) +void HotelsClassifier::Add(Results::Iter begin, Results::Iter end) +{ + for (; begin != end; ++begin) { + m_numHotels += (*begin).m_metadata.m_isHotel; ++m_numResults; - if (result.m_metadata.m_isHotel) - ++m_numHotels; } } -bool HotelsClassifier::IsHotelQuery() const +bool HotelsClassifier::IsHotelResults() const { // Threshold used to activate hotels mode. Probably is too strict, // but we don't have statistics now. diff --git a/search/hotels_classifier.hpp b/search/hotels_classifier.hpp index 1619a5c746..c6b27cf55d 100644 --- a/search/hotels_classifier.hpp +++ b/search/hotels_classifier.hpp @@ -1,11 +1,9 @@ #pragma once -#include "std/cstdint.hpp" +#include "search/result.hpp" namespace search { -class Results; - // A binary classifier that can be used in conjunction with search // engine to decide whether the majority of results are hotels or not. // @@ -13,9 +11,10 @@ class Results; class HotelsClassifier { public: - void AddBatch(Results const & results); + static bool IsHotelResults(Results const & results); - bool IsHotelQuery() const; + void Add(Results::Iter begin, Results::Iter end); + bool IsHotelResults() const; private: uint64_t m_numHotels = 0; diff --git a/search/result.cpp b/search/result.cpp index 6a9a00f3cc..2d7c176ad2 100644 --- a/search/result.cpp +++ b/search/result.cpp @@ -5,6 +5,7 @@ namespace search { +// Result ------------------------------------------------------------------------------------------ Result::Result(FeatureID const & id, m2::PointD const & pt, string const & str, string const & address, string const & type, uint32_t featureType, Metadata const & meta) @@ -139,10 +140,16 @@ string Result::ToStringForStats() const return s; } -bool Results::AddResult(Result && res) +// Results ----------------------------------------------------------------------------------------- +Results::Results() +{ + Clear(); +} + +bool Results::AddResult(Result && result) { // Find first feature result. - auto it = find_if(m_vec.begin(), m_vec.end(), [](Result const & r) + auto it = find_if(m_results.begin(), m_results.end(), [](Result const & r) { switch (r.GetResultType()) { @@ -153,43 +160,46 @@ bool Results::AddResult(Result && res) } }); - if (res.IsSuggest()) + if (result.IsSuggest()) { - if (distance(m_vec.begin(), it) >= MAX_SUGGESTS_COUNT) + if (distance(m_results.begin(), it) >= MAX_SUGGESTS_COUNT) return false; - for (auto i = m_vec.begin(); i != it; ++i) - if (res.IsEqualSuggest(*i)) + for (auto i = m_results.begin(); i != it; ++i) + if (result.IsEqualSuggest(*i)) return false; - - for (auto i = it; i != m_vec.end(); ++i) - { - auto & r = *i; - auto const oldPos = r.GetPositionInResults(); - r.SetPositionInResults(oldPos + 1); - } - res.SetPositionInResults(distance(m_vec.begin(), it)); - m_vec.insert(it, move(res)); + InsertResult(it, move(result)); } else { - for (; it != m_vec.end(); ++it) - if (res.IsEqualFeature(*it)) + for (; it != m_results.end(); ++it) + { + if (result.IsEqualFeature(*it)) return false; - - res.SetPositionInResults(m_vec.size()); - m_vec.push_back(move(res)); + } + InsertResult(m_results.end(), move(result)); } return true; } +void Results::AddResultNoChecks(Result && result) +{ + InsertResult(m_results.end(), move(result)); +} + +void Results::Clear() +{ + m_results.clear(); + m_status = Status::None; +} + size_t Results::GetSuggestsCount() const { size_t res = 0; for (size_t i = 0; i < GetCount(); i++) { - if (m_vec[i].IsSuggest()) + if (m_results[i].IsSuggest()) ++res; else { @@ -200,10 +210,22 @@ size_t Results::GetSuggestsCount() const return res; } -//////////////////////////////////////////////////////////////////////////////////// -// AddressInfo implementation -//////////////////////////////////////////////////////////////////////////////////// +void Results::InsertResult(vector::iterator where, Result && result) +{ + ASSERT_LESS(m_results.size(), numeric_limits::max(), ()); + for (auto it = where; it != m_results.end(); ++it) + { + auto & r = *it; + auto const position = r.GetPositionInResults(); + r.SetPositionInResults(position + 1); + } + + result.SetPositionInResults(distance(m_results.begin(), where)); + m_results.insert(where, move(result)); +} + +// AddressInfo ------------------------------------------------------------------------------------- bool AddressInfo::IsEmptyName() const { return m_name.empty() && m_house.empty(); @@ -324,5 +346,4 @@ string DebugPrint(Result const & result) return "Result { Name: " + result.GetString() + "; Type: " + result.GetFeatureType() + "; Info: " + DebugPrint(result.GetRankingInfo()) + " }"; } - } // namespace search diff --git a/search/result.hpp b/search/result.hpp index 916d28726d..aeb5a9b17c 100644 --- a/search/result.hpp +++ b/search/result.hpp @@ -133,67 +133,63 @@ public: class Results { - vector m_vec; - - enum StatusT - { - NONE, // default status - ENDED_CANCELLED, // search ended with canceling - ENDED // search ended itself - }; - StatusT m_status; - - explicit Results(bool isCancelled) - { - m_status = (isCancelled ? ENDED_CANCELLED : ENDED); - } - public: - Results() : m_status(NONE) {} + using Iter = vector::const_iterator; - static Results GetEndMarker(bool isCancelled) { return Results(isCancelled); } + Results(); - bool IsEndMarker() const { return (m_status != NONE); } - bool IsEndedNormal() const { return (m_status == ENDED); } - bool IsEndedCancelled() const { return m_status == ENDED_CANCELLED; } + inline bool IsEndMarker() const { return m_status != Status::None; } + inline bool IsEndedNormal() const { return m_status == Status::EndedNormal; } + inline bool IsEndedCancelled() const { return m_status == Status::EndedCancelled; } - bool AddResult(Result && res); - - /// Fast function that don't do any duplicates checks. - /// Used in viewport search only. - void AddResultNoChecks(Result && res) + void SetEndMarker(bool cancelled) { - ASSERT_LESS(m_vec.size(), numeric_limits::max(), ()); - res.SetPositionInResults(static_cast(m_vec.size())); - m_vec.push_back(move(res)); + m_status = cancelled ? Status::EndedCancelled : Status::EndedNormal; } - inline void Clear() { m_vec.clear(); } + bool AddResult(Result && result); - typedef vector::const_iterator IterT; + // Fast version of AddResult() that doesn't do any duplicates checks. + void AddResultNoChecks(Result && result); - inline IterT begin() const { return m_vec.begin(); } - inline IterT end() const { return m_vec.end(); } + void Clear(); - inline size_t GetCount() const { return m_vec.size(); } + inline Iter begin() const { return m_results.begin(); } + inline Iter end() const { return m_results.end(); } + + inline size_t GetCount() const { return m_results.size(); } size_t GetSuggestsCount() const; inline Result & GetResult(size_t i) { - ASSERT_LESS(i, m_vec.size(), ()); - return m_vec[i]; + ASSERT_LESS(i, m_results.size(), ()); + return m_results[i]; } inline Result const & GetResult(size_t i) const { - ASSERT_LESS(i, m_vec.size(), ()); - return m_vec[i]; + ASSERT_LESS(i, m_results.size(), ()); + return m_results[i]; } - inline void Swap(Results & rhs) + inline void Swap(Results & rhs) { m_results.swap(rhs.m_results); } + +private: + enum class Status { - m_vec.swap(rhs.m_vec); - } + None, + EndedCancelled, + EndedNormal + }; + + // Inserts |result| in |m_results| at position denoted by |where|. + // + // *NOTE* all iterators, references and pointers to |m_results| are + // invalid after the call. + void InsertResult(vector::iterator where, Result && result); + + vector m_results; + Status m_status; }; struct AddressInfo diff --git a/search/viewport_search_callback.cpp b/search/viewport_search_callback.cpp index 6fce48768b..988a68116f 100644 --- a/search/viewport_search_callback.cpp +++ b/search/viewport_search_callback.cpp @@ -2,18 +2,26 @@ #include "search/result.hpp" +#include "base/assert.hpp" + namespace search { ViewportSearchCallback::ViewportSearchCallback(Delegate & delegate, TOnResults onResults) - : m_delegate(delegate), m_onResults(move(onResults)), m_hotelsModeSet(false), m_firstCall(true) + : m_delegate(delegate) + , m_onResults(move(onResults)) + , m_hotelsModeSet(false) + , m_firstCall(true) + , m_lastResultsSize(0) { } void ViewportSearchCallback::operator()(Results const & results) { - m_hotelsClassif.AddBatch(results); + ASSERT_LESS_OR_EQUAL(m_lastResultsSize, results.GetCount(), ()); + m_hotelsClassif.Add(results.begin() + m_lastResultsSize, results.end()); + m_lastResultsSize = results.GetCount(); - if (!m_hotelsModeSet && m_hotelsClassif.IsHotelQuery()) + if (!m_hotelsModeSet && m_hotelsClassif.IsHotelResults()) { m_delegate.SetHotelDisplacementMode(); m_hotelsModeSet = true; diff --git a/search/viewport_search_callback.hpp b/search/viewport_search_callback.hpp index ffc4c25c02..61919f5894 100644 --- a/search/viewport_search_callback.hpp +++ b/search/viewport_search_callback.hpp @@ -37,8 +37,10 @@ private: Delegate & m_delegate; TOnResults m_onResults; - HotelsClassifier m_hotelsClassif; bool m_hotelsModeSet; bool m_firstCall; + + HotelsClassifier m_hotelsClassif; + size_t m_lastResultsSize; }; } // namespace search diff --git a/search/viewport_search_params.hpp b/search/viewport_search_params.hpp index 4c38a98f17..2b34ca8dd0 100644 --- a/search/viewport_search_params.hpp +++ b/search/viewport_search_params.hpp @@ -8,10 +8,12 @@ namespace search { +class Results; + struct ViewportSearchParams { using TOnStarted = function; - using TOnCompleted = function; + using TOnCompleted = function; string m_query; string m_inputLocale;