Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/search
diff options
context:
space:
mode:
authorYuri Gorshenin <y@maps.me>2016-08-02 14:06:14 +0300
committerYuri Gorshenin <y@maps.me>2016-08-03 13:11:06 +0300
commit0badc084a1043e06e429c7cd66176462c4fa33ab (patch)
tree1460f4d608bbc707e0225c6d7241e66b97774eaa /search
parent1e6dd75476400e45ab8bf49ba7bd4c7a65cde368 (diff)
[search][ui] Implemented hotels classifier.
Diffstat (limited to 'search')
-rw-r--r--search/hotels_classifier.cpp28
-rw-r--r--search/hotels_classifier.hpp24
-rw-r--r--search/interactive_search_callback.cpp25
-rw-r--r--search/interactive_search_callback.hpp32
-rw-r--r--search/intermediate_result.cpp6
-rw-r--r--search/result.hpp18
-rw-r--r--search/search.pro4
-rw-r--r--search/search_integration_tests/interactive_search_test.cpp110
-rw-r--r--search/search_integration_tests/search_integration_tests.pro1
-rw-r--r--search/search_tests_support/test_search_request.cpp13
-rw-r--r--search/search_tests_support/test_search_request.hpp7
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);