From 96a30087d390605bb0730693dfd9cb14cc6e1429 Mon Sep 17 00:00:00 2001 From: Yuri Gorshenin Date: Tue, 8 Dec 2015 19:25:44 +0300 Subject: [PATCH] [search] Implemented instersection of BUILDINGs with STREETs. --- base/base.pro | 2 +- base/base_tests/base_tests.pro | 1 + base/base_tests/stl_helpers_test.cpp | 23 +++++++ base/stl_helpers.hpp | 43 ++++++++++++ base/vector_operations.hpp | 14 ---- generator/feature_builder.cpp | 7 ++ generator/feature_builder.hpp | 4 ++ generator/search_index_builder.cpp | 4 +- search/mwm_traits.cpp | 50 ++++++++++++++ search/mwm_traits.hpp | 57 ++++++++++++++++ search/retrieval.cpp | 3 +- search/reverse_geocoder.cpp | 68 +++++++------------ search/reverse_geocoder.hpp | 13 ++-- search/search.pro | 4 ++ search/search_index_values.hpp | 52 -------------- .../search_query_v2_test.cpp | 38 +++++++++++ search/search_query.cpp | 29 ++++---- search/search_query.hpp | 1 + search/search_tests_support/test_feature.cpp | 48 +++++++++++++ search/search_tests_support/test_feature.hpp | 20 ++++++ search/v2/features_layer.cpp | 7 +- search/v2/features_layer.hpp | 3 + search/v2/features_layer_matcher.cpp | 11 ++- search/v2/features_layer_matcher.hpp | 67 ++++++++++++++---- search/v2/features_layer_path_finder.cpp | 20 +++--- search/v2/features_layer_path_finder.hpp | 8 +-- search/v2/geocoder.cpp | 43 +++++++----- search/v2/geocoder.hpp | 7 +- search/v2/house_numbers_matcher.cpp | 15 ++-- search/v2/house_numbers_matcher.hpp | 3 + search/v2/house_to_street_table.cpp | 66 ++++++++++++++++++ search/v2/house_to_street_table.hpp | 25 +++++++ search/v2/search_query_v2.cpp | 1 + search/v2/street_vicinity_loader.hpp | 30 +++++++- 34 files changed, 597 insertions(+), 190 deletions(-) create mode 100644 base/base_tests/stl_helpers_test.cpp create mode 100644 base/stl_helpers.hpp delete mode 100644 base/vector_operations.hpp create mode 100644 search/mwm_traits.cpp create mode 100644 search/mwm_traits.hpp create mode 100644 search/v2/house_to_street_table.cpp create mode 100644 search/v2/house_to_street_table.hpp diff --git a/base/base.pro b/base/base.pro index c1342b06d7..3d85e27022 100644 --- a/base/base.pro +++ b/base/base.pro @@ -60,6 +60,7 @@ HEADERS += \ stats.hpp \ std_serialization.hpp \ stl_add.hpp \ + stl_helpers.hpp \ stl_iterator.hpp \ string_format.hpp \ string_utils.hpp \ @@ -74,5 +75,4 @@ HEADERS += \ threaded_priority_queue.hpp \ timegm.hpp \ timer.hpp \ - vector_operations.hpp \ worker_thread.hpp \ diff --git a/base/base_tests/base_tests.pro b/base/base_tests/base_tests.pro index 3635c684d4..6f4a12b939 100644 --- a/base/base_tests/base_tests.pro +++ b/base/base_tests/base_tests.pro @@ -28,6 +28,7 @@ SOURCES += \ rolling_hash_test.cpp \ scope_guard_test.cpp \ stl_add_test.cpp \ + stl_helpers_test.cpp \ string_format_test.cpp \ string_utils_test.cpp \ sunrise_sunset_test.cpp \ diff --git a/base/base_tests/stl_helpers_test.cpp b/base/base_tests/stl_helpers_test.cpp new file mode 100644 index 0000000000..00b59ab302 --- /dev/null +++ b/base/base_tests/stl_helpers_test.cpp @@ -0,0 +1,23 @@ +#include "testing/testing.hpp" + +#include "base/stl_helpers.hpp" + +#include "std/algorithm.hpp" +#include "std/utility.hpp" +#include "std/vector.hpp" + +UNIT_TEST(CompareBy_Smoke) +{ + vector> v = {{2, 2}, {0, 4}, {3, 1}, {4, 0}, {1, 3}}; + sort(v.begin(), v.end(), my::CompareBy(&pair::first)); + for (size_t i = 0; i < v.size(); ++i) + TEST_EQUAL(i, v[i].first, ()); + + vector const *> pv; + for (auto const & p : v) + pv.push_back(&p); + + sort(pv.begin(), pv.end(), my::CompareBy(&pair::second)); + for (size_t i = 0; i < pv.size(); ++i) + TEST_EQUAL(i, pv[i]->second, ()); +} diff --git a/base/stl_helpers.hpp b/base/stl_helpers.hpp new file mode 100644 index 0000000000..7dcc4f5beb --- /dev/null +++ b/base/stl_helpers.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "std/algorithm.hpp" +#include "std/vector.hpp" + +namespace my +{ +namespace impl +{ +template +struct Comparer +{ + Comparer(T(C::*p)) : p_(p) {} + + inline bool operator()(C const & lhs, C const & rhs) const { return lhs.*p_ < rhs.*p_; } + + inline bool operator()(C const * const lhs, C const * const rhs) const + { + return lhs->*p_ < rhs->*p_; + } + + T(C::*p_); +}; +} // namespace impl + +// Sorts and removes duplicate entries from |v|. +template +void SortUnique(std::vector & v) +{ + sort(v.begin(), v.end()); + v.erase(unique(v.begin(), v.end()), v.end()); +} + +// Creates a comparer being able to compare two instances of class C +// (given by reference or pointer) by a field of C. For example, to +// create comparer that is able to compare pairs of ints by second +// component, it's enough to call CompareBy(&pair::second). +template +impl::Comparer CompareBy(T(C::*p)) +{ + return impl::Comparer(p); +} +} // namespace my diff --git a/base/vector_operations.hpp b/base/vector_operations.hpp deleted file mode 100644 index 878b4f85cb..0000000000 --- a/base/vector_operations.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "std/algorithm.hpp" -#include "std/vector.hpp" - -namespace my -{ -template -void SortUnique(std::vector & v) -{ - sort(v.begin(), v.end()); - v.erase(unique(v.begin(), v.end()), v.end()); -} -} // namespace my diff --git a/generator/feature_builder.cpp b/generator/feature_builder.cpp index c3836c945b..bbb2497891 100644 --- a/generator/feature_builder.cpp +++ b/generator/feature_builder.cpp @@ -75,6 +75,13 @@ void FeatureBuilder1::SetRank(uint8_t rank) m_params.rank = rank; } +void FeatureBuilder1::AddHouseNumber(string const & houseNumber) +{ + m_params.AddHouseNumber(houseNumber); +} + +void FeatureBuilder1::AddStreet(string const & streetName) { m_params.AddStreet(streetName); } + void FeatureBuilder1::AddPoint(m2::PointD const & p) { m_polygons.front().push_back(p); diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp index 43716303e2..084a40894c 100644 --- a/generator/feature_builder.hpp +++ b/generator/feature_builder.hpp @@ -33,6 +33,10 @@ public: void SetRank(uint8_t rank); + void AddHouseNumber(string const & houseNumber); + + void AddStreet(string const & streetName); + /// Add point to geometry. void AddPoint(m2::PointD const & p); diff --git a/generator/search_index_builder.cpp b/generator/search_index_builder.cpp index 57a7245c02..a8250249c1 100644 --- a/generator/search_index_builder.cpp +++ b/generator/search_index_builder.cpp @@ -310,9 +310,9 @@ void BuildAddressTable(FilesContainerR & container, Writer & writer) using TStreet = search::ReverseGeocoder::Street; vector streets; - rgc.GetNearbyStreets(ft, street, streets); + rgc.GetNearbyStreets(ft, streets); - ind = rgc.GetMatchedStreetIndex(streets); + ind = rgc.GetMatchedStreetIndex(street, streets); if (ind == streets.size()) { ++missing; diff --git a/search/mwm_traits.cpp b/search/mwm_traits.cpp new file mode 100644 index 0000000000..c9f3b10d37 --- /dev/null +++ b/search/mwm_traits.cpp @@ -0,0 +1,50 @@ +#include "search/mwm_traits.hpp" + +#include "base/logging.hpp" + +namespace search +{ +MwmTraits::MwmTraits(version::Format versionFormat) : m_versionFormat(versionFormat) {} + +MwmTraits::SearchIndexFormat MwmTraits::GetSearchIndexFormat() const +{ + if (m_versionFormat < version::v7) + return SearchIndexFormat::FeaturesWithRankAndCenter; + if (m_versionFormat == version::v7) + return SearchIndexFormat::CompressedBitVector; + + LOG(LWARNING, ("Unknown search index format.")); + return SearchIndexFormat::Unknown; +} + +MwmTraits::HouseToStreetTableFormat MwmTraits::GetHouseToStreetTableFormat() const +{ + if (m_versionFormat < version::v7) + return HouseToStreetTableFormat::Unknown; + return HouseToStreetTableFormat::Fixed3BitsDDVector; +} + +string DebugPrint(MwmTraits::SearchIndexFormat format) +{ + switch (format) + { + case MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter: + return "FeaturesWithRankAndCenter"; + case MwmTraits::SearchIndexFormat::CompressedBitVector: + return "CompressedBitVector"; + case MwmTraits::SearchIndexFormat::Unknown: + return "Unknown"; + } +} + +string DebugPrint(MwmTraits::HouseToStreetTableFormat format) +{ + switch (format) + { + case MwmTraits::HouseToStreetTableFormat::Fixed3BitsDDVector: + return "Fixed3BitsDDVector"; + case MwmTraits::HouseToStreetTableFormat::Unknown: + return "Unknown"; + } +} +} // namespace search diff --git a/search/mwm_traits.hpp b/search/mwm_traits.hpp new file mode 100644 index 0000000000..eb90a87e9e --- /dev/null +++ b/search/mwm_traits.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "platform/mwm_version.hpp" + +#include "std/string.hpp" + +namespace search +{ +// This is a wrapper around mwm's version. Allows users to get +// information about versions of some data structures in mwm. +class MwmTraits +{ +public: + enum class SearchIndexFormat + { + // A list of features with their ranks and centers + // is stored behind every node of the search trie. + // This format corresponds to ValueList. + FeaturesWithRankAndCenter, + + // A compressed bit vector of feature indices is + // stored behind every node of the search trie. + // This format corresponds to ValueList. + CompressedBitVector, + + // The format of the search index is unknown. Most + // likely, an error has occured. + Unknown + }; + + enum class HouseToStreetTableFormat + { + // An array of elements where i-th element is an index of a street + // in a vector returned by ReverseGeocoder::GetNearbyStreets() for + // the i-th feature. Each element normally fits into 3 bits, but + // there can be exceptions, and these exceptions are stored in a + // separate table. See ReverseGeocoder and FixedBitsDDVector for + // details. + Fixed3BitsDDVector, + + // The format of relation is unknown. Most likely, an error has occured. + Unknown + }; + + MwmTraits(version::Format versionFormat); + + SearchIndexFormat GetSearchIndexFormat() const; + + HouseToStreetTableFormat GetHouseToStreetTableFormat() const; + +private: + version::Format m_versionFormat; +}; + +string DebugPrint(MwmTraits::SearchIndexFormat format); +string DebugPrint(MwmTraits::HouseToStreetTableFormat format); +} // namespace search diff --git a/search/retrieval.cpp b/search/retrieval.cpp index 25e74fb3b6..ec5924206b 100644 --- a/search/retrieval.cpp +++ b/search/retrieval.cpp @@ -2,6 +2,7 @@ #include "feature_offset_match.hpp" #include "interval_set.hpp" +#include "mwm_traits.hpp" #include "search_index_values.hpp" #include "search_trie.hpp" @@ -17,7 +18,7 @@ #include "coding/reader_wrapper.hpp" #include "base/assert.hpp" -#include "base/vector_operations.hpp" +#include "base/stl_helpers.hpp" #include "std/algorithm.hpp" #include "std/cmath.hpp" diff --git a/search/reverse_geocoder.cpp b/search/reverse_geocoder.cpp index 1ee336b737..a6eac6e0cc 100644 --- a/search/reverse_geocoder.cpp +++ b/search/reverse_geocoder.cpp @@ -7,18 +7,16 @@ #include "indexer/index.hpp" #include "indexer/scales.hpp" +#include "base/stl_helpers.hpp" namespace search { - namespace { - double constexpr kLookupRadiusM = 500.0; size_t constexpr kMaxStreetIndex = 16; size_t constexpr kSimilarityThresholdPercent = 10; - /// @todo Need to check projection here? double CalculateMinDistance(FeatureType const & ft, m2::PointD const & pt) { @@ -34,18 +32,15 @@ double CalculateMinDistance(FeatureType const & ft, m2::PointD const & pt) return res; } - } // namespace -template -void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, TCompare comp, - vector & streets) +void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector & streets) { m2::PointD const & center = feature::GetCenter(addrFt); m2::RectD const rect = MercatorBounds::RectByCenterXYAndSizeInMeters( center, kLookupRadiusM); - auto const fn = [&](FeatureType const & ft) + auto const addStreet = [&](FeatureType const & ft) { if (ft.GetFeatureType() != feature::GEOM_LINE) return; @@ -59,59 +54,48 @@ void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, TCompare comp return; ASSERT(!name.empty(), ()); - streets.push_back({ft.GetID(), CalculateMinDistance(ft, center), comp(name)}); + streets.push_back({ft.GetID(), CalculateMinDistance(ft, center), name}); }; - m_index.ForEachInRect(fn, rect, scales::GetUpperScale()); - - sort(streets.begin(), streets.end(), [](Street const & s1, Street const & s2) - { - return s1.m_distance < s2.m_distance; - }); + m_index.ForEachInRect(addStreet, rect, scales::GetUpperScale()); + sort(streets.begin(), streets.end(), my::CompareBy(&Street::m_distanceMeters)); } -void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, string const & keyName, - vector & streets) +// static +size_t ReverseGeocoder::GetMatchedStreetIndex(string const & keyName, + vector const & streets) { - strings::UniString const uniKey1 = strings::MakeUniString(keyName); + strings::UniString const expected = strings::MakeUniString(keyName); - GetNearbyStreets(addrFt, [&uniKey1](string const & name) -> pair - { - string key; - search::GetStreetNameAsKey(name, key); - strings::UniString const uniKey2 = strings::MakeUniString(key); - /// @todo Transforming street name into street name key may produce empty strings. - //ASSERT(!uniKey2.empty(), ()); - - return { strings::EditDistance(uniKey1.begin(), uniKey1.end(), uniKey2.begin(), uniKey2.end()), - uniKey2.size() }; - }, streets); -} - -size_t ReverseGeocoder::GetMatchedStreetIndex(vector const & streets) -{ // Do limit possible return values. size_t const count = min(streets.size(), kMaxStreetIndex); // Find the exact match or the best match in kSimilarityTresholdPercent limit. - size_t res = count; + size_t result = count; size_t minPercent = kSimilarityThresholdPercent + 1; for (size_t i = 0; i < count; ++i) { - if (streets[i].m_editDistance.first == 0) + string key; + search::GetStreetNameAsKey(streets[i].m_name, key); + strings::UniString const actual = strings::MakeUniString(key); + + size_t const editDistance = + strings::EditDistance(expected.begin(), expected.end(), actual.begin(), actual.end()); + + if (editDistance == 0) return i; - if (streets[i].m_editDistance.second == 0) + + if (actual.empty()) continue; - size_t const p = streets[i].m_editDistance.first * 100 / streets[i].m_editDistance.second; - if (p < minPercent) + size_t const percent = editDistance * 100 / actual.size(); + if (percent < minPercent) { - res = i; - minPercent = p; + result = i; + minPercent = percent; } } - return (res < count ? res : streets.size()); + return (result < count ? result : streets.size()); } - } // namespace search diff --git a/search/reverse_geocoder.hpp b/search/reverse_geocoder.hpp index 8d1065ac1d..6e69f4ce6e 100644 --- a/search/reverse_geocoder.hpp +++ b/search/reverse_geocoder.hpp @@ -23,18 +23,13 @@ public: struct Street { FeatureID m_id; - - /// Min distance to the street in meters. - double m_distance; - - /// first - edit distance between actual street name and passed key name. - /// second - length of the actual street name. - pair m_editDistance; + double m_distanceMeters; + string m_name; }; - void GetNearbyStreets(FeatureType const & ft, string const & keyName, vector & streets); + void GetNearbyStreets(FeatureType const & addrFt, vector & streets); - static size_t GetMatchedStreetIndex(vector const & streets); + static size_t GetMatchedStreetIndex(string const & keyName, vector const & streets); private: template diff --git a/search/search.pro b/search/search.pro index 5db34924d4..ca965ad112 100644 --- a/search/search.pro +++ b/search/search.pro @@ -24,6 +24,7 @@ HEADERS += \ latlon_match.hpp \ locality.hpp \ locality_finder.hpp \ + mwm_traits.hpp \ params.hpp \ projection_on_street.hpp \ query_saver.hpp \ @@ -47,6 +48,7 @@ HEADERS += \ v2/features_layer_path_finder.hpp \ v2/geocoder.hpp \ v2/house_numbers_matcher.hpp \ + v2/house_to_street_table.hpp \ v2/search_model.hpp \ v2/search_query_v2.hpp \ v2/street_vicinity_loader.hpp \ @@ -63,6 +65,7 @@ SOURCES += \ latlon_match.cpp \ locality.cpp \ locality_finder.cpp \ + mwm_traits.cpp \ params.cpp \ projection_on_street.cpp \ query_saver.cpp \ @@ -80,6 +83,7 @@ SOURCES += \ v2/features_layer_path_finder.cpp \ v2/geocoder.cpp \ v2/house_numbers_matcher.cpp \ + v2/house_to_street_table.cpp \ v2/search_model.cpp \ v2/search_query_v2.cpp \ v2/street_vicinity_loader.cpp \ diff --git a/search/search_index_values.hpp b/search/search_index_values.hpp index c33ae674a7..68e02dec32 100644 --- a/search/search_index_values.hpp +++ b/search/search_index_values.hpp @@ -7,8 +7,6 @@ #include "indexer/coding_params.hpp" #include "indexer/geometry_serialization.hpp" -#include "platform/mwm_version.hpp" - #include "base/assert.hpp" #include "base/logging.hpp" @@ -281,53 +279,3 @@ public: private: vector m_values; }; - -// This is a wrapper around mwm's version. -// Search index does not have a separate version and -// derives its format from the version of its mwm. -// For now, the class provides a more readable way to -// find out what is stored in the search index than by -// staring at the mwm version number. Similar functionality -// may be added to it later. -class MwmTraits -{ -public: - enum class SearchIndexFormat - { - // A list of features with their ranks and centers - // is stored behind every node of the search trie. - // This format corresponds to ValueList. - FeaturesWithRankAndCenter, - - // A compressed bit vector of feature indices is - // stored behind every node of the search trie. - // This format corresponds to ValueList. - CompressedBitVector, - - // The format of the search index is unknown. Most - // likely, an error has occured. - Unknown - }; - - MwmTraits(version::Format versionFormat) - { - if (versionFormat < version::v7) - { - m_format = SearchIndexFormat::FeaturesWithRankAndCenter; - } - else if (versionFormat == version::v7) - { - m_format = SearchIndexFormat::CompressedBitVector; - } - else - { - LOG(LWARNING, ("Unknown search index format.")); - m_format = SearchIndexFormat::Unknown; - } - } - - SearchIndexFormat GetSearchIndexFormat() const { return m_format; } - -private: - SearchIndexFormat m_format; -}; diff --git a/search/search_integration_tests/search_query_v2_test.cpp b/search/search_integration_tests/search_query_v2_test.cpp index d7ccb754a2..017536bba7 100644 --- a/search/search_integration_tests/search_query_v2_test.cpp +++ b/search/search_integration_tests/search_query_v2_test.cpp @@ -75,6 +75,15 @@ UNIT_TEST(SearchQueryV2_Smoke) auto const feynmanStreet = make_shared( vector{m2::PointD(9.999, 9.999), m2::PointD(10, 10), m2::PointD(10.001, 10.001)}, "Feynman street", "en"); + auto const bohrStreet = make_shared( + vector{m2::PointD(9.999, 10.001), m2::PointD(10, 10), m2::PointD(10.001, 9.999)}, + "Bohr street", "en"); + auto const feynmanHouse = make_shared(m2::PointD(10, 10), "Feynman house", + "1 unit 1", *feynmanStreet, "en"); + auto const bohrHouse = + make_shared(m2::PointD(10, 10), "Bohr house", "1 unit 1", *bohrStreet, "en"); + auto const gilbertHouse = make_shared(m2::PointD(10.0005, 10.0005), "Gilbert house", + "1 unit 2", *bohrStreet, "en"); { TestMwmBuilder builder(map, feature::DataHeader::country); @@ -85,6 +94,10 @@ UNIT_TEST(SearchQueryV2_Smoke) builder.Add(*quantumTeleport2); builder.Add(*quantumCafe); builder.Add(*feynmanStreet); + builder.Add(*bohrStreet); + builder.Add(*feynmanHouse); + builder.Add(*bohrHouse); + builder.Add(*gilbertHouse); } auto const regResult = engine.RegisterMap(map); @@ -130,4 +143,29 @@ UNIT_TEST(SearchQueryV2_Smoke) make_shared(regResult.first, quantumTeleport2)}; TEST(MatchResults(engine, rules, request.Results()), ()); } + + { + TestSearchRequest request(engine, "feynman street 1", "en", search::SearchParams::ALL, + viewport); + request.Wait(); + vector> rules = { + make_shared(regResult.first, feynmanHouse)}; + TEST(MatchResults(engine, rules, request.Results()), ()); + } + + { + TestSearchRequest request(engine, "bohr street 1", "en", search::SearchParams::ALL, viewport); + request.Wait(); + vector> rules = { + make_shared(regResult.first, bohrHouse), + make_shared(regResult.first, gilbertHouse)}; + TEST(MatchResults(engine, rules, request.Results()), ()); + } + + { + TestSearchRequest request(engine, "bohr street 1 unit 3", "en", search::SearchParams::ALL, + viewport); + request.Wait(); + TEST_EQUAL(0, request.Results().size(), ()); + } } diff --git a/search/search_query.cpp b/search/search_query.cpp index 4dd445ee32..d25a393b7f 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -7,6 +7,7 @@ #include "search/indexed_value.hpp" #include "search/latlon_match.hpp" #include "search/locality.hpp" +#include "search/mwm_traits.hpp" #include "search/region.hpp" #include "search/search_common.hpp" #include "search/search_delimiters.hpp" @@ -222,6 +223,7 @@ Query::Query(Index & index, CategoriesHolder const & categories, vector , m_locality(&index) #endif , m_worldSearch(true) + , m_keepHouseNumberInQuery(false) { // Results queue's initialization. static_assert(kQueuesCount == ARRAY_SIZE(g_arrCompare1), ""); @@ -497,24 +499,27 @@ void Query::SetQuery(string const & query) // Find most suitable token for house number. #ifdef HOUSE_SEARCH_TEST - int houseInd = static_cast(m_tokens.size()) - 1; - while (houseInd >= 0) + if (!m_keepHouseNumberInQuery) { - if (feature::IsHouseNumberDeepCheck(m_tokens[houseInd])) + int houseInd = static_cast(m_tokens.size()) - 1; + while (houseInd >= 0) { - if (m_tokens.size() > 1) + if (feature::IsHouseNumberDeepCheck(m_tokens[houseInd])) { - m_house.swap(m_tokens[houseInd]); - m_tokens[houseInd].swap(m_tokens.back()); - m_tokens.pop_back(); + if (m_tokens.size() > 1) + { + m_house.swap(m_tokens[houseInd]); + m_tokens[houseInd].swap(m_tokens.back()); + m_tokens.pop_back(); + } + break; } - break; + --houseInd; } - --houseInd; - } - // do check for prefix if last token is not a house number - checkPrefix = m_house.empty() || houseInd < m_tokens.size(); + // do check for prefix if last token is not a house number + checkPrefix = m_house.empty() || houseInd < m_tokens.size(); + } #endif // Assign prefix with last parsed token. diff --git a/search/search_query.hpp b/search/search_query.hpp index dddbf59104..ccfa87262f 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -283,6 +283,7 @@ protected: }; TQueue m_results[kQueuesCount]; size_t m_queuesCount; + bool m_keepHouseNumberInQuery; //@} }; diff --git a/search/search_tests_support/test_feature.cpp b/search/search_tests_support/test_feature.cpp index a6bd3143e8..a4ec7a21ad 100644 --- a/search/search_tests_support/test_feature.cpp +++ b/search/search_tests_support/test_feature.cpp @@ -4,6 +4,7 @@ #include "indexer/classificator.hpp" #include "indexer/feature.hpp" +#include "indexer/ftypes_matcher.hpp" #include "coding/multilang_utf8_string.hpp" @@ -86,6 +87,8 @@ TestStreet::TestStreet(vector const & points, string const & name, s void TestStreet::Serialize(FeatureBuilder1 & fb) const { CHECK(fb.AddName(m_lang, m_name), ("Can't set feature name:", m_name, "(", m_lang, ")")); + if (m_lang != "default") + CHECK(fb.AddName("default", m_name), ("Can't set feature name:", m_name, "( default ")); auto const & classificator = classif(); fb.SetType(classificator.GetTypeByPath({"highway", "living_street"})); @@ -102,6 +105,51 @@ string TestStreet::ToString() const return os.str(); } +// TestBuilding ------------------------------------------------------------------------------------ +TestBuilding::TestBuilding(m2::PointD const & center, string const & name, + string const & houseNumber, string const & lang) + : TestFeature(center, name, lang), m_houseNumber(houseNumber) +{ +} + +TestBuilding::TestBuilding(m2::PointD const & center, string const & name, + string const & houseNumber, TestStreet const & street, + string const & lang) + : TestFeature(center, name, lang) + , m_houseNumber(houseNumber) + , m_streetName(street.GetName()) +{ +} + +void TestBuilding::Serialize(FeatureBuilder1 & fb) const +{ + TestFeature::Serialize(fb); + fb.AddHouseNumber(m_houseNumber); + if (!m_streetName.empty()) + fb.AddStreet(m_streetName); + + auto const & classificator = classif(); + fb.SetType(classificator.GetTypeByPath({"building"})); + + fb.PreSerialize(); +} + +bool TestBuilding::Matches(FeatureType const & feature) const +{ + static ftypes::IsBuildingChecker const & checker = ftypes::IsBuildingChecker::Instance(); + if (!checker(feature)) + return false; + return TestFeature::Matches(feature) && m_houseNumber == feature.GetHouseNumber(); +} + +string TestBuilding::ToString() const +{ + ostringstream os; + os << "TestBuilding [" << m_name << ", " << m_houseNumber << ", " << m_lang << ", " + << DebugPrint(m_center) << "]"; + return os.str(); +} + string DebugPrint(TestFeature const & feature) { return feature.ToString(); } } // namespace tests_support } // namespace search diff --git a/search/search_tests_support/test_feature.hpp b/search/search_tests_support/test_feature.hpp index 88eef792c1..3588d58991 100644 --- a/search/search_tests_support/test_feature.hpp +++ b/search/search_tests_support/test_feature.hpp @@ -21,6 +21,8 @@ public: virtual bool Matches(FeatureType const & feature) const; virtual string ToString() const = 0; + inline string const & GetName() const { return m_name; } + protected: TestFeature(string const & name, string const & lang); TestFeature(m2::PointD const & center, string const & name, string const & lang); @@ -66,6 +68,24 @@ private: vector m_points; }; +class TestBuilding : public TestFeature +{ +public: + TestBuilding(m2::PointD const & center, string const & name, string const & houseNumber, + string const & lang); + TestBuilding(m2::PointD const & center, string const & name, string const & houseNumber, + TestStreet const & street, string const & lang); + + // TestFeature overrides: + void Serialize(FeatureBuilder1 & fb) const override; + bool Matches(FeatureType const & feature) const; + string ToString() const override; + +protected: + string const m_houseNumber; + string const m_streetName; +}; + string DebugPrint(TestFeature const & feature); } // namespace tests_support } // namespace search diff --git a/search/v2/features_layer.cpp b/search/v2/features_layer.cpp index 7bd2bf60c3..4a7dbcb776 100644 --- a/search/v2/features_layer.cpp +++ b/search/v2/features_layer.cpp @@ -13,6 +13,7 @@ FeaturesLayer::FeaturesLayer() { Clear(); } void FeaturesLayer::Clear() { m_sortedFeatures.clear(); + m_subQuery.clear(); m_startToken = 0; m_endToken = 0; m_type = SearchModel::SEARCH_TYPE_COUNT; @@ -21,9 +22,9 @@ void FeaturesLayer::Clear() string DebugPrint(FeaturesLayer const & layer) { ostringstream os; - os << "FeaturesLayer [ m_sortedFeatures: " << ::DebugPrint(layer.m_sortedFeatures) - << ", m_startToken: " << layer.m_startToken << ", m_endToken: " << layer.m_endToken - << ", m_type: " << DebugPrint(layer.m_type) << " ]"; + os << "FeaturesLayer [ size of m_sortedFeatures: " << layer.m_sortedFeatures.size() + << ", m_subQuery: " << layer.m_subQuery << ", m_startToken: " << layer.m_startToken + << ", m_endToken: " << layer.m_endToken << ", m_type: " << DebugPrint(layer.m_type) << " ]"; return os.str(); } } // namespace v2 diff --git a/search/v2/features_layer.hpp b/search/v2/features_layer.hpp index 5beaccb6e7..da8c1c8412 100644 --- a/search/v2/features_layer.hpp +++ b/search/v2/features_layer.hpp @@ -18,6 +18,9 @@ struct FeaturesLayer void Clear(); vector m_sortedFeatures; + + string m_subQuery; + size_t m_startToken; size_t m_endToken; SearchModel::SearchType m_type; diff --git a/search/v2/features_layer_matcher.cpp b/search/v2/features_layer_matcher.cpp index b6f23693e5..2ec682c6ee 100644 --- a/search/v2/features_layer_matcher.cpp +++ b/search/v2/features_layer_matcher.cpp @@ -4,15 +4,22 @@ #include "indexer/scales.hpp" +#include "base/assert.hpp" + namespace search { namespace v2 { -FeaturesLayerMatcher::FeaturesLayerMatcher(MwmValue & value, FeaturesVector const & featuresVector) - : m_featuresVector(featuresVector) +FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmSet::MwmId const & mwmId, + MwmValue & value, FeaturesVector const & featuresVector) + : m_mwmId(mwmId) + , m_reverseGeocoder(index) + , m_houseToStreetTable(HouseToStreetTable::Load(value)) + , m_featuresVector(featuresVector) , m_loader(value, featuresVector, scales::GetUpperScale(), ProjectionOnStreetCalculator::kDefaultMaxDistMeters) { + ASSERT(m_houseToStreetTable.get(), ("Can't load HouseToStreetTable")); } } // namespace v2 } // namespace search diff --git a/search/v2/features_layer_matcher.hpp b/search/v2/features_layer_matcher.hpp index c6e5147c89..94f55c5074 100644 --- a/search/v2/features_layer_matcher.hpp +++ b/search/v2/features_layer_matcher.hpp @@ -1,6 +1,9 @@ #pragma once +#include "search/reverse_geocoder.hpp" #include "search/v2/features_layer.hpp" +#include "search/v2/house_numbers_matcher.hpp" +#include "search/v2/house_to_street_table.hpp" #include "search/v2/search_model.hpp" #include "search/v2/street_vicinity_loader.hpp" @@ -8,17 +11,20 @@ #include "indexer/feature_algo.hpp" #include "indexer/features_vector.hpp" #include "indexer/ftypes_matcher.hpp" +#include "indexer/mwm_set.hpp" #include "geometry/mercator.hpp" #include "geometry/point2d.hpp" #include "geometry/rect2d.hpp" #include "base/macros.hpp" +#include "base/stl_helpers.hpp" #include "std/algorithm.hpp" #include "std/bind.hpp" #include "std/vector.hpp" +class Index; class MwmValue; namespace search @@ -28,10 +34,11 @@ namespace v2 class FeaturesLayerMatcher { public: - FeaturesLayerMatcher(MwmValue & value, FeaturesVector const & featuresVector); + FeaturesLayerMatcher(Index & index, MwmSet::MwmId const & mwmId, MwmValue & value, + FeaturesVector const & featuresVector); template - void Match(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn) + void Match(FeaturesLayer & child, FeaturesLayer & parent, TFn && fn) { if (child.m_type >= parent.m_type) return; @@ -67,7 +74,7 @@ public: for (size_t i = 0; i < child.m_sortedFeatures.size(); ++i) { if (parentRects[j].IsPointInside(childCenters[i])) - fn(i, j); + fn(child.m_sortedFeatures[i], parent.m_sortedFeatures[j]); } } } @@ -79,24 +86,60 @@ private: ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_POI, ()); ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ()); - for (size_t j = 0; j < parent.m_sortedFeatures.size(); ++j) - { - uint32_t streetId = parent.m_sortedFeatures[j]; - m_loader.ForEachInVicinity(streetId, child.m_sortedFeatures, bind(fn, _1, j)); - } + for (uint32_t streetId : parent.m_sortedFeatures) + m_loader.ForEachInVicinity(streetId, child.m_sortedFeatures, bind(fn, _1, streetId)); } template - void MatchBuildingsWithStreets(FeaturesLayer const & child, FeaturesLayer const & parent, - TFn && fn) + void MatchBuildingsWithStreets(FeaturesLayer & child, FeaturesLayer const & parent, TFn && fn) { + // child.m_sortedFeatures contains only buildings matched by name, + // not by house number. So, we need to add to + // child.m_sortedFeatures all buildings match by house number + // here. + + auto const & checker = ftypes::IsBuildingChecker::Instance(); + ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_BUILDING, ()); ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ()); - // TODO (@y): implement this - NOTIMPLEMENTED(); + vector queryTokens; + NormalizeHouseNumber(child.m_subQuery, queryTokens); + + auto filter = [&](uint32_t id, FeatureType & feature) -> bool + { + if (!checker(feature)) + return false; + if (binary_search(child.m_sortedFeatures.begin(), child.m_sortedFeatures.end(), id)) + return true; + return HouseNumbersMatch(feature.GetHouseNumber(), queryTokens); + }; + + vector houseIds; + + auto addEdge = [&](uint32_t houseId, FeatureType & houseFeature, uint32_t streetId) + { + vector streets; + m_reverseGeocoder.GetNearbyStreets(houseFeature, streets); + uint32_t streetIndex = m_houseToStreetTable->Get(houseId); + + if (streetIndex < streets.size() && streets[streetIndex].m_id.m_index == streetId) + { + houseIds.push_back(houseId); + fn(houseId, streetId); + } + }; + + for (uint32_t streetId : parent.m_sortedFeatures) + m_loader.FilterFeaturesInVicinity(streetId, filter, bind(addEdge, _1, _2, streetId)); + + my::SortUnique(houseIds); + child.m_sortedFeatures.swap(houseIds); } + MwmSet::MwmId m_mwmId; + ReverseGeocoder m_reverseGeocoder; + unique_ptr m_houseToStreetTable; FeaturesVector const & m_featuresVector; StreetVicinityLoader m_loader; }; diff --git a/search/v2/features_layer_path_finder.cpp b/search/v2/features_layer_path_finder.cpp index 240bb5ec8a..0e5d26f986 100644 --- a/search/v2/features_layer_path_finder.cpp +++ b/search/v2/features_layer_path_finder.cpp @@ -1,6 +1,6 @@ #include "search/v2/features_layer_path_finder.hpp" -#include "search/v2/features_layer.hpp" +#include "search/v2/features_layer_matcher.hpp" #include "indexer/features_vector.hpp" @@ -8,9 +8,8 @@ namespace search { namespace v2 { -FeaturesLayerPathFinder::FeaturesLayerPathFinder(MwmValue & value, - FeaturesVector const & featuresVector) - : m_matcher(value, featuresVector) +FeaturesLayerPathFinder::FeaturesLayerPathFinder(FeaturesLayerMatcher & matcher) + : m_matcher(matcher) { } @@ -18,13 +17,18 @@ void FeaturesLayerPathFinder::BuildGraph(vector const & layers) { m_graph.clear(); - for (size_t i = 0; i + 1 < layers.size(); ++i) + if (layers.empty()) + return; + + // The order matters here, as we need to intersect BUILDINGs with + // STREETs first, and then POIs with BUILDINGs. + for (size_t i = layers.size() - 1; i != 0; --i) { - auto & child = (*layers[i]); - auto & parent = (*layers[i + 1]); + auto & child = (*layers[i - 1]); + auto & parent = (*layers[i]); auto addEdges = [&](uint32_t from, uint32_t to) { - m_graph[parent.m_sortedFeatures[to]].push_back(child.m_sortedFeatures[from]); + m_graph[to].push_back(from); }; m_matcher.Match(child, parent, addEdges); } diff --git a/search/v2/features_layer_path_finder.hpp b/search/v2/features_layer_path_finder.hpp index c94fb458f6..d2851edb0e 100644 --- a/search/v2/features_layer_path_finder.hpp +++ b/search/v2/features_layer_path_finder.hpp @@ -1,6 +1,6 @@ #pragma once -#include "search/v2/features_layer_matcher.hpp" +#include "search/v2/features_layer.hpp" #include "std/unordered_map.hpp" #include "std/unordered_set.hpp" @@ -13,7 +13,7 @@ namespace search { namespace v2 { -struct FeaturesLayer; +class FeaturesLayerMatcher; class FeaturesLayerPathFinder { @@ -21,7 +21,7 @@ public: using TAdjList = vector; using TLayerGraph = unordered_map; - FeaturesLayerPathFinder(MwmValue & value, FeaturesVector const & featuresVector); + FeaturesLayerPathFinder(FeaturesLayerMatcher & matcher); template void ForEachReachableVertex(vector const & layers, TFn && fn) @@ -47,7 +47,7 @@ private: void Dfs(uint32_t u); - FeaturesLayerMatcher m_matcher; + FeaturesLayerMatcher & m_matcher; TLayerGraph m_graph; unordered_set m_visited; }; diff --git a/search/v2/geocoder.cpp b/search/v2/geocoder.cpp index c3f3d48525..fee93786e7 100644 --- a/search/v2/geocoder.cpp +++ b/search/v2/geocoder.cpp @@ -1,6 +1,7 @@ #include "search/v2/geocoder.hpp" #include "search/retrieval.hpp" +#include "search/v2/features_layer_matcher.hpp" #include "search/v2/features_layer_path_finder.hpp" #include "search/search_delimiters.hpp" #include "search/search_string_utils.hpp" @@ -99,13 +100,16 @@ void Geocoder::Go(vector & results) MY_SCOPE_GUARD(cleanup, [&]() { m_finder.reset(); + m_matcher.reset(); m_loader.reset(); m_cache.clear(); }); - m_loader.reset(new Index::FeaturesLoaderGuard(m_index, m_mwmId)); - m_finder.reset(new FeaturesLayerPathFinder(*m_value, m_loader->GetFeaturesVector())); m_cache.clear(); + m_loader.reset(new Index::FeaturesLoaderGuard(m_index, m_mwmId)); + m_matcher.reset( + new FeaturesLayerMatcher(m_index, m_mwmId, *m_value, m_loader->GetFeaturesVector())); + m_finder.reset(new FeaturesLayerPathFinder(*m_matcher)); DoGeocoding(0 /* curToken */); } @@ -137,6 +141,9 @@ void Geocoder::PrepareParams(size_t curToken, size_t endToken) void Geocoder::DoGeocoding(size_t curToken) { + if (IsCancelled()) + MYTHROW(CancelException, ("Cancelled.")); + if (curToken == m_numTokens) { // All tokens were consumed, find paths through layers, emit @@ -157,14 +164,14 @@ void Geocoder::DoGeocoding(size_t curToken) layer.Clear(); layer.m_startToken = curToken; layer.m_endToken = curToken + n; + JoinQueryTokens(m_params, layer.m_startToken, layer.m_endToken, " " /* sep */, + layer.m_subQuery); } // TODO (@y, @m): as |n| increases, good optimization is to update // |features| incrementally, from [curToken, curToken + n) to // [curToken, curToken + n + 1). auto features = RetrieveAddressFeatures(curToken, curToken + n); - if (!features || features->PopCount() == 0) - continue; vector clusters[SearchModel::SEARCH_TYPE_COUNT]; auto clusterize = [&](uint64_t featureId) @@ -177,12 +184,17 @@ void Geocoder::DoGeocoding(size_t curToken) clusters[searchType].push_back(featureId); }; - coding::CompressedBitVectorEnumerator::ForEach(*features, clusterize); + if (features) + coding::CompressedBitVectorEnumerator::ForEach(*features, clusterize); - bool const looksLikeHouseNumber = LooksLikeHouseNumber(curToken, curToken + n); + bool const looksLikeHouseNumber = feature::IsHouseNumber(m_layers.back().m_subQuery); for (size_t i = 0; i != SearchModel::SEARCH_TYPE_COUNT; ++i) { + // ATTENTION: DO NOT USE layer after recursive calls to + // DoGeocoding(). This may lead to use-after-free. + auto & layer = m_layers.back(); + if (i == SearchModel::SEARCH_TYPE_BUILDING) { if (clusters[i].empty() && !looksLikeHouseNumber) @@ -193,9 +205,13 @@ void Geocoder::DoGeocoding(size_t curToken) continue; } - // ATTENTION: DO NOT USE layer after recursive calls to - // DoGeocoding(). This may lead to use-after-free. - auto & layer = m_layers.back(); + if (i == SearchModel::SEARCH_TYPE_STREET) + { + string key; + GetStreetNameAsKey(layer.m_subQuery, key); + if (key.empty()) + continue; + } layer.m_sortedFeatures.swap(clusters[i]); ASSERT(is_sorted(layer.m_sortedFeatures.begin(), layer.m_sortedFeatures.end()), ()); @@ -237,15 +253,6 @@ bool Geocoder::IsLayerSequenceSane() const return true; } -bool Geocoder::LooksLikeHouseNumber(size_t curToken, size_t endToken) const -{ - string res; - JoinQueryTokens(m_params, curToken, endToken, " " /* sep */, res); - - // TODO (@y): we need to implement a better check here. - return feature::IsHouseNumber(res); -} - void Geocoder::FindPaths() { ASSERT(!m_layers.empty(), ()); diff --git a/search/v2/geocoder.hpp b/search/v2/geocoder.hpp index ac1bc6bcaa..b0ea4769e3 100644 --- a/search/v2/geocoder.hpp +++ b/search/v2/geocoder.hpp @@ -34,6 +34,7 @@ class RankTable; namespace v2 { +class FeaturesLayerMatcher; class FeaturesLayerPathFinder; class SearchModel; @@ -86,9 +87,6 @@ private: // pre-check to cut off unnecessary work. bool IsLayerSequenceSane() const; - // Returns true if [curToken, endToken) subsequence looks like a house number. - bool LooksLikeHouseNumber(size_t curToken, size_t endToken) const; - // Finds all paths through layers and emits reachable features from // the lowest layer. void FindPaths(); @@ -120,6 +118,9 @@ private: // Features loader. unique_ptr m_loader; + // Features matcher for layers intersection. + unique_ptr m_matcher; + // Path finder for interpretations. unique_ptr m_finder; diff --git a/search/v2/house_numbers_matcher.cpp b/search/v2/house_numbers_matcher.cpp index f033bf02e8..e972e129ef 100644 --- a/search/v2/house_numbers_matcher.cpp +++ b/search/v2/house_numbers_matcher.cpp @@ -169,12 +169,20 @@ bool HouseNumbersMatch(string const & houseNumber, string const & query) if (houseNumber == query) return true; - vector houseNumberTokens; - NormalizeHouseNumber(houseNumber, houseNumberTokens); - vector queryTokens; NormalizeHouseNumber(query, queryTokens); + if (!queryTokens.empty()) + sort(queryTokens.begin() + 1, queryTokens.end()); + + return HouseNumbersMatch(houseNumber, queryTokens); +} + +bool HouseNumbersMatch(string const & houseNumber, vector const & queryTokens) +{ + vector houseNumberTokens; + NormalizeHouseNumber(houseNumber, houseNumberTokens); + if (houseNumberTokens.empty() || queryTokens.empty()) return false; @@ -183,7 +191,6 @@ bool HouseNumbersMatch(string const & houseNumber, string const & query) return false; sort(houseNumberTokens.begin() + 1, houseNumberTokens.end()); - sort(queryTokens.begin() + 1, queryTokens.end()); size_t i = 1, j = 1; while (i != houseNumberTokens.size() && j != queryTokens.size()) diff --git a/search/v2/house_numbers_matcher.hpp b/search/v2/house_numbers_matcher.hpp index 7475cfa8a5..a3009b2033 100644 --- a/search/v2/house_numbers_matcher.hpp +++ b/search/v2/house_numbers_matcher.hpp @@ -42,5 +42,8 @@ void NormalizeHouseNumber(string const & s, vector & ts); // Returns true when |query| matches to |houseNumber|. bool HouseNumbersMatch(string const & houseNumber, string const & query); + +// Returns true when |queryTokens| match to |houseNumber|. +bool HouseNumbersMatch(string const & houseNumber, vector const & queryTokens); } // namespace v2 } // namespace search diff --git a/search/v2/house_to_street_table.cpp b/search/v2/house_to_street_table.cpp new file mode 100644 index 0000000000..410943c240 --- /dev/null +++ b/search/v2/house_to_street_table.cpp @@ -0,0 +1,66 @@ +#include "search/v2/house_to_street_table.hpp" + +#include "search/mwm_traits.hpp" + +#include "indexer/index.hpp" + +#include "coding/fixed_bits_ddvector.hpp" +#include "coding/reader.hpp" + +#include "base/assert.hpp" + +#include "defines.hpp" + +namespace search +{ +namespace v2 +{ +namespace +{ +class Fixed3BitsTable : public HouseToStreetTable +{ +public: + using TVector = FixedBitsDDVector<3, ModelReaderPtr>; + + Fixed3BitsTable(MwmValue & value) + : m_vector(TVector::Create(value.m_cont.GetReader(SEARCH_ADDRESS_FILE_TAG))) + { + ASSERT(m_vector.get(), ("Can't instantiate FixedBitsDDVector.")); + } + + uint32_t Get(uint32_t houseId) const override { return m_vector->Get(houseId); } + +private: + unique_ptr m_vector; +}; + +class DummyTable : public HouseToStreetTable +{ +public: + uint32_t Get(uint32_t /* houseId */) const override { return 0; } +}; +} // namespace + +unique_ptr HouseToStreetTable::Load(MwmValue & value) +{ + MwmTraits traits(value.GetMwmVersion().format); + auto const format = traits.GetHouseToStreetTableFormat(); + + unique_ptr result; + + try + { + if (format == MwmTraits::HouseToStreetTableFormat::Fixed3BitsDDVector) + result.reset(new Fixed3BitsTable(value)); + } + catch (Reader::OpenException & e) + { + LOG(LWARNING, (e.Msg())); + } + + if (!result) + result.reset(new DummyTable()); + return result; +} +} // namespace v2 +} // namespace search diff --git a/search/v2/house_to_street_table.hpp b/search/v2/house_to_street_table.hpp new file mode 100644 index 0000000000..7c89bac4ac --- /dev/null +++ b/search/v2/house_to_street_table.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "std/limits.hpp" +#include "std/unique_ptr.hpp" + +class MwmValue; + +namespace search +{ +namespace v2 +{ +class HouseToStreetTable +{ +public: + virtual ~HouseToStreetTable() = default; + + static unique_ptr Load(MwmValue & value); + + // Returns an order number for a street corresponding to |houseId|. + // The order number is an index of a street in a list returned + // by ReverseGeocoder::GetNearbyStreets(). + virtual uint32_t Get(uint32_t houseId) const = 0; +}; +} // namespace v2 +} // namespace search diff --git a/search/v2/search_query_v2.cpp b/search/v2/search_query_v2.cpp index af46a44fc0..0fbbb4d6b2 100644 --- a/search/v2/search_query_v2.cpp +++ b/search/v2/search_query_v2.cpp @@ -15,6 +15,7 @@ SearchQueryV2::SearchQueryV2(Index & index, CategoriesHolder const & categories, storage::CountryInfoGetter const & infoGetter) : Query(index, categories, suggests, infoGetter), m_geocoder(index) { + m_keepHouseNumberInQuery = true; } void SearchQueryV2::Reset() diff --git a/search/v2/street_vicinity_loader.hpp b/search/v2/street_vicinity_loader.hpp index 779b864a34..dc2ffdb02c 100644 --- a/search/v2/street_vicinity_loader.hpp +++ b/search/v2/street_vicinity_loader.hpp @@ -3,6 +3,7 @@ #include "search/projection_on_street.hpp" #include "indexer/feature.hpp" +#include "indexer/feature_algo.hpp" #include "indexer/features_vector.hpp" #include "indexer/scale_index.hpp" @@ -53,8 +54,7 @@ public: for (uint32_t id : street.m_features) { // Load center and check projection only when |id| is in |sortedIds|. - auto it = lower_bound(sortedIds.begin(), sortedIds.end(), id); - if (it == sortedIds.end() || *it != id) + if (!binary_search(sortedIds.begin(), sortedIds.end(), id)) continue; FeatureType ft; @@ -62,7 +62,31 @@ public: if (!calculator.GetProjection(ft.GetCenter(), proj)) continue; - fn(distance(sortedIds.begin(), it)); + fn(id); + } + } + + template + void FilterFeaturesInVicinity(uint32_t streetId, TFilter && filter, TFn && fn) + { + Street const & street = GetStreet(streetId); + if (street.IsEmpty()) + return; + + ProjectionOnStreetCalculator calculator(street.m_points, m_offsetMeters); + ProjectionOnStreet proj; + + for (uint32_t id : street.m_features) + { + FeatureType ft; + m_featuresVector.GetByIndex(id, ft); + if (!filter(id, ft)) + continue; + + if (!calculator.GetProjection(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY), proj)) + continue; + + fn(id, ft); } }