diff --git a/base/macros.hpp b/base/macros.hpp index e554dcac96..4f9373f7ee 100644 --- a/base/macros.hpp +++ b/base/macros.hpp @@ -1,4 +1,6 @@ #pragma once + +#include "base/assert.hpp" #include "base/base.hpp" namespace my diff --git a/generator/osm2type.cpp b/generator/osm2type.cpp index b963c78dfb..0aa0cfa00b 100644 --- a/generator/osm2type.cpp +++ b/generator/osm2type.cpp @@ -535,7 +535,6 @@ namespace ftype //{ "addr:streetnumber", "*", [¶ms](string & k, string & v) { params.AddStreet(Normalize(v)); k.clear(); v.clear(); }}, //{ "addr:full", "*", [¶ms](string & k, string & v) { params.AddAddress(Normalize(v)); k.clear(); v.clear(); }}, { "addr:postcode", "*", [¶ms](string & k, string & v) { params.AddPostcode(Normalize(v)); k.clear(); v.clear(); }}, - { "addr:flats", "*", [¶ms](string & k, string & v) { params.flats = v; k.clear(); v.clear(); }}, { "population", "*", [¶ms](string & k, string & v) { diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp index 6dcbf8a7a1..e8fee50e22 100644 --- a/indexer/ftypes_matcher.cpp +++ b/indexer/ftypes_matcher.cpp @@ -95,6 +95,17 @@ IsFuelStationChecker const & IsFuelStationChecker::Instance() return inst; } +IsRailwayStationChecker::IsRailwayStationChecker() +{ + Classificator const & c = classif(); + m_types.push_back(c.GetTypeByPath({"railway", "station"})); +} + +IsRailwayStationChecker const & IsRailwayStationChecker::Instance() +{ + static const IsRailwayStationChecker inst; + return inst; +} IsStreetChecker::IsStreetChecker() { diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp index b439fdfb9b..551a2a3d52 100644 --- a/indexer/ftypes_matcher.hpp +++ b/indexer/ftypes_matcher.hpp @@ -62,6 +62,14 @@ public: static IsFuelStationChecker const & Instance(); }; +class IsRailwayStationChecker : public BaseChecker +{ +public: + IsRailwayStationChecker(); + + static IsRailwayStationChecker const & Instance(); +}; + class IsStreetChecker : public BaseChecker { public: diff --git a/indexer/index.hpp b/indexer/index.hpp index 7cdf80daf1..b4e2ecd5d5 100644 --- a/indexer/index.hpp +++ b/indexer/index.hpp @@ -253,6 +253,7 @@ public: string GetCountryFileName() const; bool IsWorld() const; void GetFeatureByIndex(uint32_t index, FeatureType & ft); + inline FeaturesVector const & GetFeaturesVector() const { return m_vector; } private: MwmHandle m_handle; diff --git a/indexer/indexer_tests/feature_metadata_test.cpp b/indexer/indexer_tests/feature_metadata_test.cpp index 9d9cd4de90..9b2cdb9d16 100644 --- a/indexer/indexer_tests/feature_metadata_test.cpp +++ b/indexer/indexer_tests/feature_metadata_test.cpp @@ -49,7 +49,7 @@ UNIT_TEST(Feature_Metadata_PresentTypes) auto const types = m.GetPresentTypes(); TEST_EQUAL(types.size(), m.Size(), ()); for (auto const & type : types) - TEST_EQUAL(m.Get(type), kKeyValues.find(type)->second, ()); + TEST_EQUAL(m.Get(type), kKeyValues.find(static_cast(type))->second, ()); } UNIT_TEST(Feature_Serialization) diff --git a/search/dummy_rank_table.cpp b/search/dummy_rank_table.cpp new file mode 100644 index 0000000000..5c6142b291 --- /dev/null +++ b/search/dummy_rank_table.cpp @@ -0,0 +1,27 @@ +#include "search/dummy_rank_table.hpp" + +#include "base/macros.hpp" + +#include "std/limits.hpp" + +namespace search +{ +uint8_t DummyRankTable::Get(uint64_t i) const { return 0; } + +uint64_t DummyRankTable::Size() const +{ + NOTIMPLEMENTED(); + return numeric_limits::max(); +} + +RankTable::Version DummyRankTable::GetVersion() const +{ + NOTIMPLEMENTED(); + return RankTable::VERSION_COUNT; +} + +void DummyRankTable::Serialize(Writer & /* writer */, bool /* preserveHostEndianness */) +{ + NOTIMPLEMENTED(); +} +} // namespace search diff --git a/search/dummy_rank_table.hpp b/search/dummy_rank_table.hpp new file mode 100644 index 0000000000..212a991a87 --- /dev/null +++ b/search/dummy_rank_table.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "indexer/rank_table.hpp" + +namespace search +{ +// This dummy rank table is used instead of a normal rank table when +// the latter can't be loaded. It should not be serialized and can't +// be loaded. +class DummyRankTable : public RankTable +{ +public: + // RankTable overrides: + uint8_t Get(uint64_t i) const override; + uint64_t Size() const override; + Version GetVersion() const override; + void Serialize(Writer & /* writer */, bool /* preserveHostEndianness */) override; +}; +} // namespace search diff --git a/search/retrieval.cpp b/search/retrieval.cpp index 8049fa5956..2e7b51bf01 100644 --- a/search/retrieval.cpp +++ b/search/retrieval.cpp @@ -20,7 +20,6 @@ #include "std/algorithm.hpp" #include "std/cmath.hpp" -#include "std/exception.hpp" #include "std/limits.hpp" namespace search @@ -38,15 +37,6 @@ double const kMaxViewportScaleLevel = scales::GetUpperCountryScale(); // Otherwise, slow path is used. uint64_t constexpr kFastPathThreshold = 100; -// This exception can be thrown by callbacks from deeps of search and -// geometry retrieval for fast cancellation of time-consuming tasks. -// -// TODO (@gorshenin): after merge to master, move this class to -// base/cancellable.hpp. -struct CancelException : public exception -{ -}; - unique_ptr SortFeaturesAndBuildCBV(vector && features) { my::SortUnique(features); @@ -60,14 +50,13 @@ void CoverRect(m2::RectD const & rect, int scale, covering::IntervalsT & result) result.insert(result.end(), intervals.begin(), intervals.end()); } -namespace -{ +// Retrieves from the search index corresponding to |value| all +// features matching to |params|. template unique_ptr RetrieveAddressFeaturesImpl( - MwmSet::MwmHandle const & handle, my::Cancellable const & cancellable, - SearchQueryParams const & params) + MwmValue * value, my::Cancellable const & cancellable, SearchQueryParams const & params) { - auto * value = handle.GetValue(); + ASSERT(value, ()); serial::CodingParams codingParams(trie::GetCodingParams(value->GetHeader().GetDefCodingParams())); ModelReaderPtr searchReader = value->m_cont.GetReader(SEARCH_INDEX_FILE_TAG); @@ -92,33 +81,6 @@ unique_ptr RetrieveAddressFeaturesImpl( MatchFeaturesInTrie(params, *trieRoot, emptyFilter, collector); return SortFeaturesAndBuildCBV(move(features)); } -} // namespace - -// Retrieves from the search index corresponding to |handle| all -// features matching to |params|. -unique_ptr RetrieveAddressFeatures(MwmSet::MwmHandle const & handle, - my::Cancellable const & cancellable, - SearchQueryParams const & params) -{ - auto * value = handle.GetValue(); - ASSERT(value, ()); - - MwmTraits mwmTraits(value->GetMwmVersion().format); - - if (mwmTraits.GetSearchIndexFormat() == - MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter) - { - using TValue = FeatureWithRankAndCenter; - return RetrieveAddressFeaturesImpl(handle, cancellable, params); - } - else if (mwmTraits.GetSearchIndexFormat() == - MwmTraits::SearchIndexFormat::CompressedBitVector) - { - using TValue = FeatureIndexValue; - return RetrieveAddressFeaturesImpl(handle, cancellable, params); - } - return unique_ptr(); -} // Retrieves from the geometry index corresponding to handle all // features from |coverage|. @@ -402,6 +364,29 @@ Retrieval::Bucket::Bucket(MwmSet::MwmHandle && handle) // Retrieval --------------------------------------------------------------------------------------- Retrieval::Retrieval() : m_index(nullptr), m_featuresReported(0) {} +// static +unique_ptr Retrieval::RetrieveAddressFeatures( + MwmValue * value, my::Cancellable const & cancellable, SearchQueryParams const & params) +{ + ASSERT(value, ()); + + MwmTraits mwmTraits(value->GetMwmVersion().format); + + if (mwmTraits.GetSearchIndexFormat() == + MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter) + { + using TValue = FeatureWithRankAndCenter; + return RetrieveAddressFeaturesImpl(value, cancellable, params); + } + else if (mwmTraits.GetSearchIndexFormat() == + MwmTraits::SearchIndexFormat::CompressedBitVector) + { + using TValue = FeatureIndexValue; + return RetrieveAddressFeaturesImpl(value, cancellable, params); + } + return unique_ptr(); +} + void Retrieval::Init(Index & index, vector> const & infos, m2::RectD const & viewport, SearchQueryParams const & params, Limits const & limits) @@ -532,7 +517,8 @@ bool Retrieval::InitBucketStrategy(Bucket & bucket, double scale) try { - addressFeatures = RetrieveAddressFeatures(bucket.m_handle, *this /* cancellable */, m_params); + addressFeatures = RetrieveAddressFeatures(bucket.m_handle.GetValue(), + *this /* cancellable */, m_params); } catch (CancelException &) { diff --git a/search/retrieval.hpp b/search/retrieval.hpp index f31d916c60..0333ed9e26 100644 --- a/search/retrieval.hpp +++ b/search/retrieval.hpp @@ -9,11 +9,13 @@ #include "base/cancellable.hpp" #include "base/macros.hpp" +#include "std/exception.hpp" #include "std/function.hpp" #include "std/unique_ptr.hpp" #include "std/vector.hpp" class Index; +class MwmValue; namespace coding { @@ -22,6 +24,15 @@ class CompressedBitVector; namespace search { +// This exception can be thrown by callbacks from deeps of search and +// geometry retrieval for fast cancellation of time-consuming tasks. +// +// TODO (@gorshenin): after merge to master, move this class to +// base/cancellable.hpp. +struct CancelException : public exception +{ +}; + class Retrieval : public my::Cancellable { public: @@ -103,6 +114,11 @@ public: Retrieval(); + // Retrieves from the search index corresponding to |value| all + // features matching to |params|. + static unique_ptr RetrieveAddressFeatures( + MwmValue * value, my::Cancellable const & cancellable, SearchQueryParams const & params); + // Initializes retrieval process, sets up internal state, takes all // necessary system resources. void Init(Index & index, vector> const & infos, m2::RectD const & viewport, diff --git a/search/search.pro b/search/search.pro index e4420be2bc..a89163548e 100644 --- a/search/search.pro +++ b/search/search.pro @@ -11,6 +11,7 @@ include($$ROOT_DIR/common.pri) HEADERS += \ algos.hpp \ approximate_string_match.hpp \ + dummy_rank_table.hpp \ feature_offset_match.hpp \ geometry_utils.hpp \ house_detector.hpp \ @@ -35,9 +36,16 @@ HEADERS += \ search_query_params.hpp \ search_string_intersection.hpp \ suggest.hpp \ + v2/features_layer.hpp \ + v2/features_layer_matcher.hpp \ + v2/features_layer_path_finder.hpp \ + v2/geocoder.hpp \ + v2/search_model.hpp \ + v2/search_query_v2.hpp \ SOURCES += \ approximate_string_match.cpp \ + dummy_rank_table.cpp \ geometry_utils.cpp \ house_detector.cpp \ intermediate_result.cpp \ @@ -55,3 +63,9 @@ SOURCES += \ search_engine.cpp \ search_query.cpp \ search_query_params.cpp \ + v2/features_layer.cpp \ + v2/features_layer_matcher.cpp \ + v2/features_layer_path_finder.cpp \ + v2/geocoder.cpp \ + v2/search_model.cpp \ + v2/search_query_v2.cpp \ diff --git a/search/search_engine.cpp b/search/search_engine.cpp index 496c48b70f..2d0394a547 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -241,7 +241,7 @@ void Engine::DoSearch(SearchParams const & params, m2::RectD const & viewport, Results res; - m_query->SearchCoordinates(params.m_query, res); + m_query->SearchCoordinates(res); try { diff --git a/search/search_integration_tests/retrieval_test.cpp b/search/search_integration_tests/retrieval_test.cpp index cd22edf8b4..73df230c74 100644 --- a/search/search_integration_tests/retrieval_test.cpp +++ b/search/search_integration_tests/retrieval_test.cpp @@ -1,5 +1,11 @@ #include "testing/testing.hpp" +#include "search/search_integration_tests/test_feature.hpp" +#include "search/search_integration_tests/test_mwm_builder.hpp" +#include "search/search_integration_tests/test_results_matching.hpp" +#include "search/search_integration_tests/test_search_engine.hpp" +#include "search/search_integration_tests/test_search_request.hpp" + #include "indexer/classificator_loader.hpp" #include "indexer/index.hpp" #include "indexer/mwm_set.hpp" @@ -29,7 +35,6 @@ #include "std/algorithm.hpp" #include "std/initializer_list.hpp" #include "std/limits.hpp" -#include "std/sstream.hpp" #include "std/shared_ptr.hpp" using namespace search::tests_support; @@ -61,118 +66,6 @@ bool AllMwmsReleased(Index & index) return true; } -class MatchingRule -{ -public: - - virtual ~MatchingRule() = default; - - virtual bool Matches(FeatureType const & feature) const = 0; - virtual string ToString() const = 0; -}; - -string DebugPrint(MatchingRule const & rule) { return rule.ToString(); } - -class ExactMatch : public MatchingRule -{ -public: - ExactMatch(MwmSet::MwmId const & mwmId, shared_ptr feature) - : m_mwmId(mwmId), m_feature(feature) - { - } - - // MatchingRule overrides: - bool Matches(FeatureType const & feature) const override - { - if (m_mwmId != feature.GetID().m_mwmId) - return false; - return m_feature->Matches(feature); - } - - string ToString() const override - { - ostringstream os; - os << "ExactMatch [ " << DebugPrint(m_mwmId) << ", " << DebugPrint(*m_feature) << " ]"; - return os.str(); - } - -private: - MwmSet::MwmId m_mwmId; - shared_ptr m_feature; -}; - -class AlternativesMatch : public MatchingRule -{ -public: - AlternativesMatch(initializer_list> rules) : m_rules(move(rules)) {} - - // MatchingRule overrides: - bool Matches(FeatureType const & feature) const override - { - for (auto const & rule : m_rules) - { - if (rule->Matches(feature)) - return true; - } - return false; - } - - string ToString() const override - { - ostringstream os; - os << "OrRule [ "; - for (auto it = m_rules.cbegin(); it != m_rules.cend(); ++it) - { - os << (*it)->ToString(); - if (it + 1 != m_rules.cend()) - os << " | "; - } - os << " ]"; - return os.str(); - } - -private: - vector> m_rules; -}; - -bool MatchResults(Index const & index, vector> rules, - vector const & actual) -{ - vector resultIds; - for (auto const & a : actual) - resultIds.push_back(a.GetFeatureID()); - sort(resultIds.begin(), resultIds.end()); - - vector unexpected; - auto removeMatched = [&rules, &unexpected](FeatureType const & feature) - { - for (auto it = rules.begin(); it != rules.end(); ++it) - { - if ((*it)->Matches(feature)) - { - rules.erase(it); - return; - } - } - unexpected.push_back(DebugPrint(feature) + " from " + DebugPrint(feature.GetID().m_mwmId)); - }; - index.ReadFeatures(removeMatched, resultIds); - - if (rules.empty() && unexpected.empty()) - return true; - - ostringstream os; - os << "Unsatisfied rules:" << endl; - for (auto const & e : rules) - os << " " << DebugPrint(*e) << endl; - os << "Unexpected retrieved features:" << endl; - for (auto const & u : unexpected) - os << " " << u << endl; - - LOG(LWARNING, (os.str())); - return false; -} - void Cleanup(platform::LocalCountryFile const & map) { platform::CountryIndexes::DeleteFromDisk(map); diff --git a/search/search_integration_tests/search_integration_tests.pro b/search/search_integration_tests/search_integration_tests.pro index ef2093d941..4ede564928 100644 --- a/search/search_integration_tests/search_integration_tests.pro +++ b/search/search_integration_tests/search_integration_tests.pro @@ -22,4 +22,5 @@ macx-*: LIBS *= "-framework IOKit" SOURCES += \ ../../testing/testingmain.cpp \ retrieval_test.cpp \ + search_query_v2_test.cpp \ smoke_test.cpp \ diff --git a/search/search_integration_tests/search_query_v2_test.cpp b/search/search_integration_tests/search_query_v2_test.cpp new file mode 100644 index 0000000000..8f43018932 --- /dev/null +++ b/search/search_integration_tests/search_query_v2_test.cpp @@ -0,0 +1,112 @@ +#include "testing/testing.hpp" + +#include "search/search_integration_tests/test_feature.hpp" +#include "search/search_integration_tests/test_mwm_builder.hpp" +#include "search/search_integration_tests/test_results_matching.hpp" +#include "search/search_integration_tests/test_search_engine.hpp" +#include "search/search_integration_tests/test_search_request.hpp" + +#include "search/search_query_factory.hpp" +#include "search/v2/search_query_v2.hpp" + +#include "indexer/classificator_loader.hpp" +#include "indexer/mwm_set.hpp" + +#include "storage/country_decl.hpp" +#include "storage/country_info_getter.hpp" + +#include "geometry/point2d.hpp" + +#include "platform/country_file.hpp" +#include "platform/local_country_file.hpp" +#include "platform/local_country_file_utils.hpp" +#include "platform/platform.hpp" + +#include "base/scope_guard.hpp" + +namespace +{ +void Cleanup(platform::LocalCountryFile const & map) +{ + platform::CountryIndexes::DeleteFromDisk(map); + map.DeleteFromDisk(MapOptions::Map); +} + +class TestSearchQueryFactory : public search::SearchQueryFactory +{ + // search::SearchQueryFactory overrides: + unique_ptr BuildSearchQuery(Index & index, CategoriesHolder const & categories, + vector const & suggests, + storage::CountryInfoGetter const & infoGetter) override + { + return make_unique(index, categories, suggests, infoGetter); + } +}; +} // namespace + +UNIT_TEST(SearchQueryV2_Smoke) +{ + classificator::Load(); + Platform & platform = GetPlatform(); + platform::LocalCountryFile map(platform.WritableDir(), platform::CountryFile("map"), 0); + m2::RectD viewport(m2::PointD(-1.0, -1.0), m2::PointD(1.0, 1.0)); + + Cleanup(map); + MY_SCOPE_GUARD(cleanup, [&]() + { + Cleanup(map); + }); + + vector countries; + countries.emplace_back(map.GetCountryName(), viewport); + + TestSearchEngine engine("en", make_unique(countries), + make_unique()); + auto const mskCity = make_shared(m2::PointD(0, 0), "Moscow", "en", 100 /* rank */); + auto const busStop = make_shared(m2::PointD(0, 0), "Bus stop", "en"); + auto const tramStop = make_shared(m2::PointD(0.0001, 0.0001), "Tram stop", "en"); + auto const quantumTeleport1 = + make_shared(m2::PointD(0.0002, 0.0002), "Quantum teleport 1", "en"); + auto const quantumTeleport2 = + make_shared(m2::PointD(10, 10), "Quantum teleport 2", "en"); + auto const quantumCafe = make_shared(m2::PointD(-0.0002, -0.0002), "Quantum cafe", "en"); + + { + TestMwmBuilder builder(map, feature::DataHeader::country); + builder.Add(*mskCity); + builder.Add(*busStop); + builder.Add(*tramStop); + builder.Add(*quantumTeleport1); + builder.Add(*quantumTeleport2); + builder.Add(*quantumCafe); + } + + auto const regResult = engine.RegisterMap(map); + TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ()); + + { + TestSearchRequest request(engine, "Bus stop", "en", search::SearchParams::ALL, viewport); + request.Wait(); + vector> rules = {make_shared(regResult.first, busStop)}; + TEST(MatchResults(engine, rules, request.Results()), ()); + } + + { + TestSearchRequest request(engine, "quantum", "en", search::SearchParams::ALL, viewport); + request.Wait(); + vector> rules = { + make_shared(regResult.first, quantumCafe), + make_shared(regResult.first, quantumTeleport1), + make_shared(regResult.first, quantumTeleport2)}; + TEST(MatchResults(engine, rules, request.Results()), ()); + } + + { + TestSearchRequest request(engine, "quantum Moscow ", "en", search::SearchParams::ALL, viewport); + request.Wait(); + vector> rules = { + make_shared(regResult.first, quantumCafe), + make_shared(regResult.first, quantumTeleport1)}; + TEST(MatchResults(engine, rules, request.Results()), ()); + } +} diff --git a/search/search_integration_tests/test_results_matching.cpp b/search/search_integration_tests/test_results_matching.cpp new file mode 100644 index 0000000000..28f1745ee8 --- /dev/null +++ b/search/search_integration_tests/test_results_matching.cpp @@ -0,0 +1,94 @@ +#include "search/search_integration_tests/test_results_matching.hpp" + +#include "indexer/feature_decl.hpp" +#include "indexer/index.hpp" + +#include "std/sstream.hpp" + +ExactMatch::ExactMatch(MwmSet::MwmId const & mwmId, shared_ptr feature) + : m_mwmId(mwmId), m_feature(feature) +{ +} + +bool ExactMatch::Matches(FeatureType const & feature) const +{ + if (m_mwmId != feature.GetID().m_mwmId) + return false; + return m_feature->Matches(feature); +} + +string ExactMatch::ToString() const +{ + ostringstream os; + os << "ExactMatch [ " << DebugPrint(m_mwmId) << ", " << DebugPrint(*m_feature) << " ]"; + return os.str(); +} + +AlternativesMatch::AlternativesMatch(initializer_list> rules) + : m_rules(move(rules)) +{ +} + +bool AlternativesMatch::Matches(FeatureType const & feature) const +{ + for (auto const & rule : m_rules) + { + if (rule->Matches(feature)) + return true; + } + return false; +} + +string AlternativesMatch::ToString() const +{ + ostringstream os; + os << "OrRule [ "; + for (auto it = m_rules.cbegin(); it != m_rules.cend(); ++it) + { + os << (*it)->ToString(); + if (it + 1 != m_rules.cend()) + os << " | "; + } + os << " ]"; + return os.str(); +} + +bool MatchResults(Index const & index, vector> rules, + vector const & actual) +{ + vector resultIds; + for (auto const & a : actual) + resultIds.push_back(a.GetFeatureID()); + sort(resultIds.begin(), resultIds.end()); + + vector unexpected; + auto removeMatched = [&rules, &unexpected](FeatureType const & feature) + { + for (auto it = rules.begin(); it != rules.end(); ++it) + { + if ((*it)->Matches(feature)) + { + rules.erase(it); + return; + } + } + unexpected.push_back(DebugPrint(feature) + " from " + DebugPrint(feature.GetID().m_mwmId)); + }; + index.ReadFeatures(removeMatched, resultIds); + + if (rules.empty() && unexpected.empty()) + return true; + + ostringstream os; + os << "Unsatisfied rules:" << endl; + for (auto const & e : rules) + os << " " << DebugPrint(*e) << endl; + os << "Unexpected retrieved features:" << endl; + for (auto const & u : unexpected) + os << " " << u << endl; + + LOG(LWARNING, (os.str())); + return false; +} + +string DebugPrint(MatchingRule const & rule) { return rule.ToString(); } diff --git a/search/search_integration_tests/test_results_matching.hpp b/search/search_integration_tests/test_results_matching.hpp new file mode 100644 index 0000000000..887bf4c13e --- /dev/null +++ b/search/search_integration_tests/test_results_matching.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "search/search_integration_tests/test_feature.hpp" + +#include "search/result.hpp" + +#include "indexer/mwm_set.hpp" + +#include "std/shared_ptr.hpp" +#include "std/string.hpp" +#include "std/vector.hpp" + +class FeatureType; +class Index; + +class MatchingRule +{ +public: + virtual ~MatchingRule() = default; + + virtual bool Matches(FeatureType const & feature) const = 0; + virtual string ToString() const = 0; +}; + +class ExactMatch : public MatchingRule +{ +public: + ExactMatch(MwmSet::MwmId const & mwmId, shared_ptr feature); + + // MatchingRule overrides: + bool Matches(FeatureType const & feature) const override; + string ToString() const override; + +private: + MwmSet::MwmId m_mwmId; + shared_ptr m_feature; +}; + +class AlternativesMatch : public MatchingRule +{ +public: + AlternativesMatch(initializer_list> rules); + + // MatchingRule overrides: + bool Matches(FeatureType const & feature) const override; + string ToString() const override; + +private: + vector> m_rules; +}; + +bool MatchResults(Index const & index, vector> rules, + vector const & actual); + +string DebugPrint(MatchingRule const & rule); diff --git a/search/search_query.cpp b/search/search_query.cpp index 5c89e3484b..2e420127a4 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -1,5 +1,6 @@ #include "search/search_query.hpp" +#include "search/dummy_rank_table.hpp" #include "search/feature_offset_match.hpp" #include "search/geometry_utils.hpp" #include "search/indexed_value.hpp" @@ -135,33 +136,6 @@ public: impl::PreResult2 const & operator*() const { return *m_val; } }; -// This dummy rank table is used instead of a normal rank table when -// the latter can't be loaded. It should not be serialized and can't -// be loaded. -class DummyRankTable : public RankTable -{ -public: - // RankTable overrides: - uint8_t Get(uint64_t i) const override { return 0; } - - uint64_t Size() const override - { - NOTIMPLEMENTED(); - return numeric_limits::max(); - } - - Version GetVersion() const override - { - NOTIMPLEMENTED(); - return RankTable::VERSION_COUNT; - } - - void Serialize(Writer & /* writer */, bool /* preserveHostEndianness */) override - { - NOTIMPLEMENTED(); - } -}; - string DebugPrint(IndexedValue const & value) { string index; @@ -508,7 +482,7 @@ void Query::ProcessEmojiIfNeeded(strings::UniString const & token, size_t ind, T void Query::SetQuery(string const & query) { - m_query = &query; + m_query = query; /// @todo Why Init is separated with SetQuery? ASSERT(m_tokens.empty(), ()); @@ -606,10 +580,10 @@ void Query::SearchViewportPoints(Results & res) } } -void Query::SearchCoordinates(string const & query, Results & res) const +void Query::SearchCoordinates(Results & res) const { double lat, lon; - if (MatchLatLonDegree(query, lat, lon)) + if (MatchLatLonDegree(m_query, lat, lon)) { ASSERT_EQUAL(res.GetCount(), 0, ()); res.AddResultNoChecks(MakeResult(impl::PreResult2(lat, lon))); @@ -923,7 +897,7 @@ void Query::GetSuggestion(string const & name, string & suggest) const if (!prefixMatched) return; - RemoveStringPrefix(*m_query, suggest); + RemoveStringPrefix(m_query, suggest); // Append unmatched result's tokens to the suggestion. for (size_t i = 0; i < vName.size(); ++i) @@ -1756,7 +1730,7 @@ void Query::SuggestStrings(Results & res) int const localesCount = GetCategoryLocales(arrLocales); string prolog; - RemoveStringPrefix(*m_query, prolog); + RemoveStringPrefix(m_query, prolog); for (int i = 0; i < localesCount; ++i) MatchForSuggestionsImpl(m_prefix, arrLocales[i], prolog, res); diff --git a/search/search_query.hpp b/search/search_query.hpp index 59ebb70269..035cb35727 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -95,9 +95,11 @@ public: /// @name Different search functions. //@{ - void Search(Results & res, size_t resCount); - void SearchViewportPoints(Results & res); - void SearchCoordinates(string const & query, Results & res) const; + virtual void Search(Results & res, size_t resCount); + virtual void SearchViewportPoints(Results & res); + + // Tries to generate a (lat, lon) result from |m_query|. + void SearchCoordinates(Results & res) const; //@} // Get scale level to make geometry index query for current viewport. @@ -113,7 +115,7 @@ public: void InitParams(bool localitySearch, SearchQueryParams & params); -private: +protected: enum ViewportID { DEFAULT_V = -1, @@ -209,7 +211,7 @@ private: storage::CountryInfoGetter const & m_infoGetter; string m_region; - string const * m_query; + string m_query; buffer_vector m_tokens; strings::UniString m_prefix; set m_prefferedTypes; @@ -271,7 +273,7 @@ public: kQueuesCount = 2 }; -private: +protected: // The values order should be the same as in // g_arrCompare1, g_arrCompare2 function arrays. enum diff --git a/search/search_query_base.cpp b/search/search_query_base.cpp new file mode 100644 index 0000000000..299e46cb56 --- /dev/null +++ b/search/search_query_base.cpp @@ -0,0 +1,11 @@ +#include "search/search_query_base.hpp" + +namespace search +{ +SearchQueryBase::SearchQueryBase(Index & index, CategoriesHolder const & categories, + vector const & suggests, + storage::CountryInfoGetter const & infoGetter) + : m_index(index), m_categories(categories), m_suggests(suggests), m_infoGetter(infoGetter) +{ +} +} // namespace search diff --git a/search/search_query_base.hpp b/search/search_query_base.hpp new file mode 100644 index 0000000000..a4634f3b25 --- /dev/null +++ b/search/search_query_base.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "search/suggest.hpp" + +#include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" + +#include "base/cancellable.hpp" + +#include "std/string.hpp" +#include "std/vector.hpp" + +class Index; +class CategoriesHolder; + +namespace storage +{ +class CountryInfoGetter; +} + +namespace search +{ +class Results; + +class SearchQueryBase : public my::Cancellable +{ +public: + SearchQueryBase(Index & index, CategoriesHolder const & categories, + vector const & suggests, storage::CountryInfoGetter const & infoGetter); + + virtual void Init(bool viewportSearch) = 0; + + virtual void SetInputLocale(string const & locale) = 0; + virtual void SetPreferredLocale(string const & locale) = 0; + virtual void SetQuery(string const & query) = 0; + virtual void SetRankPivot(m2::PointD const & pivot) = 0; + virtual void SetSearchInWorld(bool searchInWorld) = 0; + virtual void SetSupportOldFormat(bool supportOldFormat) = 0; + virtual void SetViewport(m2::RectD const & viewport, bool forceUpdate) = 0; + + virtual void Search(Results & results, size_t maxResults) = 0; + virtual void SearchViewportPoints(Results & results) = 0; + virtual void SearchCoordinates(Results & results) = 0; + + virtual void ClearCaches() = 0; + +protected: + Index & m_index; + CategoriesHolder const & m_categories; + vector const & m_suggests; + storage::CountryInfoGetter const & m_infoGetter; +}; +} // namespace search diff --git a/search/search_query_factory.hpp b/search/search_query_factory.hpp index 68b995f64a..6e0db01b6d 100644 --- a/search/search_query_factory.hpp +++ b/search/search_query_factory.hpp @@ -3,6 +3,10 @@ #include "search/search_query.hpp" #include "search/suggest.hpp" +#if defined(USE_SEARCH_QUERY_V2) +#include "search/v2/search_query_v2.hpp" +#endif // defined(USE_SEARCH_QUERY_V2) + #include "std/unique_ptr.hpp" namespace storage @@ -21,7 +25,11 @@ public: vector const & suggests, storage::CountryInfoGetter const & infoGetter) { +#if defined(USE_SEARCH_QUERY_V2) + return make_unique(index, categories, suggests, infoGetter); +#else return make_unique(index, categories, suggests, infoGetter); +#endif // defined(USE_SEARCH_QUERY_V2) } }; } // namespace search diff --git a/search/search_tests_support/test_search_engine.cpp b/search/search_tests_support/test_search_engine.cpp index 5ba60e6e1d..49f124f2d5 100644 --- a/search/search_tests_support/test_search_engine.cpp +++ b/search/search_tests_support/test_search_engine.cpp @@ -67,6 +67,16 @@ TestSearchEngine::TestSearchEngine(string const & locale, { } +TestSearchEngine::TestSearchEngine(string const & locale, + unique_ptr && infoGetter, + unique_ptr && factory) + : m_platform(GetPlatform()) + , m_infoGetter(move(infoGetter)) + , m_engine(*this, m_platform.GetReader(SEARCH_CATEGORIES_FILE_NAME), *m_infoGetter, locale, + move(factory)) +{ +} + TestSearchEngine::~TestSearchEngine() {} weak_ptr TestSearchEngine::Search(search::SearchParams const & params, diff --git a/search/search_tests_support/test_search_engine.hpp b/search/search_tests_support/test_search_engine.hpp index b4f46f5b96..c349e98742 100644 --- a/search/search_tests_support/test_search_engine.hpp +++ b/search/search_tests_support/test_search_engine.hpp @@ -27,6 +27,8 @@ class TestSearchEngine : public Index public: TestSearchEngine(string const & locale); TestSearchEngine(string const & locale, unique_ptr && infoGetter); + TestSearchEngine(string const & locale, unique_ptr && infoGetter, + unique_ptr && factory); ~TestSearchEngine(); weak_ptr Search(search::SearchParams const & params, diff --git a/search/v2/features_layer.cpp b/search/v2/features_layer.cpp new file mode 100644 index 0000000000..4d137d958c --- /dev/null +++ b/search/v2/features_layer.cpp @@ -0,0 +1,30 @@ +#include "search/v2/features_layer.hpp" + +#include "base/internal/message.hpp" + +#include "std/sstream.hpp" + +namespace search +{ +namespace v2 +{ +FeaturesLayer::FeaturesLayer() { Clear(); } + +void FeaturesLayer::Clear() +{ + m_features.clear(); + m_startToken = 0; + m_endToken = 0; + m_type = SearchModel::SEARCH_TYPE_COUNT; +} + +string DebugPrint(FeaturesLayer const & layer) +{ + ostringstream os; + os << "FeaturesLayer [ m_features: " << ::DebugPrint(layer.m_features) + << ", m_startToken: " << layer.m_startToken << ", m_endToken: " << layer.m_endToken + << ", m_type: " << DebugPrint(layer.m_type) << " ]"; + return os.str(); +} +} // namespace v2 +} // namespace search diff --git a/search/v2/features_layer.hpp b/search/v2/features_layer.hpp new file mode 100644 index 0000000000..97bac2a857 --- /dev/null +++ b/search/v2/features_layer.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "search/v2/search_model.hpp" + +#include "std/vector.hpp" + +namespace search +{ +namespace v2 +{ +// This structure represents a part of search query interpretation - +// when to a substring of tokens [m_startToken, m_endToken) is matched +// with a set of m_features of the same m_type. +struct FeaturesLayer +{ + FeaturesLayer(); + + void Clear(); + + vector m_features; + size_t m_startToken; + size_t m_endToken; + SearchModel::SearchType m_type; +}; + +string DebugPrint(FeaturesLayer const & layer); +} // namespace v2 +} // namespace search diff --git a/search/v2/features_layer_matcher.cpp b/search/v2/features_layer_matcher.cpp new file mode 100644 index 0000000000..7de71e3f89 --- /dev/null +++ b/search/v2/features_layer_matcher.cpp @@ -0,0 +1,12 @@ +#include "search/v2/features_layer_matcher.hpp" + +namespace search +{ +namespace v2 +{ +FeaturesLayerMatcher::FeaturesLayerMatcher(FeaturesVector const & featuresVector) + : m_featuresVector(featuresVector) +{ +} +} // namespace v2 +} // namespace search diff --git a/search/v2/features_layer_matcher.hpp b/search/v2/features_layer_matcher.hpp new file mode 100644 index 0000000000..6fded3a286 --- /dev/null +++ b/search/v2/features_layer_matcher.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "search/v2/features_layer.hpp" +#include "search/v2/search_model.hpp" + +#include "indexer/feature.hpp" +#include "indexer/feature_algo.hpp" +#include "indexer/features_vector.hpp" +#include "indexer/ftypes_matcher.hpp" + +#include "geometry/mercator.hpp" +#include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" + +#include "base/macros.hpp" + +#include "std/vector.hpp" + +namespace search +{ +namespace v2 +{ +class FeaturesLayerMatcher +{ +public: + FeaturesLayerMatcher(FeaturesVector const & featuresVector); + + template + void Match(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn) const + { + if (child.m_type >= parent.m_type) + return; + if (child.m_type == SearchModel::SEARCH_TYPE_BUILDING && + parent.m_type == SearchModel::SEARCH_TYPE_STREET) + { + // TODO (y@): match buildings with streets. + return; + } + + vector childCenters; + for (uint32_t featureId : child.m_features) + { + FeatureType ft; + m_featuresVector.GetByIndex(featureId, ft); + childCenters.push_back(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY)); + } + + vector parentRects; + for (uint32_t featureId : parent.m_features) + { + FeatureType feature; + m_featuresVector.GetByIndex(featureId, feature); + m2::PointD center = feature::GetCenter(feature, FeatureType::WORST_GEOMETRY); + double radius = ftypes::GetRadiusByPopulation(feature.GetPopulation()); + parentRects.push_back(MercatorBounds::RectByCenterXYAndSizeInMeters(center, radius)); + } + + for (size_t j = 0; j < parent.m_features.size(); ++j) + { + for (size_t i = 0; i < child.m_features.size(); ++i) + { + if (parentRects[j].IsPointInside(childCenters[i])) + fn(i, j); + } + } + } + +private: + FeaturesVector const & m_featuresVector; +}; +} // namespace v2 +} // namespace search diff --git a/search/v2/features_layer_path_finder.cpp b/search/v2/features_layer_path_finder.cpp new file mode 100644 index 0000000000..7853e4d498 --- /dev/null +++ b/search/v2/features_layer_path_finder.cpp @@ -0,0 +1,45 @@ +#include "search/v2/features_layer_path_finder.hpp" + +#include "search/v2/features_layer.hpp" + +#include "indexer/features_vector.hpp" + +namespace search +{ +namespace v2 +{ +FeaturesLayerPathFinder::FeaturesLayerPathFinder(FeaturesVector const & featuresVector) + : m_matcher(featuresVector) +{ +} + +void FeaturesLayerPathFinder::BuildGraph(vector const & layers) +{ + m_graph.clear(); + + for (size_t i = 0; i + 1 < layers.size(); ++i) + { + auto & child = (*layers[i]); + auto & parent = (*layers[i + 1]); + auto addEdges = [&](uint32_t from, uint32_t to) + { + m_graph[parent.m_features[to]].push_back(child.m_features[from]); + }; + m_matcher.Match(child, parent, addEdges); + } +} + +void FeaturesLayerPathFinder::Dfs(uint32_t u) +{ + m_visited.insert(u); + auto const adj = m_graph.find(u); + if (adj == m_graph.end()) + return; + for (uint32_t v : adj->second) + { + if (m_visited.count(v) == 0) + Dfs(v); + } +} +} // namespace v2 +} // namespace search diff --git a/search/v2/features_layer_path_finder.hpp b/search/v2/features_layer_path_finder.hpp new file mode 100644 index 0000000000..1096185cb4 --- /dev/null +++ b/search/v2/features_layer_path_finder.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "search/v2/features_layer_matcher.hpp" + +#include "std/unordered_map.hpp" +#include "std/unordered_set.hpp" +#include "std/vector.hpp" + +class FeaturesVector; + +namespace search +{ +namespace v2 +{ +struct FeaturesLayer; + +class FeaturesLayerPathFinder +{ +public: + using TAdjList = vector; + using TLayerGraph = unordered_map; + + FeaturesLayerPathFinder(FeaturesVector const & featuresVector); + + template + void ForEachReachableVertex(vector const & layers, TFn && fn) + { + if (layers.empty()) + return; + + BuildGraph(layers); + + m_visited.clear(); + for (uint32_t featureId : (*layers.back()).m_features) + Dfs(featureId); + + for (uint32_t featureId : (*layers.front()).m_features) + { + if (m_visited.count(featureId) != 0) + fn(featureId); + } + } + +private: + void BuildGraph(vector const & layers); + + void Dfs(uint32_t u); + + FeaturesLayerMatcher m_matcher; + TLayerGraph m_graph; + unordered_set m_visited; +}; +} // namespace v2 +} // namespace search diff --git a/search/v2/geocoder.cpp b/search/v2/geocoder.cpp new file mode 100644 index 0000000000..507f4bd0d9 --- /dev/null +++ b/search/v2/geocoder.cpp @@ -0,0 +1,233 @@ +#include "search/v2/geocoder.hpp" + +#include "search/retrieval.hpp" +#include "search/v2/features_layer_path_finder.hpp" + +#include "indexer/feature_decl.hpp" +#include "indexer/index.hpp" +#include "indexer/search_delimiters.hpp" +#include "indexer/search_string_utils.hpp" + +#include "coding/multilang_utf8_string.hpp" + +#include "platform/preferred_languages.hpp" + +#include "base/assert.hpp" +#include "base/logging.hpp" +#include "base/macros.hpp" +#include "base/scope_guard.hpp" +#include "base/stl_add.hpp" + +#include "std/algorithm.hpp" +#include "std/iterator.hpp" + +namespace search +{ +namespace v2 +{ +Geocoder::Geocoder(Index & index) + : m_index(index) + , m_numTokens(0) + , m_model(SearchModel::Instance()) + , m_value(nullptr) + , m_results(nullptr) +{ +} + +Geocoder::~Geocoder() {} + +void Geocoder::SetSearchQueryParams(SearchQueryParams const & params) +{ + m_params = params; + m_retrievalParams = params; + + m_numTokens = m_params.m_tokens.size(); + if (!m_params.m_prefixTokens.empty()) + ++m_numTokens; +} + +void Geocoder::Go(vector & results) +{ + m_results = &results; + + try + { + vector> mwmsInfo; + m_index.GetMwmsInfo(mwmsInfo); + + // Iterate through all alive mwms and perform geocoding. + for (auto const & info : mwmsInfo) + { + auto handle = m_index.GetMwmHandleById(MwmSet::MwmId(info)); + if (!handle.IsAlive()) + continue; + + m_value = handle.GetValue(); + if (!m_value || !m_value->m_cont.IsExist(SEARCH_INDEX_FILE_TAG)) + continue; + + m_mwmId = handle.GetId(); + + MY_SCOPE_GUARD(cleanup, [&]() + { + m_finder.reset(); + m_loader.reset(); + m_cache.clear(); + }); + + m_loader.reset(new Index::FeaturesLoaderGuard(m_index, m_mwmId)); + m_finder.reset(new FeaturesLayerPathFinder(m_loader->GetFeaturesVector())); + m_cache.clear(); + + DoGeocoding(0 /* curToken */); + } + } + catch (CancelException & e) + { + } +} + +void Geocoder::PrepareParams(size_t from, size_t to) +{ + ASSERT_LESS(from, to, ()); + ASSERT_LESS_OR_EQUAL(to, m_numTokens, ()); + + m_retrievalParams.m_tokens.clear(); + m_retrievalParams.m_prefixTokens.clear(); + + for (size_t i = from; i < to; ++i) + { + if (i < m_params.m_tokens.size()) + m_retrievalParams.m_tokens.push_back(m_params.m_tokens[i]); + else + m_retrievalParams.m_prefixTokens = m_params.m_prefixTokens; + } +} + +void Geocoder::DoGeocoding(size_t curToken) +{ + if (curToken == m_numTokens) + { + // All tokens were consumed, intersect layers, emit features. + IntersectLayers(); + return; + } + + m_layers.emplace_back(); + MY_SCOPE_GUARD(cleanupGuard, bind(&vector::pop_back, &m_layers)); + + // Try to consume first n tokens starting at |curToken|. + for (size_t n = 1; curToken + n <= m_numTokens; ++n) + { + PrepareParams(curToken, curToken + n); + { + auto & layer = m_layers.back(); + layer.Clear(); + layer.m_startToken = curToken; + layer.m_endToken = curToken + n; + } + + auto features = RetrieveAddressFeatures(curToken, curToken + n); + if (!features || features->PopCount() == 0) + continue; + + vector clusters[SearchModel::SEARCH_TYPE_COUNT]; + auto clusterize = [&](uint64_t featureId) + { + FeatureType feature; + m_loader->GetFeatureByIndex(featureId, feature); + feature.ParseTypes(); + SearchModel::SearchType searchType = m_model.GetSearchType(feature); + if (searchType != SearchModel::SEARCH_TYPE_COUNT) + clusters[searchType].push_back(featureId); + }; + + coding::CompressedBitVectorEnumerator::ForEach(*features, clusterize); + + bool const looksLikeHouseNumber = LooksLikeHouseNumber(curToken, curToken + n); + + for (size_t i = 0; i != SearchModel::SEARCH_TYPE_COUNT; ++i) + { + if (i == SearchModel::SEARCH_TYPE_BUILDING) + { + if (clusters[i].empty() && !looksLikeHouseNumber) + continue; + } + else if (clusters[i].empty()) + { + continue; + } + + // ATTENTION: DO NOT USE layer after recursive calls to + // DoGeocoding(). This may lead to use-after-free. + auto & layer = m_layers.back(); + + layer.m_features.swap(clusters[i]); + layer.m_type = static_cast(i); + if (IsLayerSequenceSane()) + DoGeocoding(curToken + n); + } + } +} + +coding::CompressedBitVector * Geocoder::RetrieveAddressFeatures(size_t curToken, size_t endToken) +{ + uint64_t const key = (static_cast(curToken) << 32) | static_cast(endToken); + if (m_cache.find(key) == m_cache.end()) + { + m_cache[key] = + Retrieval::RetrieveAddressFeatures(m_value, *this /* cancellable */, m_retrievalParams); + } + return m_cache[key].get(); +} + +bool Geocoder::IsLayerSequenceSane() const +{ + ASSERT(!m_layers.empty(), ()); + static_assert(SearchModel::SEARCH_TYPE_COUNT <= 32, + "Select a wider type to represent search types mask."); + uint32_t mask = 0; + for (auto const & layer : m_layers) + { + ASSERT_NOT_EQUAL(layer.m_type, SearchModel::SEARCH_TYPE_COUNT, ()); + + // TODO (@y): probably it's worth to check belongs-to-locality here. + + uint32_t bit = 1U << layer.m_type; + if (mask & bit) + return false; + mask |= bit; + } + return true; +} + +bool Geocoder::LooksLikeHouseNumber(size_t curToken, size_t endToken) const +{ + // TODO (@y): implement this. + // NOTIMPLEMENTED(); + return false; +} + +void Geocoder::IntersectLayers() +{ + ASSERT(!m_layers.empty(), ()); + + auto const compareByType = [](FeaturesLayer const * lhs, FeaturesLayer const * rhs) + { + return lhs->m_type < rhs->m_type; + }; + + // Layers ordered by a search type. + vector sortedLayers; + sortedLayers.reserve(m_layers.size()); + for (auto & layer : m_layers) + sortedLayers.push_back(&layer); + sort(sortedLayers.begin(), sortedLayers.end(), compareByType); + + m_finder->ForEachReachableVertex(sortedLayers, [this](uint32_t featureId) + { + m_results->emplace_back(m_mwmId, featureId); + }); +} +} // namespace v2 +} // namespace search diff --git a/search/v2/geocoder.hpp b/search/v2/geocoder.hpp new file mode 100644 index 0000000000..3d2f0202c8 --- /dev/null +++ b/search/v2/geocoder.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "search/search_query_params.hpp" +#include "search/v2/features_layer.hpp" +#include "search/v2/search_model.hpp" + +#include "indexer/mwm_set.hpp" +#include "indexer/index.hpp" + +#include "coding/compressed_bit_vector.hpp" + +#include "geometry/rect2d.hpp" + +#include "base/buffer_vector.hpp" +#include "base/cancellable.hpp" +#include "base/string_utils.hpp" + +#include "std/set.hpp" +#include "std/string.hpp" +#include "std/unique_ptr.hpp" +#include "std/unordered_map.hpp" +#include "std/vector.hpp" + +class MwmValue; + +namespace coding +{ +class CompressedBitVector; +} + +namespace search +{ +class RankTable; + +namespace v2 +{ +class FeaturesLayerPathFinder; +class SearchModel; + +class Geocoder : public my::Cancellable +{ +public: + Geocoder(Index & index); + + ~Geocoder() override; + + void SetSearchQueryParams(SearchQueryParams const & params); + + void Go(vector & results); + +private: + void PrepareParams(size_t from, size_t to); + + void DoGeocoding(size_t curToken); + + coding::CompressedBitVector * RetrieveAddressFeatures(size_t curToken, size_t endToken); + + bool IsLayerSequenceSane() const; + + bool LooksLikeHouseNumber(size_t curToken, size_t endToken) const; + + void IntersectLayers(); + + Index & m_index; + + // Initial search query params. + SearchQueryParams m_params; + + // Total number of tokens (including last prefix token, if + // non-empty). + size_t m_numTokens; + + // This field is used to map features to a limited number of search + // classes. + SearchModel const & m_model; + + // Following fields are set up by Search() method and can be + // modified and used only from Search() or it's callees. + + // Value of a current mwm. + MwmValue * m_value; + + // Id of a current mwm. + MwmSet::MwmId m_mwmId; + + // Cache of bit-vectors. + unordered_map> m_cache; + + // Features loader. + unique_ptr m_loader; + + // Path finder for interpretations. + unique_ptr m_finder; + + // Search query params prepared for retrieval. + SearchQueryParams m_retrievalParams; + + // Stack of layers filled during geocoding. + vector m_layers; + + vector * m_results; +}; +} // namespace v2 +} // namespace search diff --git a/search/v2/search_model.cpp b/search/v2/search_model.cpp new file mode 100644 index 0000000000..d0729e057b --- /dev/null +++ b/search/v2/search_model.cpp @@ -0,0 +1,93 @@ +#include "search/v2/search_model.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/ftypes_matcher.hpp" + +#include "base/macros.hpp" + +using namespace ftypes; + +namespace search +{ +namespace v2 +{ +SearchModel::SearchModel() +{ + m_poiCheckers.push_back(&IsPeakChecker::Instance()); + m_poiCheckers.push_back(&IsATMChecker::Instance()); + m_poiCheckers.push_back(&IsFuelStationChecker::Instance()); + m_poiCheckers.push_back(&IsRailwayStationChecker::Instance()); +} + +// static +SearchModel const & SearchModel::Instance() +{ + static SearchModel model; + return model; +} + +SearchModel::SearchType SearchModel::GetSearchType(FeatureType const & feature) const +{ + static auto const & buildingChecker = IsBuildingChecker::Instance(); + static auto const & streetChecker = IsStreetChecker::Instance(); + static auto const & localityChecker = IsLocalityChecker::Instance(); + + for (auto const * checker : m_poiCheckers) + { + if ((*checker)(feature)) + return SEARCH_TYPE_POI; + } + + if (buildingChecker(feature)) + return SEARCH_TYPE_BUILDING; + + if (streetChecker(feature)) + return SEARCH_TYPE_STREET; + + if (localityChecker(feature)) + { + Type type = localityChecker.GetType(feature); + switch (type) + { + case NONE: + return SEARCH_TYPE_COUNT; + case COUNTRY: + return SEARCH_TYPE_COUNTRY; + case STATE: + return SEARCH_TYPE_STATE; + case CITY: + case TOWN: + case VILLAGE: + return SEARCH_TYPE_CITY; + case LOCALITY_COUNT: + return SEARCH_TYPE_COUNT; + } + } + + return SEARCH_TYPE_COUNT; +} + +string DebugPrint(SearchModel::SearchType type) +{ + switch (type) + { + case SearchModel::SEARCH_TYPE_POI: + return "POI"; + case SearchModel::SEARCH_TYPE_BUILDING: + return "BUILDING"; + case SearchModel::SEARCH_TYPE_STREET: + return "STREET"; + case SearchModel::SEARCH_TYPE_CITY: + return "CITY"; + case SearchModel::SEARCH_TYPE_STATE: + return "STATE"; + case SearchModel::SEARCH_TYPE_COUNTRY: + return "COUNTRY"; + case SearchModel::SEARCH_TYPE_COUNT: + return "COUNT"; + } + ASSERT(false, ("Unknown search type:", static_cast(type))); + return string(); +} +} // namespace v2 +} // namespace search diff --git a/search/v2/search_model.hpp b/search/v2/search_model.hpp new file mode 100644 index 0000000000..03cdd4ceee --- /dev/null +++ b/search/v2/search_model.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "std/string.hpp" +#include "std/vector.hpp" + +class FeatureType; + +namespace ftypes +{ +class BaseChecker; +} + +namespace search +{ +namespace v2 +{ +// This class is used to map our types to a restricted set of +// different search classes (do not confuse these classes with search +// categories - they are completely different things). +class SearchModel +{ +public: + enum SearchType + { + SEARCH_TYPE_POI, + SEARCH_TYPE_BUILDING, + SEARCH_TYPE_STREET, + SEARCH_TYPE_CITY, + SEARCH_TYPE_STATE, + SEARCH_TYPE_COUNTRY, + SEARCH_TYPE_COUNT + }; + + SearchModel(); + + static SearchModel const & Instance(); + + SearchType GetSearchType(FeatureType const & feature) const; + +private: + vector m_poiCheckers; +}; + +string DebugPrint(SearchModel::SearchType type); +} // namespace v2 +} // namespace search diff --git a/search/v2/search_query_v2.cpp b/search/v2/search_query_v2.cpp new file mode 100644 index 0000000000..af46a44fc0 --- /dev/null +++ b/search/v2/search_query_v2.cpp @@ -0,0 +1,78 @@ +#include "search/v2/search_query_v2.hpp" + +#include "search/dummy_rank_table.hpp" + +#include "indexer/rank_table.hpp" + +#include "base/macros.hpp" + +namespace search +{ +namespace v2 +{ +SearchQueryV2::SearchQueryV2(Index & index, CategoriesHolder const & categories, + vector const & suggests, + storage::CountryInfoGetter const & infoGetter) + : Query(index, categories, suggests, infoGetter), m_geocoder(index) +{ +} + +void SearchQueryV2::Reset() +{ + Query::Reset(); + m_geocoder.Reset(); +} + +void SearchQueryV2::Cancel() +{ + Query::Cancel(); + m_geocoder.Cancel(); +} + +void SearchQueryV2::Search(Results & res, size_t resCount) +{ + if (IsCancelled()) + return; + if (m_tokens.empty()) + SuggestStrings(res); + + SearchQueryParams params; + InitParams(false /* localitySearch */, params); + m_geocoder.SetSearchQueryParams(params); + + vector results; + m_geocoder.Go(results); + AddPreResults1(results); + + FlushResults(res, false /* allMWMs */, resCount); +} + +void SearchQueryV2::SearchViewportPoints(Results & res) { NOTIMPLEMENTED(); } + +void SearchQueryV2::AddPreResults1(vector & results) +{ + // Group all features by MwmId and add them as PreResult1. + sort(results.begin(), results.end()); + + auto ib = results.begin(); + while (ib != results.end()) + { + auto ie = ib; + while (ie != results.end() && ie->m_mwmId == ib->m_mwmId) + ++ie; + + MwmSet::MwmHandle handle = m_index.GetMwmHandleById(ib->m_mwmId); + if (handle.IsAlive()) + { + auto rankTable = RankTable::Load(handle.GetValue()->m_cont); + if (!rankTable.get()) + rankTable.reset(new DummyRankTable()); + + for (auto ii = ib; ii != ie; ++ii) + AddPreResult1(ii->m_mwmId, ii->m_index, rankTable->Get(ii->m_index), 0.0 /* priority */); + } + ib = ie; + } +} +} // namespace v2 +} // namespace search diff --git a/search/v2/search_query_v2.hpp b/search/v2/search_query_v2.hpp new file mode 100644 index 0000000000..785e12a1bf --- /dev/null +++ b/search/v2/search_query_v2.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "search/search_query.hpp" +#include "search/v2/geocoder.hpp" + +namespace search +{ +namespace v2 +{ +class SearchQueryV2 : public Query +{ +public: + SearchQueryV2(Index & index, CategoriesHolder const & categories, + vector const & suggests, storage::CountryInfoGetter const & infoGetter); + + // my::Cancellable overrides: + void Reset() override; + void Cancel() override; + + // Query overrides: + void Search(Results & res, size_t resCount) override; + void SearchViewportPoints(Results & res) override; + +protected: + // Adds a bunch of features as PreResult1. + void AddPreResults1(vector & results); + + Geocoder m_geocoder; +}; +} // namespace v2 +} // namespace search