From 6cdd6a8f2a85509deb3215805f14aeed5a707ca2 Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Wed, 3 Apr 2019 15:03:54 +0300 Subject: [PATCH] [booking] booking filter is adapted to work with raw feature ids. --- map/booking_availability_filter.cpp | 266 ++++++++++++++++++-------- map/booking_availability_filter.hpp | 5 + map/booking_filter.hpp | 2 + map/booking_filter_params.hpp | 4 + map/booking_filter_processor.cpp | 67 ++++--- map/booking_filter_processor.hpp | 14 +- map/map_tests/booking_filter_test.cpp | 106 +++++++++- partners_api/booking_api.hpp | 1 + 8 files changed, 349 insertions(+), 116 deletions(-) diff --git a/map/booking_availability_filter.cpp b/map/booking_availability_filter.cpp index f3d29e5e1e..0456828b0d 100644 --- a/map/booking_availability_filter.cpp +++ b/map/booking_availability_filter.cpp @@ -8,6 +8,7 @@ #include "indexer/data_source.hpp" #include "indexer/feature_decl.hpp" +#include "indexer/ftypes_sponsored.hpp" #include "platform/platform.hpp" @@ -20,71 +21,120 @@ using namespace booking::filter; namespace { -struct HotelToResult -{ - explicit HotelToResult(search::Result const & result) : m_result(result) {} +auto constexpr kMaxCountInRequest = booking::RawApi::GetMaxHotelsInAvailabilityRequest(); - search::Result m_result; +template +class HotelInfo +{ +public: + explicit HotelInfo(T const & requestedVia) : m_mappedValue(requestedVia) {} + HotelInfo(std::string const & hotelId, availability::Cache::HotelStatus const & cacheStatus, + T const & mappedValue) + : m_hotelId(hotelId), m_cacheStatus(cacheStatus), m_mappedValue(mappedValue) + { + } + + void SetHotelId(std::string const & hotelId) { m_hotelId = hotelId; } + void SetStatus(availability::Cache::HotelStatus cacheStatus) { m_cacheStatus = cacheStatus; } + + std::string const & GetHotelId() const { return m_hotelId; } + availability::Cache::HotelStatus GetStatus() const { return m_cacheStatus; } + + T const & GetMappedValue() const { return m_mappedValue; } + T & GetMappedValue() { return m_mappedValue; } + +private: std::string m_hotelId; availability::Cache::HotelStatus m_cacheStatus = availability::Cache::HotelStatus::Absent; + T m_mappedValue; }; -using HotelToResults = std::vector; +template +using HotelsMapping = std::vector>; -void UpdateCache(HotelToResults const & hotelToResults, std::vector const & hotelIds, +using HotelToResult = HotelInfo; +using HotelToFeatureId = HotelInfo; +using HotelToResults = HotelsMapping; +using HotelToFeatureIds = HotelsMapping; + +template +void UpdateCache(HotelsMapping const & hotelsMapping, std::vector const & hotelIds, availability::Cache & cache) { using availability::Cache; ASSERT(std::is_sorted(hotelIds.begin(), hotelIds.end()), ()); - for (auto & hotelToResult : hotelToResults) + for (auto & hotel : hotelsMapping) { - if (hotelToResult.m_cacheStatus != Cache::HotelStatus::Absent) + if (hotel.GetStatus() != Cache::HotelStatus::Absent) continue; - if (std::binary_search(hotelIds.cbegin(), hotelIds.cend(), hotelToResult.m_hotelId)) - cache.Insert(hotelToResult.m_hotelId, Cache::HotelStatus::Available); + if (std::binary_search(hotelIds.cbegin(), hotelIds.cend(), hotel.GetHotelId())) + cache.Insert(hotel.GetHotelId(), Cache::HotelStatus::Available); else - cache.Insert(hotelToResult.m_hotelId, Cache::HotelStatus::Unavailable); + cache.Insert(hotel.GetHotelId(), Cache::HotelStatus::Unavailable); + } +} + +template +void FillResults(HotelsMapping && hotelsMapping, std::vector const & hotelIds, + availability::Cache & cache, Inserter const & inserter) +{ + using availability::Cache; + + ASSERT(std::is_sorted(hotelIds.begin(), hotelIds.end()), ()); + + for (auto & hotel : hotelsMapping) + { + switch (hotel.GetStatus()) + { + case Cache::HotelStatus::Unavailable: continue; + case Cache::HotelStatus::Available: + { + inserter(std::move(hotel.GetMappedValue())); + continue; + } + case Cache::HotelStatus::NotReady: + { + auto hotelStatus = cache.Get(hotel.GetHotelId()); + + if (hotelStatus == Cache::HotelStatus::Available) + inserter(std::move(hotel.GetMappedValue())); + + continue; + } + case Cache::HotelStatus::Absent: + { + if (std::binary_search(hotelIds.cbegin(), hotelIds.cend(), hotel.GetHotelId())) + inserter(std::move(hotel.GetMappedValue())); + + continue; + } + } } } void FillResults(HotelToResults && hotelToResults, std::vector const & hotelIds, availability::Cache & cache, search::Results & results) { - using availability::Cache; - - ASSERT(std::is_sorted(hotelIds.begin(), hotelIds.end()), ()); - - for (auto & hotelToResult : hotelToResults) + auto const inserter = [&results](search::Result && result) { - switch (hotelToResult.m_cacheStatus) - { - case Cache::HotelStatus::Unavailable: continue; - case Cache::HotelStatus::Available: - { - results.AddResult(std::move(hotelToResult.m_result)); - continue; - } - case Cache::HotelStatus::NotReady: - { - auto hotelStatus = cache.Get(hotelToResult.m_hotelId); + results.AddResult(std::move(result)); + }; - if (hotelStatus == Cache::HotelStatus::Available) - results.AddResult(std::move(hotelToResult.m_result)); + FillResults(std::move(hotelToResults), hotelIds, cache, inserter); +} - continue; - } - case Cache::HotelStatus::Absent: - { - if (std::binary_search(hotelIds.cbegin(), hotelIds.cend(), hotelToResult.m_hotelId)) - results.AddResult(std::move(hotelToResult.m_result)); +void FillResults(HotelToFeatureIds && hotelToFeatureIds, std::vector const & hotelIds, + availability::Cache & cache, std::vector & results) +{ + auto const inserter = [&results](FeatureID result) + { + results.push_back(result); + }; - continue; - } - } - } + FillResults(std::move(hotelToFeatureIds), hotelIds, cache, inserter); } void PrepareData(DataSource const & dataSource, search::Results const & results, @@ -117,7 +167,7 @@ void PrepareData(DataSource const & dataSource, search::Results const & results, auto it = std::find_if(hotelToResults.begin(), hotelToResults.end(), [&featureId](HotelToResult const & item) { - return item.m_result.GetFeatureID() == featureId; + return item.GetMappedValue().GetFeatureID() == featureId; }); ASSERT(it != hotelToResults.cend(), ()); @@ -129,11 +179,53 @@ void PrepareData(DataSource const & dataSource, search::Results const & results, continue; } + if (!ftypes::IsBookingChecker::Instance()(*ft)) + continue; + auto const hotelId = ft->GetMetadata().Get(feature::Metadata::FMD_SPONSORED_ID); auto const status = cache.Get(hotelId); - it->m_hotelId = hotelId; - it->m_cacheStatus = status; + it->SetHotelId(hotelId); + it->SetStatus(status); + + if (status != availability::Cache::HotelStatus::Absent) + continue; + + cache.Reserve(hotelId); + p.m_hotelIds.push_back(std::move(hotelId)); + } +} + +void PrepareData(DataSource const & dataSource, std::vector const & featureIds, + HotelToFeatureIds & hotelToFeatures, availability::Cache & cache, + booking::AvailabilityParams & p) +{ + ASSERT(std::is_sorted(featureIds.begin(), featureIds.end()), ()); + + MwmSet::MwmId mwmId; + std::unique_ptr guard; + for (auto const featureId : featureIds) + { + if (mwmId != featureId.m_mwmId) + { + guard = std::make_unique(dataSource, featureId.m_mwmId); + mwmId = featureId.m_mwmId; + } + + auto ft = guard->GetFeatureByIndex(featureId.m_index); + if (!ft) + { + LOG(LERROR, ("Feature can't be loaded:", featureId)); + continue; + } + + if (!ftypes::IsBookingChecker::Instance()(*ft)) + continue; + + auto const hotelId = ft->GetMetadata().Get(feature::Metadata::FMD_SPONSORED_ID); + auto const status = cache.Get(hotelId); + + hotelToFeatures.emplace_back(hotelId, status, featureId); if (status != availability::Cache::HotelStatus::Absent) continue; @@ -153,46 +245,13 @@ AvailabilityFilter::AvailabilityFilter(Delegate const & d) : FilterBase(d) {} void AvailabilityFilter::ApplyFilter(search::Results const & results, ParamsInternal const & filterParams) { - ASSERT(filterParams.m_apiParams, ()); + ApplyFilterInternal(results, filterParams); +} - auto const & p = *filterParams.m_apiParams; - auto const & cb = filterParams.m_callback; - - UpdateParams(p); - - m_apiParams.m_hotelIds.clear(); - - HotelToResults hotelToResults; - PrepareData(GetDelegate().GetDataSource(), results, hotelToResults, *m_cache, m_apiParams); - - if (m_apiParams.m_hotelIds.empty()) - { - search::Results result; - FillResults(std::move(hotelToResults), {} /* hotelIds */, *m_cache, result); - cb(result); - - return; - } - - auto availabilityCache = m_cache; - - auto const apiCallback = - [cb, hotelToResults, availabilityCache](std::vector hotelIds) mutable - { - GetPlatform().RunTask(Platform::Thread::File, - [cb, hotelToResults, availabilityCache, hotelIds]() mutable - { - search::Results results; - std::sort(hotelIds.begin(), hotelIds.end()); - UpdateCache(hotelToResults, hotelIds, *availabilityCache); - FillResults(std::move(hotelToResults), hotelIds, *availabilityCache, - results); - cb(results); - }); - }; - - GetDelegate().GetApi().GetHotelAvailability(m_apiParams, apiCallback); - m_cache->RemoveOutdated(); +void AvailabilityFilter::ApplyFilter(std::vector const & featureIds, + ParamsRawInternal const & filterParams) +{ + ApplyFilterInternal(featureIds, filterParams); } void AvailabilityFilter::UpdateParams(ParamsBase const & apiParams) @@ -204,6 +263,51 @@ void AvailabilityFilter::UpdateParams(ParamsBase const & apiParams) m_cache = std::make_shared(); } +template +void AvailabilityFilter::ApplyFilterInternal(Source const & source, Parameters const & filterParams) +{ + ASSERT(filterParams.m_apiParams, ()); + + auto const & cb = filterParams.m_callback; + + UpdateParams(*filterParams.m_apiParams); + + m_apiParams.m_hotelIds.clear(); + + HotelsMapping hotelsToSourceValue; + PrepareData(GetDelegate().GetDataSource(), source, hotelsToSourceValue, *m_cache, m_apiParams); + + ASSERT_LESS_OR_EQUAL(m_apiParams.m_hotelIds.size(), kMaxCountInRequest, ()); + + if (m_apiParams.m_hotelIds.empty() || m_apiParams.m_hotelIds.size() > kMaxCountInRequest) + { + Source result; + FillResults(std::move(hotelsToSourceValue), {} /* hotelIds */, *m_cache, result); + cb(result); + + return; + } + + auto const apiCallback = + [cb, cache = m_cache, hotelToValue = std::move(hotelsToSourceValue)] + (std::vector hotelIds) mutable + { + GetPlatform().RunTask( + Platform::Thread::File, + [cb, cache, hotelToValue = std::move(hotelToValue), hotelIds = std::move(hotelIds)]() mutable + { + Source updatedResults; + std::sort(hotelIds.begin(), hotelIds.end()); + UpdateCache(hotelToValue, hotelIds, *cache); + FillResults(std::move(hotelToValue), hotelIds, *cache, updatedResults); + cb(updatedResults); + }); + }; + + GetDelegate().GetApi().GetHotelAvailability(m_apiParams, apiCallback); + m_cache->RemoveOutdated(); +} + void AvailabilityFilter::GetFeaturesFromCache(search::Results const & results, std::vector & sortedResults) { diff --git a/map/booking_availability_filter.hpp b/map/booking_availability_filter.hpp index dc986460ea..5b6c493c77 100644 --- a/map/booking_availability_filter.hpp +++ b/map/booking_availability_filter.hpp @@ -22,12 +22,17 @@ class AvailabilityFilter : public FilterBase public: explicit AvailabilityFilter(Delegate const & d); void ApplyFilter(search::Results const & results, ParamsInternal const & filterParams) override; + void ApplyFilter(std::vector const & featureIds, + ParamsRawInternal const & params) override; void GetFeaturesFromCache(search::Results const & results, std::vector & sortedResults) override; void UpdateParams(ParamsBase const & apiParams) override; private: + template + void ApplyFilterInternal(Source const & results, Parameters const & filterParams); + using CachePtr = std::shared_ptr; CachePtr m_cache = std::make_shared(); diff --git a/map/booking_filter.hpp b/map/booking_filter.hpp index a15e2cd387..d6f30e2e80 100644 --- a/map/booking_filter.hpp +++ b/map/booking_filter.hpp @@ -37,6 +37,8 @@ public: virtual void ApplyFilter(search::Results const & results, ParamsInternal const & filterParams) = 0; + virtual void ApplyFilter(std::vector const & featureIds, + ParamsRawInternal const & params) = 0; virtual void GetFeaturesFromCache(search::Results const & results, std::vector & sortedResults) = 0; virtual void UpdateParams(ParamsBase const & apiParams) = 0; diff --git a/map/booking_filter_params.hpp b/map/booking_filter_params.hpp index 8cb06b047b..c1e721b416 100644 --- a/map/booking_filter_params.hpp +++ b/map/booking_filter_params.hpp @@ -25,6 +25,7 @@ namespace filter using Results = platform::SafeCallback const & params, std::vector const & sortedFeatures)>; using ResultsUnsafe = std::function; +using ResultsRawUnsafe = std::function const & results)>; template struct ParamsImpl @@ -44,6 +45,7 @@ struct ParamsImpl using Params = ParamsImpl; using ParamsInternal = ParamsImpl; +using ParamsRawInternal = ParamsImpl; enum class Type { @@ -68,6 +70,7 @@ struct TaskImpl using Task = TaskImpl; using TaskInternal = TaskImpl; +using TaskRawInternal = TaskImpl; enum ApplicationMode { @@ -138,5 +141,6 @@ private: using Tasks = TasksImpl; using TasksInternal = std::vector; +using TasksRawInternal = std::vector; } // namespace filter } // namespace booking diff --git a/map/booking_filter_processor.cpp b/map/booking_filter_processor.cpp index 0a732ad8fb..eb1df1e4c4 100644 --- a/map/booking_filter_processor.cpp +++ b/map/booking_filter_processor.cpp @@ -3,6 +3,9 @@ #include "search/result.hpp" +#include +#include + namespace booking { namespace filter @@ -17,16 +20,13 @@ FilterProcessor::FilterProcessor(DataSource const & dataSource, booking::Api con void FilterProcessor::ApplyFilters(search::Results const & results, TasksInternal && tasks, ApplicationMode const mode) { - GetPlatform().RunTask(Platform::Thread::File, [this, results, tasks = std::move(tasks), mode]() mutable - { - CHECK(!tasks.empty(), ()); + ApplyFiltersInternal(results, std::move(tasks), mode); +} - switch (mode) - { - case Independent: ApplyIndependently(results, tasks); break; - case Consecutive: ApplyConsecutively(results, tasks); break; - } - }); +void FilterProcessor::ApplyFilters(std::vector && featureIds, TasksRawInternal && tasks, + ApplicationMode const mode) +{ + ApplyFiltersInternal(std::move(featureIds), std::move(tasks), mode); } void FilterProcessor::OnParamsUpdated(Type const type, @@ -66,33 +66,54 @@ Api const & FilterProcessor::GetApi() const return m_api; } -void FilterProcessor::ApplyConsecutively(search::Results const & results, TasksInternal & tasks) +template +void FilterProcessor::ApplyFiltersInternal(Source && source, TaskInternalType && tasks, + ApplicationMode const mode) +{ + GetPlatform().RunTask(Platform::Thread::File, [this, source = std::forward(source), + tasks = std::forward(tasks), + mode]() mutable + { + CHECK(!tasks.empty(), ()); + + switch (mode) + { + case Independent: ApplyIndependently(source, tasks); break; + case Consecutive: ApplyConsecutively(source, tasks); break; + } + }); +} + +template +void FilterProcessor::ApplyConsecutively(Source const & source, TaskInternalType & tasks) { for (size_t i = tasks.size() - 1; i > 0; --i) { auto const & cb = tasks[i - 1].m_filterParams.m_callback; tasks[i - 1].m_filterParams.m_callback = - [this, cb, nextTask = std::move(tasks[i])](search::Results const & results) mutable - { - cb(results); - // Run the next filter with obtained results from the previous one. - // Post different task on the file thread to increase granularity. - GetPlatform().RunTask(Platform::Thread::File, [this, results, nextTask = std::move(nextTask)]() - { - m_filters.at(nextTask.m_type)->ApplyFilter(results, nextTask.m_filterParams); - }); + [ this, cb, nextTask = std::move(tasks[i]) ](auto const & filterResults) mutable + { + cb(filterResults); + // Run the next filter with obtained results from the previous one. + // Post different task on the file thread to increase granularity. + // Note: FilterProcessor works on file thread, so all filters will + // be applied on the single thread. + GetPlatform().RunTask( + Platform::Thread::File, [this, filterResults, nextTask = std::move(nextTask)]() { + m_filters.at(nextTask.m_type)->ApplyFilter(filterResults, nextTask.m_filterParams); + }); }; } // Run the first filter. - m_filters.at(tasks.front().m_type)->ApplyFilter(results, tasks.front().m_filterParams); + m_filters.at(tasks.front().m_type)->ApplyFilter(source, tasks.front().m_filterParams); } -void FilterProcessor::ApplyIndependently(search::Results const & results, - TasksInternal const & tasks) +template +void FilterProcessor::ApplyIndependently(Source const & source, TaskInternalType const & tasks) { for (auto const & task : tasks) - m_filters.at(task.m_type)->ApplyFilter(results, task.m_filterParams); + m_filters.at(task.m_type)->ApplyFilter(source, task.m_filterParams); } } // namespace filter } // namespace booking diff --git a/map/booking_filter_processor.hpp b/map/booking_filter_processor.hpp index d99fe7b625..4ece328e64 100644 --- a/map/booking_filter_processor.hpp +++ b/map/booking_filter_processor.hpp @@ -51,6 +51,9 @@ public: void ApplyFilters(search::Results const & results, TasksInternal && tasks, ApplicationMode const mode); + void ApplyFilters(std::vector && featureIds, TasksRawInternal && tasks, + ApplicationMode const mode); + void OnParamsUpdated(Type const type, std::shared_ptr const & params); void GetFeaturesFromCache(Types const & types, search::Results const & results, @@ -61,8 +64,15 @@ public: Api const & GetApi() const override; private: - void ApplyConsecutively(search::Results const & results, TasksInternal & tasks); - void ApplyIndependently(search::Results const & results, TasksInternal const & tasks); + template + void ApplyFiltersInternal(Source && source, TaskInternalType && tasks, + ApplicationMode const mode); + + template + void ApplyConsecutively(Source const & source, TaskInternalType & tasks); + + template + void ApplyIndependently(Source const & source, TaskInternalType const & tasks); DataSource const & m_dataSource; Api const & m_api; diff --git a/map/map_tests/booking_filter_test.cpp b/map/map_tests/booking_filter_test.cpp index 1148ed840b..79cdd2051c 100644 --- a/map/map_tests/booking_filter_test.cpp +++ b/map/map_tests/booking_filter_test.cpp @@ -8,8 +8,6 @@ #include "partners_api/booking_api.hpp" -#include "platform/platform.hpp" - #include "search/result.hpp" #include "indexer/data_source.hpp" @@ -17,6 +15,9 @@ #include "storage/country_info_getter.hpp" +#include "platform/platform.hpp" + +#include #include #include @@ -55,6 +56,21 @@ private: booking::Api m_api; }; +class TestBookingHotel : public TestPOI +{ +public: + TestBookingHotel(m2::PointD const & center, std::string const & name, std::string const & lang) + : TestPOI(center, name, lang) + { + SetTypes({{"tourism", "hotel"}, {"sponsored", "booking"}}); + } + + TestBookingHotel(m2::PointD const & center, std::string const & name) + : TestBookingHotel(center, name, "en") + { + } +}; + void InsertResult(search::Result r, search::Results & dst) { dst.AddResult(std::move(r)); @@ -68,15 +84,15 @@ UNIT_CLASS_TEST(TestMwmEnvironment, BookingFilter_AvailabilitySmoke) BuildCountry("TestMwm", [&kHotelIds](TestMwmBuilder & builder) { - TestPOI hotel1(m2::PointD(1.0, 1.0), "hotel 1", "en"); + TestBookingHotel hotel1(m2::PointD(1.0, 1.0), "hotel 1"); hotel1.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[0]); builder.Add(hotel1); - TestPOI hotel2(m2::PointD(1.1, 1.1), "hotel 2", "en"); + TestBookingHotel hotel2(m2::PointD(1.1, 1.1), "hotel 2"); hotel2.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[1]); builder.Add(hotel2); - TestPOI hotel3(m2::PointD(0.9, 0.9), "hotel 3", "en"); + TestBookingHotel hotel3(m2::PointD(0.9, 0.9), "hotel 3"); hotel3.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[2]); builder.Add(hotel3); }); @@ -124,23 +140,23 @@ UNIT_CLASS_TEST(TestMwmEnvironment, BookingFilter_ProcessorSmoke) BuildCountry("TestMwm", [&kHotelIds](TestMwmBuilder & builder) { - TestPOI hotel1(m2::PointD(0.7, 0.7), "hotel 22", "en"); + TestBookingHotel hotel1(m2::PointD(0.7, 0.7), "hotel 22", "en"); hotel1.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[0]); builder.Add(hotel1); - TestPOI hotel2(m2::PointD(0.8, 0.8), "hotel 23", "en"); + TestBookingHotel hotel2(m2::PointD(0.8, 0.8), "hotel 23", "en"); hotel2.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[1]); builder.Add(hotel2); - TestPOI hotel3(m2::PointD(0.9, 0.9), "hotel 24", "en"); + TestBookingHotel hotel3(m2::PointD(0.9, 0.9), "hotel 24", "en"); hotel3.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[2]); builder.Add(hotel3); - TestPOI hotel4(m2::PointD(1.0, 1.0), "hotel 25", "en"); + TestBookingHotel hotel4(m2::PointD(1.0, 1.0), "hotel 25", "en"); hotel4.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[3]); builder.Add(hotel4); - TestPOI hotel5(m2::PointD(1.1, 1.1), "hotel 26", "en"); + TestBookingHotel hotel5(m2::PointD(1.1, 1.1), "hotel 26", "en"); hotel5.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[4]); builder.Add(hotel5); }); @@ -246,4 +262,74 @@ UNIT_CLASS_TEST(TestMwmEnvironment, BookingFilter_ProcessorSmoke) TEST_EQUAL(dealsResults[i].GetFeatureID(), expectedAvailableWithDeals[i].GetFeatureID(), ()); } } + +UNIT_CLASS_TEST(TestMwmEnvironment, BookingFilter_ApplyFilterOntoWithFeatureIds) +{ + AvailabilityFilter filter(*this); + + std::vector const kHotelIds = {"10623", "10624", "10625"}; + + BuildCountry("TestMwm", [&kHotelIds](TestMwmBuilder & builder) + { + TestBookingHotel hotel1(m2::PointD(1.0, 1.0), "hotel 1"); + + hotel1.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[0]); + builder.Add(hotel1); + + TestBookingHotel hotel2(m2::PointD(1.1, 1.1), "hotel 2"); + hotel2.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[1]); + builder.Add(hotel2); + + TestBookingHotel notExpectedHotel(m2::PointD(0.9, 0.9), "hotel 900"); + notExpectedHotel.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, "123456"); + builder.Add(notExpectedHotel); + + TestBookingHotel hotel3(m2::PointD(0.9, 0.9), "hotel 3"); + hotel3.GetMetadata().Set(feature::Metadata::FMD_SPONSORED_ID, kHotelIds[2]); + builder.Add(hotel3); + + TestPOI notExpectedFeature(m2::PointD(0.8, 0.8), "some feature", "en"); + builder.Add(notExpectedFeature); + }); + + m2::RectD const rect(m2::PointD(0.5, 0.5), m2::PointD(1.5, 1.5)); + search::Results results; + results.AddResult({"suggest for testing", "suggest for testing"}); + std::vector allFeatureIds; + std::vector expectedFeatureIds; + m_dataSource.ForEachInRect( + [&results, &allFeatureIds, &expectedFeatureIds, &kHotelIds](FeatureType & ft) { + search::Result::Metadata metadata; + metadata.m_isSponsoredHotel = true; + search::Result result(ft.GetID(), ft.GetCenter(), "", "", 0, metadata); + auto copy = result; + results.AddResult(std::move(result)); + + allFeatureIds.push_back(ft.GetID()); + + auto hotelId = ft.GetMetadata().Get(feature::Metadata::FMD_SPONSORED_ID); + if (kHotelIds.cend() != std::find(kHotelIds.cbegin(), kHotelIds.cend(), hotelId)) + expectedFeatureIds.push_back(ft.GetID()); + }, + rect, scales::GetUpperScale()); + + std::sort(expectedFeatureIds.begin(), expectedFeatureIds.end()); + std::sort(allFeatureIds.begin(), allFeatureIds.end()); + + ParamsRawInternal filterParams; + std::vector filteredResults; + filterParams.m_apiParams = make_shared(); + filterParams.m_callback = [&filteredResults](std::vector const & results) { + filteredResults = results; + testing::Notify(); + }; + + filter.ApplyFilter(allFeatureIds, filterParams); + + testing::Wait(); + + TEST_NOT_EQUAL(filteredResults.size(), 0, ()); + TEST_EQUAL(filteredResults.size(), expectedFeatureIds.size(), ()); + TEST_EQUAL(filteredResults, expectedFeatureIds, ()); +} } // namespace diff --git a/partners_api/booking_api.hpp b/partners_api/booking_api.hpp index ca23e3a658..f0c88626dd 100644 --- a/partners_api/booking_api.hpp +++ b/partners_api/booking_api.hpp @@ -117,6 +117,7 @@ public: // Booking Api v2 methods: static bool HotelAvailability(AvailabilityParams const & params, std::string & result); static bool BlockAvailability(BlockParams const & params, string & result); + static size_t constexpr GetMaxHotelsInAvailabilityRequest() { return 300; }; }; using BlockAvailabilityCallback =