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
diff options
context:
space:
mode:
-rw-r--r--base/string_utils.cpp7
-rw-r--r--base/string_utils.hpp2
-rw-r--r--generator/feature_builder.cpp5
-rw-r--r--generator/feature_builder.hpp3
-rw-r--r--generator/generator_tests_support/test_feature.cpp5
-rw-r--r--indexer/ftypes_matcher.hpp18
-rw-r--r--map/framework.cpp3
-rw-r--r--search/categories_cache.cpp20
-rw-r--r--search/categories_cache.hpp47
-rw-r--r--search/everywhere_search_params.hpp2
-rw-r--r--search/geocoder.cpp47
-rw-r--r--search/geocoder.hpp15
-rw-r--r--search/geocoder_context.hpp3
-rw-r--r--search/hotels_filter.cpp105
-rw-r--r--search/hotels_filter.hpp227
-rw-r--r--search/processor.cpp3
-rw-r--r--search/processor.hpp5
-rw-r--r--search/search.pro2
-rw-r--r--search/search_integration_tests/processor_test.cpp90
-rw-r--r--search/search_integration_tests/smoke_test.cpp5
-rw-r--r--search/search_params.hpp3
-rw-r--r--search/viewport_search_params.hpp4
22 files changed, 540 insertions, 81 deletions
diff --git a/base/string_utils.cpp b/base/string_utils.cpp
index 6d017e2405..67eed2cb12 100644
--- a/base/string_utils.cpp
+++ b/base/string_utils.cpp
@@ -96,6 +96,13 @@ bool to_int64(char const * s, int64_t & i)
return *stop == 0 && s != stop;
}
+bool to_float(char const * s, float & f)
+{
+ char * stop;
+ f = strtof(s, &stop);
+ return *stop == 0 && s != stop && isfinite(f);
+}
+
bool to_double(char const * s, double & d)
{
char * stop;
diff --git a/base/string_utils.hpp b/base/string_utils.hpp
index 179a5d560b..03c11fb61f 100644
--- a/base/string_utils.hpp
+++ b/base/string_utils.hpp
@@ -341,6 +341,7 @@ bool to_int(char const * s, int & i, int base = 10);
bool to_uint(char const * s, unsigned int & i, int base = 10);
bool to_uint64(char const * s, uint64_t & i);
bool to_int64(char const * s, int64_t & i);
+bool to_float(char const * s, float & f);
bool to_double(char const * s, double & d);
inline bool is_number(string const & s)
@@ -356,6 +357,7 @@ inline bool to_uint(string const & s, unsigned int & i, int base = 10)
}
inline bool to_uint64(string const & s, uint64_t & i) { return to_uint64(s.c_str(), i); }
inline bool to_int64(string const & s, int64_t & i) { return to_int64(s.c_str(), i); }
+inline bool to_float(string const & s, float & f) { return to_float(s.c_str(), f); }
inline bool to_double(string const & s, double & d) { return to_double(s.c_str(), d); }
//@}
diff --git a/generator/feature_builder.cpp b/generator/feature_builder.cpp
index 5423274bbf..ed66b68c05 100644
--- a/generator/feature_builder.cpp
+++ b/generator/feature_builder.cpp
@@ -79,11 +79,6 @@ void FeatureBuilder1::SetRank(uint8_t rank)
m_params.rank = rank;
}
-void FeatureBuilder1::SetTestId(uint64_t id)
-{
- m_params.GetMetadata().Set(Metadata::FMD_TEST_ID, strings::to_string(id));
-}
-
void FeatureBuilder1::AddHouseNumber(string const & houseNumber)
{
m_params.AddHouseNumber(houseNumber);
diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp
index ff27806cf0..e766211121 100644
--- a/generator/feature_builder.hpp
+++ b/generator/feature_builder.hpp
@@ -33,8 +33,6 @@ public:
void SetRank(uint8_t rank);
- void SetTestId(uint64_t id);
-
void AddHouseNumber(string const & houseNumber);
void AddStreet(string const & streetName);
@@ -61,6 +59,7 @@ public:
inline feature::Metadata const & GetMetadata() const { return m_params.GetMetadata(); }
+ inline feature::Metadata & GetMetadataForTesting() { return m_params.GetMetadata(); }
inline TGeometry const & GetGeometry() const { return m_polygons; }
inline TPointSeq const & GetOuterGeometry() const { return m_polygons.front(); }
inline feature::EGeomType GetGeomType() const { return m_params.GetGeomType(); }
diff --git a/generator/generator_tests_support/test_feature.cpp b/generator/generator_tests_support/test_feature.cpp
index bf645e3735..e3bc0ac932 100644
--- a/generator/generator_tests_support/test_feature.cpp
+++ b/generator/generator_tests_support/test_feature.cpp
@@ -15,6 +15,7 @@
#include "coding/multilang_utf8_string.hpp"
#include "base/assert.hpp"
+#include "base/string_utils.hpp"
#include "std/atomic.hpp"
#include "std/sstream.hpp"
@@ -53,7 +54,9 @@ bool TestFeature::Matches(FeatureType const & feature) const
void TestFeature::Serialize(FeatureBuilder1 & fb) const
{
- fb.SetTestId(m_id);
+ auto & metadata = fb.GetMetadataForTesting();
+ metadata.Set(feature::Metadata::FMD_TEST_ID, strings::to_string(m_id));
+
if (m_hasCenter)
fb.SetCenter(m_center);
if (!m_name.empty())
diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp
index 2e8fd6bc3e..17d1e77665 100644
--- a/indexer/ftypes_matcher.hpp
+++ b/indexer/ftypes_matcher.hpp
@@ -32,6 +32,12 @@ public:
bool operator() (vector<uint32_t> const & types) const;
static uint32_t PrepareToMatch(uint32_t type, uint8_t level);
+
+ template <typename TFn>
+ void ForEachType(TFn && fn) const
+ {
+ for_each(m_types.cbegin(), m_types.cend(), forward<TFn>(fn));
+ }
};
class IsPeakChecker : public BaseChecker
@@ -73,12 +79,6 @@ class IsStreetChecker : public BaseChecker
{
IsStreetChecker();
public:
- template <typename TFn>
- void ForEachType(TFn && fn) const
- {
- for_each(m_types.cbegin(), m_types.cend(), forward<TFn>(fn));
- }
-
static IsStreetChecker const & Instance();
};
@@ -94,12 +94,6 @@ class IsVillageChecker : public BaseChecker
IsVillageChecker();
public:
- template <typename TFn>
- void ForEachType(TFn && fn) const
- {
- for_each(m_types.cbegin(), m_types.cend(), forward<TFn>(fn));
- }
-
static IsVillageChecker const & Instance();
};
diff --git a/map/framework.cpp b/map/framework.cpp
index 3bd858f5d7..9ac57322f7 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -1124,6 +1124,8 @@ bool Framework::SearchEverywhere(search::EverywhereSearchParams const & params)
p.m_mode = search::Mode::Everywhere;
p.m_forceSearch = true;
p.m_suggestsEnabled = true;
+ p.m_hotelsFilter = params.m_hotelsFilter;
+
p.m_onResults = [params](search::Results const & results) {
if (params.m_onResults)
GetPlatform().RunOnGuiThread([params, results]() { params.m_onResults(results); });
@@ -1141,6 +1143,7 @@ bool Framework::SearchInViewport(search::ViewportSearchParams const & params)
p.m_mode = search::Mode::Viewport;
p.m_forceSearch = false;
p.m_suggestsEnabled = false;
+ p.m_hotelsFilter = params.m_hotelsFilter;
p.m_onStarted = [params]() {
if (params.m_onStarted)
diff --git a/search/categories_cache.cpp b/search/categories_cache.cpp
index 214edb360d..233b21b84f 100644
--- a/search/categories_cache.cpp
+++ b/search/categories_cache.cpp
@@ -32,12 +32,6 @@ void UniteCBVs(vector<CBV> & cbvs)
} // namespace
// CategoriesCache ---------------------------------------------------------------------------------
-CategoriesCache::CategoriesCache(CategoriesSet const & categories,
- my::Cancellable const & cancellable)
- : m_categories(categories), m_cancellable(cancellable)
-{
-}
-
CBV CategoriesCache::Get(MwmContext const & context)
{
if (!context.m_handle.IsAlive() || !context.m_value.HasSearchIndex())
@@ -85,17 +79,19 @@ CBV CategoriesCache::Load(MwmContext const & context)
// StreetsCache ------------------------------------------------------------------------------------
StreetsCache::StreetsCache(my::Cancellable const & cancellable)
- : m_cache(m_categories, cancellable)
+ : CategoriesCache(ftypes::IsStreetChecker::Instance(), cancellable)
{
- ftypes::IsStreetChecker::Instance().ForEachType(
- [this](uint32_t type) { m_categories.Add(type); });
}
// VillagesCache -----------------------------------------------------------------------------------
VillagesCache::VillagesCache(my::Cancellable const & cancellable)
- : m_cache(m_categories, cancellable)
+ : CategoriesCache(ftypes::IsVillageChecker::Instance(), cancellable)
+{
+}
+
+// HotelsCache -------------------------------------------------------------------------------------
+HotelsCache::HotelsCache(my::Cancellable const & cancellable)
+ : CategoriesCache(ftypes::IsHotelChecker::Instance(), cancellable)
{
- ftypes::IsVillageChecker::Instance().ForEachType(
- [this](uint32_t type) { m_categories.Add(type); });
}
} // namespace search
diff --git a/search/categories_cache.hpp b/search/categories_cache.hpp
index cf2da9297f..356ee2772d 100644
--- a/search/categories_cache.hpp
+++ b/search/categories_cache.hpp
@@ -16,46 +16,47 @@ class MwmContext;
class CategoriesCache
{
public:
- CategoriesCache(CategoriesSet const & categories, my::Cancellable const & cancellable);
+ template <typename TypesSource>
+ CategoriesCache(TypesSource const & source, my::Cancellable const & cancellable)
+ : m_cancellable(cancellable)
+ {
+ source.ForEachType([this](uint32_t type) { m_categories.Add(type); });
+ }
+
+ virtual ~CategoriesCache() = default;
CBV Get(MwmContext const & context);
+
+ inline bool HasCategory(strings::UniString const & category) const
+ {
+ return m_categories.HasKey(category);
+ }
+
inline void Clear() { m_cache.clear(); }
private:
CBV Load(MwmContext const & context);
- CategoriesSet const & m_categories;
+ CategoriesSet m_categories;
my::Cancellable const & m_cancellable;
map<MwmSet::MwmId, CBV> m_cache;
};
-class StreetsCache
+class StreetsCache : public CategoriesCache
{
- public:
+public:
StreetsCache(my::Cancellable const & cancellable);
-
- inline CBV Get(MwmContext const & context) { return m_cache.Get(context); }
- inline void Clear() { m_cache.Clear(); }
- inline bool HasCategory(strings::UniString const & category) const
- {
- return m_categories.HasKey(category);
- }
-
- private:
- CategoriesSet m_categories;
- CategoriesCache m_cache;
};
-class VillagesCache
+class VillagesCache : public CategoriesCache
{
- public:
+public:
VillagesCache(my::Cancellable const & cancellable);
+};
- inline CBV Get(MwmContext const & context) { return m_cache.Get(context); }
- inline void Clear() { m_cache.Clear(); }
-
-private:
- CategoriesSet m_categories;
- CategoriesCache m_cache;
+class HotelsCache : public CategoriesCache
+{
+public:
+ HotelsCache(my::Cancellable const & cancellable);
};
} // namespace search
diff --git a/search/everywhere_search_params.hpp b/search/everywhere_search_params.hpp
index f3df639644..e6d668f7ff 100644
--- a/search/everywhere_search_params.hpp
+++ b/search/everywhere_search_params.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "search/hotels_filter.hpp"
#include "search/search_params.hpp"
#include "std/string.hpp"
@@ -10,6 +11,7 @@ struct EverywhereSearchParams
{
string m_query;
string m_inputLocale;
+ shared_ptr<hotels_filter::Rule> m_hotelsFilter;
SearchParams::TOnResults m_onResults;
};
diff --git a/search/geocoder.cpp b/search/geocoder.cpp
index 5345d18933..fb1562b1b5 100644
--- a/search/geocoder.cpp
+++ b/search/geocoder.cpp
@@ -344,6 +344,8 @@ Geocoder::Geocoder(Index const & index, storage::CountryInfoGetter const & infoG
, m_infoGetter(infoGetter)
, m_streetsCache(cancellable)
, m_villagesCache(villagesCache)
+ , m_hotelsCache(cancellable)
+ , m_hotelsFilter(m_hotelsCache)
, m_cancellable(cancellable)
, m_model(SearchModel::Instance())
, m_pivotRectsCache(kPivotRectsCacheSize, m_cancellable, Processor::kMaxViewportRadiusM)
@@ -558,7 +560,8 @@ void Geocoder::ClearCaches()
m_matchersCache.clear();
m_streetsCache.Clear();
- m_villagesCache.Clear();
+ m_hotelsCache.Clear();
+ m_hotelsFilter.ClearCaches();
m_postcodes.Clear();
}
@@ -595,6 +598,7 @@ void Geocoder::InitBaseContext(BaseContext & ctx)
PrepareRetrievalParams(i, i + 1);
ctx.m_features[i] = RetrieveAddressFeatures(*m_context, m_cancellable, m_retrievalParams);
}
+ ctx.m_hotelsFilter = m_hotelsFilter.MakeScopedFilter(*m_context, m_params.m_hotelsFilter);
}
void Geocoder::InitLayer(SearchModel::SearchType type, size_t startToken, size_t endToken,
@@ -854,7 +858,7 @@ void Geocoder::MatchRegions(BaseContext & ctx, RegionType type)
if (ctx.AllTokensUsed())
{
// Region matches to search query, we need to emit it as is.
- EmitResult(region, startToken, endToken);
+ EmitResult(ctx, region, startToken, endToken);
continue;
}
@@ -897,7 +901,7 @@ void Geocoder::MatchCities(BaseContext & ctx)
if (ctx.AllTokensUsed())
{
// City matches to search query, we need to emit it as is.
- EmitResult(city, startToken, endToken);
+ EmitResult(ctx, city, startToken, endToken);
continue;
}
@@ -1027,7 +1031,7 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken)
// All tokens were consumed, find paths through layers, emit
// features.
if (m_postcodes.m_features.IsEmpty())
- return FindPaths();
+ return FindPaths(ctx);
// When there are no layers but user entered a postcode, we have
// to emit all features matching to the postcode.
@@ -1041,15 +1045,15 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken)
SearchModel::SearchType searchType;
if (GetSearchTypeInGeocoding(ctx, id, searchType))
{
- EmitResult(m_context->GetId(), id, searchType, m_postcodes.m_startToken,
- m_postcodes.m_endToken);
+ EmitResult(ctx, m_context->GetId(), id, searchType,
+ m_postcodes.m_startToken, m_postcodes.m_endToken);
}
});
return;
}
if (!(m_layers.size() == 1 && m_layers[0].m_type == SearchModel::SEARCH_TYPE_STREET))
- return FindPaths();
+ return FindPaths(ctx);
// If there're only one street layer but user also entered a
// postcode, we need to emit all features matching to postcode on
@@ -1063,7 +1067,7 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken)
{
if (!m_postcodes.m_features.HasBit(id))
continue;
- EmitResult(m_context->GetId(), id, SearchModel::SEARCH_TYPE_STREET,
+ EmitResult(ctx, m_context->GetId(), id, SearchModel::SEARCH_TYPE_STREET,
m_layers.back().m_startToken, m_layers.back().m_endToken);
}
}
@@ -1080,7 +1084,7 @@ void Geocoder::MatchPOIsAndBuildings(BaseContext & ctx, size_t curToken)
vector<uint32_t> features;
m_postcodes.m_features.ForEach(MakeBackInsertFunctor(features));
layer.m_sortedFeatures = &features;
- return FindPaths();
+ return FindPaths(ctx);
}
m_layers.emplace_back();
@@ -1244,7 +1248,7 @@ bool Geocoder::IsLayerSequenceSane() const
return true;
}
-void Geocoder::FindPaths()
+void Geocoder::FindPaths(BaseContext const & ctx)
{
if (m_layers.empty())
return;
@@ -1263,21 +1267,24 @@ void Geocoder::FindPaths()
else
m_matcher->SetPostcodes(nullptr);
m_finder.ForEachReachableVertex(
- *m_matcher, sortedLayers, [this, &innermostLayer](IntersectionResult const & result)
+ *m_matcher, sortedLayers, [this, &ctx, &innermostLayer](IntersectionResult const & result)
{
ASSERT(result.IsValid(), ());
// TODO(@y, @m, @vng): use rest fields of IntersectionResult for
// better scoring.
- EmitResult(m_context->GetId(), result.InnermostResult(), innermostLayer.m_type,
+ EmitResult(ctx, m_context->GetId(), result.InnermostResult(), innermostLayer.m_type,
innermostLayer.m_startToken, innermostLayer.m_endToken);
});
}
-void Geocoder::EmitResult(MwmSet::MwmId const & mwmId, uint32_t ftId, SearchModel::SearchType type,
- size_t startToken, size_t endToken)
+void Geocoder::EmitResult(BaseContext const & ctx, MwmSet::MwmId const & mwmId, uint32_t ftId,
+ SearchModel::SearchType type, size_t startToken, size_t endToken)
{
FeatureID id(mwmId, ftId);
+ if (ctx.m_hotelsFilter && !ctx.m_hotelsFilter->Matches(id))
+ return;
+
// Distance and rank will be filled at the end, for all results at once.
//
// TODO (@y, @m): need to skip zero rank features that are too
@@ -1291,7 +1298,8 @@ void Geocoder::EmitResult(MwmSet::MwmId const & mwmId, uint32_t ftId, SearchMode
m_preRanker.Emplace(id, info);
}
-void Geocoder::EmitResult(Region const & region, size_t startToken, size_t endToken)
+void Geocoder::EmitResult(BaseContext const & ctx, Region const & region, size_t startToken,
+ size_t endToken)
{
SearchModel::SearchType type;
switch (region.m_type)
@@ -1300,12 +1308,13 @@ void Geocoder::EmitResult(Region const & region, size_t startToken, size_t endTo
case REGION_TYPE_COUNTRY: type = SearchModel::SEARCH_TYPE_COUNTRY; break;
case REGION_TYPE_COUNT: type = SearchModel::SEARCH_TYPE_COUNT; break;
}
- EmitResult(m_worldId, region.m_featureId, type, startToken, endToken);
+ EmitResult(ctx, m_worldId, region.m_featureId, type, startToken, endToken);
}
-void Geocoder::EmitResult(City const & city, size_t startToken, size_t endToken)
+void Geocoder::EmitResult(BaseContext const & ctx, City const & city, size_t startToken,
+ size_t endToken)
{
- EmitResult(city.m_countryId, city.m_featureId, city.m_type, startToken, endToken);
+ EmitResult(ctx, city.m_countryId, city.m_featureId, city.m_type, startToken, endToken);
}
void Geocoder::MatchUnclassified(BaseContext & ctx, size_t curToken)
@@ -1341,7 +1350,7 @@ void Geocoder::MatchUnclassified(BaseContext & ctx, size_t curToken)
if (!GetSearchTypeInGeocoding(ctx, featureId, searchType))
return;
if (searchType == SearchModel::SEARCH_TYPE_UNCLASSIFIED)
- EmitResult(m_context->GetId(), featureId, searchType, startToken, curToken);
+ EmitResult(ctx, m_context->GetId(), featureId, searchType, startToken, curToken);
};
allFeatures.ForEach(emitUnclassified);
}
diff --git a/search/geocoder.hpp b/search/geocoder.hpp
index d6b30a1e1d..ed178ee6e2 100644
--- a/search/geocoder.hpp
+++ b/search/geocoder.hpp
@@ -7,6 +7,7 @@
#include "search/features_layer_path_finder.hpp"
#include "search/geocoder_context.hpp"
#include "search/geometry_cache.hpp"
+#include "search/hotels_filter.hpp"
#include "search/mode.hpp"
#include "search/model.hpp"
#include "search/mwm_context.hpp"
@@ -78,6 +79,7 @@ public:
Mode m_mode;
m2::RectD m_pivot;
+ shared_ptr<hotels_filter::Rule> m_hotelsFilter;
};
enum RegionType
@@ -246,13 +248,14 @@ private:
// Finds all paths through layers and emits reachable features from
// the lowest layer.
- void FindPaths();
+ void FindPaths(BaseContext const & ctx);
// Forms result and feeds it to |m_preRanker|.
- void EmitResult(MwmSet::MwmId const & mwmId, uint32_t ftId, SearchModel::SearchType type,
- size_t startToken, size_t endToken);
- void EmitResult(Region const & region, size_t startToken, size_t endToken);
- void EmitResult(City const & city, size_t startToken, size_t endToken);
+ void EmitResult(BaseContext const & ctx, MwmSet::MwmId const & mwmId, uint32_t ftId,
+ SearchModel::SearchType type, size_t startToken, size_t endToken);
+ void EmitResult(BaseContext const & ctx, Region const & region, size_t startToken,
+ size_t endToken);
+ void EmitResult(BaseContext const & ctx, City const & city, size_t startToken, size_t endToken);
// Tries to match unclassified objects from lower layers, like
// parks, forests, lakes, rivers, etc. This method finds all
@@ -276,6 +279,8 @@ private:
StreetsCache m_streetsCache;
VillagesCache & m_villagesCache;
+ HotelsCache m_hotelsCache;
+ hotels_filter::HotelsFilter m_hotelsFilter;
my::Cancellable const & m_cancellable;
diff --git a/search/geocoder_context.hpp b/search/geocoder_context.hpp
index 63b7d4696f..216c323987 100644
--- a/search/geocoder_context.hpp
+++ b/search/geocoder_context.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "search/cbv.hpp"
+#include "search/hotels_filter.hpp"
#include "std/unique_ptr.hpp"
#include "std/vector.hpp"
@@ -37,5 +38,7 @@ struct BaseContext
// Number of tokens in the query.
size_t m_numTokens = 0;
+
+ unique_ptr<hotels_filter::HotelsFilter::ScopedFilter> m_hotelsFilter;
};
} // namespace search
diff --git a/search/hotels_filter.cpp b/search/hotels_filter.cpp
new file mode 100644
index 0000000000..10f335a39f
--- /dev/null
+++ b/search/hotels_filter.cpp
@@ -0,0 +1,105 @@
+#include "search/hotels_filter.hpp"
+
+#include "indexer/feature.hpp"
+#include "indexer/feature_meta.hpp"
+
+#include "base/assert.hpp"
+
+#include "std/algorithm.hpp"
+
+namespace search
+{
+namespace hotels_filter
+{
+// static
+typename Rating::Value const Rating::kDefault = 0;
+
+// static
+typename PriceRate::Value const PriceRate::kDefault = 0;
+
+// Description -------------------------------------------------------------------------------------
+void Description::FromFeature(FeatureType & ft)
+{
+ m_rating = Rating::kDefault;
+ m_priceRate = PriceRate::kDefault;
+
+ auto const & metadata = ft.GetMetadata();
+
+ if (metadata.Has(feature::Metadata::FMD_RATING))
+ {
+ string const rating = metadata.Get(feature::Metadata::FMD_RATING);
+ float r;
+ if (strings::to_float(rating, r))
+ m_rating = r;
+ }
+
+ if (metadata.Has(feature::Metadata::FMD_PRICE_RATE))
+ {
+ string const priceRate = metadata.Get(feature::Metadata::FMD_PRICE_RATE);
+ int pr;
+ if (strings::to_int(priceRate, pr))
+ m_priceRate = pr;
+ }
+}
+
+// HotelsFilter::ScopedFilter ----------------------------------------------------------------------
+HotelsFilter::ScopedFilter::ScopedFilter(MwmSet::MwmId const & mwmId,
+ Descriptions const & descriptions, shared_ptr<Rule> rule)
+ : m_mwmId(mwmId), m_descriptions(descriptions), m_rule(rule)
+{
+ CHECK(m_rule.get(), ());
+}
+
+bool HotelsFilter::ScopedFilter::Matches(FeatureID const & fid) const
+{
+ if (fid.m_mwmId != m_mwmId)
+ return false;
+
+ auto it =
+ lower_bound(m_descriptions.begin(), m_descriptions.end(),
+ make_pair(fid.m_index, Description{}),
+ [](pair<uint32_t, Description> const & lhs,
+ pair<uint32_t, Description> const & rhs) { return lhs.first < rhs.first; });
+ if (it == m_descriptions.end() || it->first != fid.m_index)
+ return false;
+
+ return m_rule->Matches(it->second);
+}
+
+// HotelsFilter ------------------------------------------------------------------------------------
+HotelsFilter::HotelsFilter(HotelsCache & hotels): m_hotels(hotels) {}
+
+unique_ptr<HotelsFilter::ScopedFilter> HotelsFilter::MakeScopedFilter(MwmContext const & context,
+ shared_ptr<Rule> rule)
+{
+ if (!rule)
+ return {};
+ return make_unique<ScopedFilter>(context.GetId(), GetDescriptions(context), rule);
+}
+
+void HotelsFilter::ClearCaches()
+{
+ m_descriptions.clear();
+}
+
+HotelsFilter::Descriptions const & HotelsFilter::GetDescriptions(MwmContext const & context)
+{
+ auto const & mwmId = context.GetId();
+ auto const it = m_descriptions.find(mwmId);
+ if (it != m_descriptions.end())
+ return it->second;
+
+ auto const hotels = m_hotels.Get(context);
+ auto & descriptions = m_descriptions[mwmId];
+ hotels.ForEach([&descriptions, &context](uint32_t id) {
+ FeatureType ft;
+
+ Description description;
+ if (context.GetFeature(id, ft))
+ description.FromFeature(ft);
+ descriptions.emplace_back(id, description);
+ });
+ return descriptions;
+}
+} // namespace hotels_filter
+} // namespace search
diff --git a/search/hotels_filter.hpp b/search/hotels_filter.hpp
new file mode 100644
index 0000000000..6ea1e9a12a
--- /dev/null
+++ b/search/hotels_filter.hpp
@@ -0,0 +1,227 @@
+#pragma once
+
+#include "search/categories_cache.hpp"
+#include "search/cbv.hpp"
+#include "search/mwm_context.hpp"
+
+#include "indexer/mwm_set.hpp"
+
+#include "std/map.hpp"
+#include "std/shared_ptr.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/utility.hpp"
+#include "std/vector.hpp"
+
+class FeatureType;
+
+namespace search
+{
+namespace hotels_filter
+{
+struct Rating
+{
+ using Value = float;
+
+ static Value const kDefault;
+
+ static bool Lt(Value lhs, Value rhs) { return lhs + 0.05 < rhs; }
+ static bool Gt(Value lhs, Value rhs) { return lhs > rhs + 0.05; }
+ static bool Eq(Value lhs, Value rhs) { return !Lt(lhs, rhs) && !Gt(lhs, rhs); }
+
+ template <typename Description>
+ static Value Select(Description const & d)
+ {
+ return d.m_rating;
+ }
+};
+
+struct PriceRate
+{
+ using Value = int;
+
+ static Value const kDefault;
+
+ static bool Lt(Value lhs, Value rhs) { return lhs < rhs; }
+ static bool Gt(Value lhs, Value rhs) { return lhs > rhs; }
+ static bool Eq(Value lhs, Value rhs) { return lhs == rhs; }
+
+ template <typename Description>
+ static Value Select(Description const & d)
+ {
+ return d.m_priceRate;
+ }
+};
+
+struct Description
+{
+ void FromFeature(FeatureType & ft);
+
+ Rating::Value m_rating = Rating::kDefault;
+ PriceRate::Value m_priceRate = PriceRate::kDefault;
+};
+
+struct Rule
+{
+ virtual ~Rule() = default;
+ virtual bool Matches(Description const & d) const = 0;
+};
+
+template <typename Field>
+struct EqRule : public Rule
+{
+ using Value = typename Field::Value;
+
+ EqRule(Value value) : m_value(value) {}
+
+ // Rule overrides:
+ bool Matches(Description const & d) const override
+ {
+ return Field::Eq(Field::Select(d), m_value);
+ }
+
+ Value const m_value;
+};
+
+template <typename Field>
+struct LtRule : public Rule
+{
+ using Value = typename Field::Value;
+
+ LtRule(Value value) : m_value(value) {}
+
+ // Rule overrides:
+ bool Matches(Description const & d) const override
+ {
+ return Field::Lt(Field::Select(d), m_value);
+ }
+
+ Value const m_value;
+};
+
+template <typename Field>
+struct GtRule : public Rule
+{
+ using Value = typename Field::Value;
+
+ GtRule(Value value) : m_value(value) {}
+
+ // Rule overrides:
+ bool Matches(Description const & d) const override
+ {
+ return Field::Gt(Field::Select(d), m_value);
+ }
+
+ Value const m_value;
+};
+
+struct AndRule : public Rule
+{
+ AndRule(shared_ptr<Rule> lhs, shared_ptr<Rule> rhs) : m_lhs(move(lhs)), m_rhs(move(rhs)) {}
+
+ // Rule overrides:
+ bool Matches(Description const & d) const override
+ {
+ bool matches = true;
+ if (m_lhs)
+ matches = matches && m_lhs->Matches(d);
+ if (m_rhs)
+ matches = matches && m_rhs->Matches(d);
+ return matches;
+ }
+
+ shared_ptr<Rule> m_lhs;
+ shared_ptr<Rule> m_rhs;
+};
+
+struct OrRule : public Rule
+{
+ OrRule(shared_ptr<Rule> lhs, shared_ptr<Rule> rhs) : m_lhs(move(lhs)), m_rhs(move(rhs)) {}
+
+ // Rule overrides:
+ bool Matches(Description const & d) const override
+ {
+ bool matches = false;
+ if (m_lhs)
+ matches = matches || m_lhs->Matches(d);
+ if (m_rhs)
+ matches = matches || m_rhs->Matches(d);
+ return matches;
+ }
+
+ shared_ptr<Rule> m_lhs;
+ shared_ptr<Rule> m_rhs;
+};
+
+template <typename Field>
+shared_ptr<Rule> Eq(typename Field::Value value)
+{
+ return make_shared<EqRule<Field>>(value);
+}
+
+template <typename Field>
+shared_ptr<Rule> Lt(typename Field::Value value)
+{
+ return make_shared<LtRule<Field>>(value);
+}
+
+template <typename Field>
+inline shared_ptr<Rule> Gt(typename Field::Value value)
+{
+ return make_shared<GtRule<Field>>(value);
+}
+
+inline shared_ptr<Rule> And(shared_ptr<Rule> lhs, shared_ptr<Rule> rhs)
+{
+ return make_shared<AndRule>(lhs, rhs);
+}
+
+inline shared_ptr<Rule> Or(shared_ptr<Rule> lhs, shared_ptr<Rule> rhs)
+{
+ return make_shared<OrRule>(lhs, rhs);
+}
+
+template <typename Field>
+shared_ptr<Rule> Le(typename Field::Value value)
+{
+ return Or(Lt<Field>(value), Eq<Field>(value));
+}
+
+template <typename Field>
+shared_ptr<Rule> Ge(typename Field::Value value)
+{
+ return Or(Gt<Field>(value), Eq<Field>(value));
+}
+
+class HotelsFilter
+{
+public:
+ using Descriptions = vector<pair<uint32_t, Description>>;
+
+ class ScopedFilter
+ {
+ public:
+ ScopedFilter(MwmSet::MwmId const & mwmId, Descriptions const & descriptions,
+ shared_ptr<Rule> rule);
+
+ bool Matches(FeatureID const & fid) const;
+
+ private:
+ MwmSet::MwmId const m_mwmId;
+ Descriptions const & m_descriptions;
+ shared_ptr<Rule> const m_rule;
+ };
+
+ HotelsFilter(HotelsCache & hotels);
+
+ unique_ptr<ScopedFilter> MakeScopedFilter(MwmContext const & context, shared_ptr<Rule> rule);
+
+ void ClearCaches();
+
+private:
+ Descriptions const & GetDescriptions(MwmContext const & context);
+
+ HotelsCache & m_hotels;
+ map<MwmSet::MwmId, Descriptions> m_descriptions;
+};
+} // namespace hotels_filter
+} // namespace search
diff --git a/search/processor.cpp b/search/processor.cpp
index e076df2e96..659bd53f19 100644
--- a/search/processor.cpp
+++ b/search/processor.cpp
@@ -405,6 +405,7 @@ void Processor::Search(SearchParams const & params, m2::RectD const & viewport)
SetMinDistanceOnMapBetweenResults(params.m_minDistanceOnMapBetweenResults);
SetSuggestsEnabled(params.m_suggestsEnabled);
+ m_hotelsFilter = params.m_hotelsFilter;
SetInputLocale(params.m_inputLocale);
@@ -676,6 +677,7 @@ void Processor::InitGeocoder(Geocoder::Params & params)
params.m_pivot = m_viewport[CURRENT_V];
else
params.m_pivot = GetPivotRect();
+ params.m_hotelsFilter = m_hotelsFilter;
m_geocoder.SetParams(params);
}
@@ -735,6 +737,7 @@ void Processor::ClearCaches()
ClearCache(i);
m_geocoder.ClearCaches();
+ m_villagesCache.Clear();
m_preRanker.ClearCaches();
m_ranker.ClearCaches();
}
diff --git a/search/processor.hpp b/search/processor.hpp
index 0aa694a3af..7ca2ea7552 100644
--- a/search/processor.hpp
+++ b/search/processor.hpp
@@ -3,6 +3,7 @@
#include "search/categories_set.hpp"
#include "search/emitter.hpp"
#include "search/geocoder.hpp"
+#include "search/hotels_filter.hpp"
#include "search/mode.hpp"
#include "search/pre_ranker.hpp"
#include "search/rank_table_cache.hpp"
@@ -26,9 +27,10 @@
#include "std/function.hpp"
#include "std/map.hpp"
+#include "std/shared_ptr.hpp"
#include "std/string.hpp"
-#include "std/unordered_set.hpp"
#include "std/unique_ptr.hpp"
+#include "std/unordered_set.hpp"
#include "std/vector.hpp"
class FeatureType;
@@ -162,6 +164,7 @@ protected:
double m_minDistanceOnMapBetweenResults;
Mode m_mode;
bool m_suggestsEnabled;
+ shared_ptr<hotels_filter::Rule> m_hotelsFilter;
SearchParams::TOnResults m_onResults;
/// @name Get ranking params.
diff --git a/search/search.pro b/search/search.pro
index 8d747347aa..7c7cacc87f 100644
--- a/search/search.pro
+++ b/search/search.pro
@@ -33,6 +33,7 @@ HEADERS += \
geometry_cache.hpp \
geometry_utils.hpp \
hotels_classifier.hpp \
+ hotels_filter.hpp \
house_detector.hpp \
house_numbers_matcher.hpp \
house_to_street_table.hpp \
@@ -98,6 +99,7 @@ SOURCES += \
geometry_cache.cpp \
geometry_utils.cpp \
hotels_classifier.cpp \
+ hotels_filter.cpp \
house_detector.cpp \
house_numbers_matcher.cpp \
house_to_street_table.cpp \
diff --git a/search/search_integration_tests/processor_test.cpp b/search/search_integration_tests/processor_test.cpp
index 03db7f3df8..8af3dfbd3a 100644
--- a/search/search_integration_tests/processor_test.cpp
+++ b/search/search_integration_tests/processor_test.cpp
@@ -6,6 +6,7 @@
#include "search/search_tests_support/test_search_request.hpp"
#include "search/token_slice.hpp"
+#include "generator/feature_builder.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
@@ -16,6 +17,7 @@
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
+#include "base/assert.hpp"
#include "base/math.hpp"
#include "std/shared_ptr.hpp"
@@ -28,6 +30,37 @@ namespace search
{
namespace
{
+class TestHotel : public TestPOI
+{
+public:
+ TestHotel(m2::PointD const & center, string const & name, string const & lang, float rating,
+ int priceRate)
+ : TestPOI(center, name, lang), m_rating(rating), m_priceRate(priceRate)
+ {
+ CHECK_GREATER_OR_EQUAL(m_rating, 0.0, ());
+ CHECK_LESS_OR_EQUAL(m_rating, 10.0, ());
+
+ CHECK_GREATER_OR_EQUAL(m_priceRate, 0, ());
+ CHECK_LESS_OR_EQUAL(m_priceRate, 5, ());
+
+ SetTypes({{"tourism", "hotel"}});
+ }
+
+ // TestPOI overrides:
+ void Serialize(FeatureBuilder1 & fb) const override
+ {
+ TestPOI::Serialize(fb);
+
+ auto & metadata = fb.GetMetadataForTesting();
+ metadata.Set(feature::Metadata::FMD_RATING, strings::to_string(m_rating));
+ metadata.Set(feature::Metadata::FMD_PRICE_RATE, strings::to_string(m_priceRate));
+ }
+
+private:
+ float const m_rating;
+ int const m_priceRate;
+};
+
class ProcessorTest : public SearchTest
{
public:
@@ -660,5 +693,62 @@ UNIT_CLASS_TEST(ProcessorTest, TestCoords)
auto const actual = result.GetFeatureCenter();
TEST(MercatorBounds::DistanceOnEarth(expected, actual) <= 1.0, ());
}
+
+UNIT_CLASS_TEST(ProcessorTest, HotelsFiltering)
+{
+ char const countryName[] = "Wonderland";
+
+ TestHotel h1(m2::PointD(0, 0), "h1", "en", 8.0 /* rating */, 2 /* priceRate */);
+ TestHotel h2(m2::PointD(0, 1), "h2", "en", 7.0 /* rating */, 5 /* priceRate */);
+ TestHotel h3(m2::PointD(1, 0), "h3", "en", 9.0 /* rating */, 0 /* priceRate */);
+ TestHotel h4(m2::PointD(1, 1), "h4", "en", 2.0 /* rating */, 4 /* priceRate */);
+
+ auto id = BuildCountry(countryName, [&](TestMwmBuilder & builder) {
+ builder.Add(h1);
+ builder.Add(h2);
+ builder.Add(h3);
+ builder.Add(h4);
+ });
+
+ SearchParams params;
+ params.m_query = "hotel";
+ params.m_inputLocale = "en";
+ params.m_mode = Mode::Everywhere;
+ params.m_suggestsEnabled = false;
+
+ SetViewport(m2::RectD(m2::PointD(-1, -1), m2::PointD(2, 2)));
+ {
+ TestSearchRequest request(m_engine, params, m_viewport);
+ request.Run();
+ TRules rules = {ExactMatch(id, h1), ExactMatch(id, h2), ExactMatch(id, h3), ExactMatch(id, h4)};
+ TEST(MatchResults(rules, request.Results()), ());
+ }
+
+ using namespace hotels_filter;
+
+ params.m_hotelsFilter = And(Gt<Rating>(7.0), Le<PriceRate>(2));
+ {
+ TestSearchRequest request(m_engine, params, m_viewport);
+ request.Run();
+ TRules rules = {ExactMatch(id, h1), ExactMatch(id, h3)};
+ TEST(MatchResults(rules, request.Results()), ());
+ }
+
+ params.m_hotelsFilter = Or(Eq<Rating>(9.0), Le<PriceRate>(4));
+ {
+ TestSearchRequest request(m_engine, params, m_viewport);
+ request.Run();
+ TRules rules = {ExactMatch(id, h1), ExactMatch(id, h3), ExactMatch(id, h4)};
+ TEST(MatchResults(rules, request.Results()), ());
+ }
+
+ params.m_hotelsFilter = Or(And(Eq<Rating>(7.0), Eq<PriceRate>(5)), Eq<PriceRate>(4));
+ {
+ TestSearchRequest request(m_engine, params, m_viewport);
+ request.Run();
+ TRules rules = {ExactMatch(id, h2), ExactMatch(id, h4)};
+ TEST(MatchResults(rules, request.Results()), ());
+ }
+}
} // namespace
} // namespace search
diff --git a/search/search_integration_tests/smoke_test.cpp b/search/search_integration_tests/smoke_test.cpp
index 69c3e28b26..f7bdae1809 100644
--- a/search/search_integration_tests/smoke_test.cpp
+++ b/search/search_integration_tests/smoke_test.cpp
@@ -9,10 +9,13 @@
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "indexer/classificator.hpp"
+#include "indexer/feature_meta.hpp"
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
+#include "base/string_utils.hpp"
+
#include "std/shared_ptr.hpp"
#include "std/vector.hpp"
@@ -34,7 +37,7 @@ public:
// TestFeature overrides:
void Serialize(FeatureBuilder1 & fb) const override
{
- fb.SetTestId(m_id);
+ fb.GetMetadataForTesting().Set(feature::Metadata::FMD_TEST_ID, strings::to_string(m_id));
fb.SetCenter(m_center);
if (!m_name.empty())
diff --git a/search/search_params.hpp b/search/search_params.hpp
index e454d017de..5d21d9b3ec 100644
--- a/search/search_params.hpp
+++ b/search/search_params.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "search/hotels_filter.hpp"
#include "search/mode.hpp"
#include "geometry/latlon.hpp"
@@ -41,6 +42,8 @@ public:
bool m_forceSearch = false;
bool m_suggestsEnabled = true;
+ shared_ptr<hotels_filter::Rule> m_hotelsFilter;
+
friend string DebugPrint(SearchParams const & params);
private:
diff --git a/search/viewport_search_params.hpp b/search/viewport_search_params.hpp
index 5067ef5eb4..4c38a98f17 100644
--- a/search/viewport_search_params.hpp
+++ b/search/viewport_search_params.hpp
@@ -1,6 +1,9 @@
#pragma once
+#include "search/hotels_filter.hpp"
+
#include "std/function.hpp"
+#include "std/shared_ptr.hpp"
#include "std/string.hpp"
namespace search
@@ -12,6 +15,7 @@ struct ViewportSearchParams
string m_query;
string m_inputLocale;
+ shared_ptr<hotels_filter::Rule> m_hotelsFilter;
TOnStarted m_onStarted;
TOnCompleted m_onCompleted;