diff options
author | Yuri Gorshenin <y@maps.me> | 2016-08-02 14:06:14 +0300 |
---|---|---|
committer | Yuri Gorshenin <y@maps.me> | 2016-08-03 13:11:06 +0300 |
commit | 0badc084a1043e06e429c7cd66176462c4fa33ab (patch) | |
tree | 1460f4d608bbc707e0225c6d7241e66b97774eaa /search | |
parent | 1e6dd75476400e45ab8bf49ba7bd4c7a65cde368 (diff) |
[search][ui] Implemented hotels classifier.
Diffstat (limited to 'search')
-rw-r--r-- | search/hotels_classifier.cpp | 28 | ||||
-rw-r--r-- | search/hotels_classifier.hpp | 24 | ||||
-rw-r--r-- | search/interactive_search_callback.cpp | 25 | ||||
-rw-r--r-- | search/interactive_search_callback.hpp | 32 | ||||
-rw-r--r-- | search/intermediate_result.cpp | 6 | ||||
-rw-r--r-- | search/result.hpp | 18 | ||||
-rw-r--r-- | search/search.pro | 4 | ||||
-rw-r--r-- | search/search_integration_tests/interactive_search_test.cpp | 110 | ||||
-rw-r--r-- | search/search_integration_tests/search_integration_tests.pro | 1 | ||||
-rw-r--r-- | search/search_tests_support/test_search_request.cpp | 13 | ||||
-rw-r--r-- | search/search_tests_support/test_search_request.hpp | 7 |
11 files changed, 260 insertions, 8 deletions
diff --git a/search/hotels_classifier.cpp b/search/hotels_classifier.cpp new file mode 100644 index 0000000000..4a21d3cb67 --- /dev/null +++ b/search/hotels_classifier.cpp @@ -0,0 +1,28 @@ +#include "search/hotels_classifier.hpp" + +#include "search/result.hpp" + +namespace search +{ +void HotelsClassifier::AddBatch(Results const & results) +{ + if (results.IsEndMarker()) + return; + + for (auto const & result : results) + { + ++m_numResults; + if (result.m_metadata.m_isHotel) + ++m_numHotels; + } +} + +bool HotelsClassifier::IsHotelQuery() const +{ + // Threshold used to activate hotels mode. Probably is too strict, + // but we don't have statistics now. + double const kThreshold = 0.95; + + return m_numResults == 0 ? false : m_numHotels >= kThreshold * m_numResults; +} +} // namespace search diff --git a/search/hotels_classifier.hpp b/search/hotels_classifier.hpp new file mode 100644 index 0000000000..65a8501208 --- /dev/null +++ b/search/hotels_classifier.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "std/cstdint.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. +// +// *NOTE* This class is NOT thread safe. +class HotelsClassifier +{ +public: + void AddBatch(Results const & results); + + bool IsHotelQuery() const; + +private: + uint64_t m_numHotels = 0; + uint64_t m_numResults = 0; +}; +} // namespace search diff --git a/search/interactive_search_callback.cpp b/search/interactive_search_callback.cpp new file mode 100644 index 0000000000..d7981bab4d --- /dev/null +++ b/search/interactive_search_callback.cpp @@ -0,0 +1,25 @@ +#include "search/interactive_search_callback.hpp" + +#include "search/result.hpp" + +namespace search +{ +InteractiveSearchCallback::InteractiveSearchCallback(TSetDisplacementMode && setMode, + TOnResults && onResults) + : m_setMode(move(setMode)), m_onResults(move(onResults)), m_hotelsModeSet(false) +{ +} + +void InteractiveSearchCallback::operator()(search::Results const & results) +{ + m_hotelsClassif.AddBatch(results); + + if (!m_hotelsModeSet && m_hotelsClassif.IsHotelQuery()) + { + m_setMode(); + m_hotelsModeSet = true; + } + + m_onResults(results); +} +} // namespace search diff --git a/search/interactive_search_callback.hpp b/search/interactive_search_callback.hpp new file mode 100644 index 0000000000..8d42f537a0 --- /dev/null +++ b/search/interactive_search_callback.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "search/hotels_classifier.hpp" +#include "search/params.hpp" + +#include "std/function.hpp" + +namespace search +{ +class Results; + +// An on-results-callback that should be used for interactive search. +// +// *NOTE* the class is NOT thread safe. +class InteractiveSearchCallback +{ +public: + using TSetDisplacementMode = function<void()>; + using TOnResults = search::TOnResults; + + InteractiveSearchCallback(TSetDisplacementMode && setMode, TOnResults && onResults); + + void operator()(search::Results const & results); + +private: + TSetDisplacementMode m_setMode; + TOnResults m_onResults; + + search::HotelsClassifier m_hotelsClassif; + bool m_hotelsModeSet; +}; +} // namespace search diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index 0a983e036c..c0652c078c 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -29,7 +29,8 @@ double const DIST_SAME_STREET = 5000.0; char const * const kEmptyRatingSymbol = "-"; char const * const kPricingSymbol = "$"; -void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) +void ProcessMetadata(FeatureType const & ft, feature::TypesHolder const & types, + Result::Metadata & meta) { if (meta.m_isInitialized) return; @@ -56,6 +57,7 @@ void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) bool const isSponsoredHotel = ftypes::IsBookingChecker::Instance()(ft); meta.m_isSponsoredHotel = isSponsoredHotel; + meta.m_isHotel = ftypes::IsHotelChecker::Instance()(ft); if (isSponsoredHotel) { @@ -114,7 +116,7 @@ PreResult2::PreResult2(FeatureType const & f, PreResult1 const * p, m2::PointD c m_region.SetParams(fileName, center); m_distance = PointDistance(center, pivot); - ProcessMetadata(f, m_metadata); + ProcessMetadata(f, m_types, m_metadata); } PreResult2::PreResult2(double lat, double lon) diff --git a/search/result.hpp b/search/result.hpp index b3497bf0c5..e3912d9f31 100644 --- a/search/result.hpp +++ b/search/result.hpp @@ -31,13 +31,18 @@ public: /// Metadata for search results. Considered valid if m_resultType == RESULT_FEATURE. struct Metadata { - string m_cuisine; // Valid only if not empty. Used for restaurants. - int m_stars = 0; // Valid only if not 0. Used for hotels. - bool m_isSponsoredHotel = false; // Used for hotels. - osm::YesNoUnknown m_isOpenNow = osm::Unknown; // Valid for any result. + string m_cuisine; // Valid only if not empty. Used for restaurants. + + // Following fields are used for hotels only. string m_hotelApproximatePricing; string m_hotelRating; - /// True if the struct is already assigned or need to be calculated otherwise. + int m_stars = 0; + bool m_isSponsoredHotel = false; + bool m_isHotel = false; + + osm::YesNoUnknown m_isOpenNow = osm::Unknown; // Valid for any result. + + // True if the struct is already assigned or need to be calculated otherwise. bool m_isInitialized = false; }; @@ -171,6 +176,9 @@ public: inline IterT Begin() const { return m_vec.begin(); } inline IterT End() const { return m_vec.end(); } + inline IterT begin() const { return m_vec.begin(); } + inline IterT end() const { return m_vec.end(); } + inline size_t GetCount() const { return m_vec.size(); } size_t GetSuggestsCount() const; diff --git a/search/search.pro b/search/search.pro index b3612baf6e..3f50c1dadf 100644 --- a/search/search.pro +++ b/search/search.pro @@ -25,9 +25,11 @@ HEADERS += \ geocoder_context.hpp \ geometry_cache.hpp \ geometry_utils.hpp \ + hotels_classifier.hpp \ house_detector.hpp \ house_numbers_matcher.hpp \ house_to_street_table.hpp \ + interactive_search_callback.hpp \ intermediate_result.hpp \ intersection_result.hpp \ interval_set.hpp \ @@ -82,9 +84,11 @@ SOURCES += \ geocoder_context.cpp \ geometry_cache.cpp \ geometry_utils.cpp \ + hotels_classifier.cpp \ house_detector.cpp \ house_numbers_matcher.cpp \ house_to_street_table.cpp \ + interactive_search_callback.cpp \ intermediate_result.cpp \ intersection_result.cpp \ keyword_lang_matcher.cpp \ diff --git a/search/search_integration_tests/interactive_search_test.cpp b/search/search_integration_tests/interactive_search_test.cpp new file mode 100644 index 0000000000..d7b28ac138 --- /dev/null +++ b/search/search_integration_tests/interactive_search_test.cpp @@ -0,0 +1,110 @@ +#include "testing/testing.hpp" + +#include "generator/generator_tests_support/test_feature.hpp" + +#include "search/interactive_search_callback.hpp" +#include "search/mode.hpp" +#include "search/search_integration_tests/helpers.hpp" +#include "search/search_tests_support/test_results_matching.hpp" +#include "search/search_tests_support/test_search_request.hpp" + +#include "base/macros.hpp" + +using namespace generator::tests_support; +using namespace search::tests_support; + +using TRules = vector<shared_ptr<MatchingRule>>; + +namespace search +{ +namespace +{ +class TestCafe : public TestPOI +{ +public: + TestCafe(m2::PointD const & center) : TestPOI(center, "cafe", "en") + { + SetTypes({{"amenity", "cafe"}}); + } +}; + +class TestHotel : public TestPOI +{ +public: + TestHotel(m2::PointD const & center) : TestPOI(center, "hotel", "en") + { + SetTypes({{"tourism", "hotel"}}); + } +}; + +class InteractiveSearchRequest : public TestSearchRequest +{ +public: + InteractiveSearchRequest(TestSearchEngine & engine, string const & query, + m2::RectD const & viewport, bool & mode) + : TestSearchRequest( + engine, query, "en" /* locale */, Mode::Viewport, viewport, + bind(&InteractiveSearchRequest::OnStarted, this), + InteractiveSearchCallback([&mode]() { mode = true; }, + bind(&InteractiveSearchRequest::OnResults, this, _1))) + { + } +}; + +class InteractiveSearchTest : public SearchTest +{ +}; + +double const kDX[] = {-0.01, 0, 0, 0.01}; +double const kDY[] = {0, -0.01, 0.01, 0}; + +static_assert(ARRAY_SIZE(kDX) == ARRAY_SIZE(kDY), "Wrong deltas lists"); + +UNIT_CLASS_TEST(InteractiveSearchTest, Smoke) +{ + m2::PointD const cafesPivot(-1, -1); + m2::PointD const hotelsPivot(1, 1); + + vector<TestCafe> cafes; + for (size_t i = 0; i < ARRAY_SIZE(kDX); ++i) + cafes.emplace_back(m2::Shift(cafesPivot, kDX[i], kDY[i])); + + vector<TestHotel> hotels; + for (size_t i = 0; i < ARRAY_SIZE(kDX); ++i) + hotels.emplace_back(m2::Shift(hotelsPivot, kDX[i], kDY[i])); + + auto const id = BuildCountry("Wonderland", [&](TestMwmBuilder & builder) { + for (auto const & cafe : cafes) + builder.Add(cafe); + for (auto const & hotel : hotels) + builder.Add(hotel); + }); + + { + bool mode = false; + InteractiveSearchRequest request( + m_engine, "cafe", m2::RectD(m2::PointD(-1.5, -1.5), m2::PointD(-0.5, -0.5)), mode); + request.Wait(); + + TRules const rules = {ExactMatch(id, cafes[0]), ExactMatch(id, cafes[1]), + ExactMatch(id, cafes[2]), ExactMatch(id, cafes[3])}; + + TEST(!mode, ()); + TEST(MatchResults(m_engine, rules, request.Results()), ()); + } + + { + bool mode = false; + InteractiveSearchRequest request(m_engine, "hotel", + m2::RectD(m2::PointD(0.5, 0.5), m2::PointD(1.5, 1.5)), mode); + request.Wait(); + + TRules const rules = {ExactMatch(id, hotels[0]), ExactMatch(id, hotels[1]), + ExactMatch(id, hotels[2]), ExactMatch(id, hotels[3])}; + + TEST(mode, ()); + TEST(MatchResults(m_engine, rules, request.Results()), ()); + } +} +} // namespace +} // namespace search diff --git a/search/search_integration_tests/search_integration_tests.pro b/search/search_integration_tests/search_integration_tests.pro index c764ff9498..b05b0a5645 100644 --- a/search/search_integration_tests/search_integration_tests.pro +++ b/search/search_integration_tests/search_integration_tests.pro @@ -21,6 +21,7 @@ SOURCES += \ ../../testing/testingmain.cpp \ generate_tests.cpp \ helpers.cpp \ + interactive_search_test.cpp \ pre_ranker_test.cpp \ processor_test.cpp \ smoke_test.cpp \ diff --git a/search/search_tests_support/test_search_request.cpp b/search/search_tests_support/test_search_request.cpp index 58bb686c77..1c7f50d3ac 100644 --- a/search/search_tests_support/test_search_request.cpp +++ b/search/search_tests_support/test_search_request.cpp @@ -29,6 +29,19 @@ TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, SearchParams par engine.Search(params, viewport); } +TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, string const & query, + string const & locale, Mode mode, m2::RectD const & viewport, + TOnStarted onStarted, TOnResults onResults) +{ + SearchParams params; + params.m_query = query; + params.m_inputLocale = locale; + params.SetMode(mode); + params.m_onStarted = move(onStarted); + params.m_onResults = move(onResults); + engine.Search(params, viewport); +} + void TestSearchRequest::Wait() { unique_lock<mutex> lock(m_mu); diff --git a/search/search_tests_support/test_search_request.hpp b/search/search_tests_support/test_search_request.hpp index 35c1031ebb..4b86d5a3e2 100644 --- a/search/search_tests_support/test_search_request.hpp +++ b/search/search_tests_support/test_search_request.hpp @@ -34,8 +34,13 @@ public: steady_clock::duration ResponseTime() const; vector<search::Result> const & Results() const; -private: +protected: + TestSearchRequest(TestSearchEngine & engine, string const & query, string const & locale, + Mode mode, m2::RectD const & viewport, TOnStarted onStarted, + TOnResults onResults); + void SetUpCallbacks(SearchParams & params); + void OnStarted(); void OnResults(search::Results const & results); |