From df0bcdcf07d60c48cc6e84d57e7b42c7821e286f Mon Sep 17 00:00:00 2001 From: Yuri Gorshenin Date: Wed, 23 Dec 2015 17:30:47 +0300 Subject: [PATCH] [search] Implemented search in locality. --- .../compressed_bit_vector_test.cpp | 119 ++++++- coding/compressed_bit_vector.cpp | 118 ++++++- coding/compressed_bit_vector.hpp | 8 + search/search.pro | 2 + .../search_query_v2_test.cpp | 57 ++-- search/v2/features_filter.cpp | 66 +--- search/v2/features_filter.hpp | 59 +--- search/v2/features_layer_matcher.cpp | 15 +- search/v2/features_layer_matcher.hpp | 13 +- search/v2/features_layer_path_finder.cpp | 11 +- search/v2/features_layer_path_finder.hpp | 9 +- search/v2/geocoder.cpp | 315 +++++++++++++----- search/v2/geocoder.hpp | 54 +-- search/v2/mwm_context.cpp | 14 + search/v2/mwm_context.hpp | 25 ++ 15 files changed, 619 insertions(+), 266 deletions(-) create mode 100644 search/v2/mwm_context.cpp create mode 100644 search/v2/mwm_context.hpp diff --git a/coding/coding_tests/compressed_bit_vector_test.cpp b/coding/coding_tests/compressed_bit_vector_test.cpp index 42515d5720..b0b800ea5e 100644 --- a/coding/coding_tests/compressed_bit_vector_test.cpp +++ b/coding/coding_tests/compressed_bit_vector_test.cpp @@ -25,6 +25,14 @@ void Subtract(vector & setBits1, vector & setBits2, vector & setBits1, vector & setBits2, vector & result) +{ + sort(setBits1.begin(), setBits1.end()); + sort(setBits2.begin(), setBits2.end()); + set_union(setBits1.begin(), setBits1.end(), setBits2.begin(), setBits2.end(), + back_inserter(result)); +} + template void CheckBinaryOp(TBinaryOp op, vector & setBits1, vector & setBits2, coding::CompressedBitVector const & cbv) @@ -33,14 +41,8 @@ void CheckBinaryOp(TBinaryOp op, vector & setBits1, vector & op(setBits1, setBits2, expected); TEST_EQUAL(expected.size(), cbv.PopCount(), ()); - vector expectedBitmap; - if (!expected.empty()) - expectedBitmap.resize(expected.back() + 1); - for (size_t i = 0; i < expected.size(); ++i) - expectedBitmap[expected[i]] = true; - for (size_t i = 0; i < expectedBitmap.size(); ++i) - TEST_EQUAL(cbv.GetBit(i), expectedBitmap[i], ()); + TEST(cbv.GetBit(expected[i]), ()); } void CheckIntersection(vector & setBits1, vector & setBits2, @@ -54,6 +56,12 @@ void CheckSubtraction(vector & setBits1, vector & setBits2, { CheckBinaryOp(&Subtract, setBits1, setBits2, cbv); } + +void CheckUnion(vector & setBits1, vector & setBits2, + coding::CompressedBitVector const & cbv) +{ + CheckBinaryOp(&Union, setBits1, setBits2, cbv); +} } // namespace UNIT_TEST(CompressedBitVector_Intersect1) @@ -214,6 +222,103 @@ UNIT_TEST(CompressedBitVector_Subtract4) CheckSubtraction(setBits1, setBits2, *cbv3); } +UNIT_TEST(CompressedBitVector_Union_Smoke) +{ + vector setBits1 = {}; + vector setBits2 = {}; + + auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); + auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); + TEST(cbv1.get(), ()); + TEST(cbv2.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv1->GetStorageStrategy(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv2->GetStorageStrategy(), ()); + + auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); + TEST(cbv3.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); + CheckUnion(setBits1, setBits2, *cbv3); +} + +UNIT_TEST(CompressedBitVector_Union1) +{ + vector setBits1 = {}; + vector setBits2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); + auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); + TEST(cbv1.get(), ()); + TEST(cbv2.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv1->GetStorageStrategy(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv2->GetStorageStrategy(), ()); + + auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); + TEST(cbv3.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv3->GetStorageStrategy(), ()); + CheckUnion(setBits1, setBits2, *cbv3); +} + +UNIT_TEST(CompressedBitVector_Union2) +{ + vector setBits1 = {256, 1024}; + vector setBits2 = {0, 32, 64}; + + auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); + auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); + TEST(cbv1.get(), ()); + TEST(cbv2.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv1->GetStorageStrategy(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv2->GetStorageStrategy(), ()); + + auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); + TEST(cbv3.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); + CheckUnion(setBits1, setBits2, *cbv3); +} + +UNIT_TEST(CompressedBitVector_Union3) +{ + vector setBits1 = {0, 1, 2, 3, 4, 5, 6}; + + vector setBits2; + for (int i = 0; i < 256; ++i) + setBits2.push_back(i); + + auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); + auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); + TEST(cbv1.get(), ()); + TEST(cbv2.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv1->GetStorageStrategy(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv2->GetStorageStrategy(), ()); + + auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); + TEST(cbv3.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv3->GetStorageStrategy(), ()); + CheckUnion(setBits1, setBits2, *cbv3); +} + +UNIT_TEST(CompressedBitVector_Union4) +{ + vector setBits1; + for (int i = 0; i < coding::DenseCBV::kBlockSize; ++i) + setBits1.push_back(i); + + vector setBits2 = {1000000000}; + + auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); + auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); + TEST(cbv1.get(), ()); + TEST(cbv2.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv1->GetStorageStrategy(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv2->GetStorageStrategy(), ()); + + auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); + TEST(cbv3.get(), ()); + TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); + + CheckUnion(setBits1, setBits2, *cbv3); +} + UNIT_TEST(CompressedBitVector_SerializationDense) { int const kNumBits = 100; diff --git a/coding/compressed_bit_vector.cpp b/coding/compressed_bit_vector.cpp index 9d9513f11f..78a8c7cfde 100644 --- a/coding/compressed_bit_vector.cpp +++ b/coding/compressed_bit_vector.cpp @@ -118,6 +118,96 @@ struct SubtractOp } }; +struct UnionOp +{ + UnionOp() {} + + unique_ptr operator()(coding::DenseCBV const & a, + coding::DenseCBV const & b) const + { + size_t sizeA = a.NumBitGroups(); + size_t sizeB = b.NumBitGroups(); + + size_t commonSize = min(sizeA, sizeB); + size_t resultSize = max(sizeA, sizeB); + vector resGroups(resultSize); + for (size_t i = 0; i < commonSize; ++i) + resGroups[i] = a.GetBitGroup(i) | b.GetBitGroup(i); + if (a.NumBitGroups() == resultSize) + { + for (size_t i = commonSize; i < resultSize; ++i) + resGroups[i] = a.GetBitGroup(i); + } + else + { + for (size_t i = commonSize; i < resultSize; ++i) + resGroups[i] = b.GetBitGroup(i); + } + return CompressedBitVectorBuilder::FromBitGroups(move(resGroups)); + } + + unique_ptr operator()(coding::DenseCBV const & a, + coding::SparseCBV const & b) const + { + size_t sizeA = a.NumBitGroups(); + size_t sizeB = b.PopCount() == 0 ? 0 : (b.Select(b.PopCount() - 1) + DenseCBV::kBlockSize - 1) / + DenseCBV::kBlockSize; + if (sizeB > sizeA) + { + vector resPos; + auto j = b.Begin(); + auto merge = [&](uint64_t va) + { + while (j < b.End() && *j < va) + { + resPos.push_back(*j); + ++j; + } + resPos.push_back(va); + }; + a.ForEach(merge); + for (; j < b.End(); ++j) + resPos.push_back(*j); + return CompressedBitVectorBuilder::FromBitPositions(move(resPos)); + } + + vector resGroups(sizeA); + + size_t i = 0; + auto j = b.Begin(); + for (; i < sizeA || j < b.End(); ++i) + { + uint64_t const kBitsBegin = i * DenseCBV::kBlockSize; + uint64_t const kBitsEnd = (i + 1) * DenseCBV::kBlockSize; + + uint64_t mask = i < sizeA ? a.GetBitGroup(i) : 0; + for (; j < b.End() && *j < kBitsEnd; ++j) + { + ASSERT_GREATER_OR_EQUAL(*j, kBitsBegin, ()); + mask |= static_cast(1) << (*j - kBitsBegin); + } + + resGroups[i] = mask; + } + + return CompressedBitVectorBuilder::FromBitGroups(move(resGroups)); + } + + unique_ptr operator()(coding::SparseCBV const & a, + coding::DenseCBV const & b) const + { + return operator()(b, a); + } + + unique_ptr operator()(coding::SparseCBV const & a, + coding::SparseCBV const & b) const + { + vector resPos; + set_union(a.Begin(), a.End(), b.Begin(), b.End(), back_inserter(resPos)); + return CompressedBitVectorBuilder::FromBitPositions(move(resPos)); + } +}; + template unique_ptr Apply(TBinaryOp const & op, CompressedBitVector const & lhs, CompressedBitVector const & rhs) @@ -341,15 +431,35 @@ string DebugPrint(CompressedBitVector::StorageStrategy strat) unique_ptr CompressedBitVector::Intersect(CompressedBitVector const & lhs, CompressedBitVector const & rhs) { - static IntersectOp const intersectOp; - return Apply(intersectOp, lhs, rhs); + static IntersectOp const op; + return Apply(op, lhs, rhs); } // static unique_ptr CompressedBitVector::Subtract(CompressedBitVector const & lhs, CompressedBitVector const & rhs) { - static SubtractOp const subtractOp; - return Apply(subtractOp, lhs, rhs); + static SubtractOp const op; + return Apply(op, lhs, rhs); +} + +// static +unique_ptr CompressedBitVector::Union(CompressedBitVector const & lhs, + CompressedBitVector const & rhs) +{ + static UnionOp const op; + return Apply(op, lhs, rhs); +} + +// static +bool CompressedBitVector::IsEmpty(unique_ptr const & cbv) +{ + return !cbv || cbv->PopCount() == 0; +} + +// static +bool CompressedBitVector::IsEmpty(CompressedBitVector const * cbv) +{ + return !cbv || cbv->PopCount() == 0; } } // namespace coding diff --git a/coding/compressed_bit_vector.hpp b/coding/compressed_bit_vector.hpp index 837e35015f..23d7b1c04c 100644 --- a/coding/compressed_bit_vector.hpp +++ b/coding/compressed_bit_vector.hpp @@ -43,6 +43,14 @@ public: static unique_ptr Subtract(CompressedBitVector const & lhs, CompressedBitVector const & rhs); + // Unites two bit vectors. + static unique_ptr Union(CompressedBitVector const & lhs, + CompressedBitVector const & rhs); + + static bool IsEmpty(unique_ptr const & cbv); + + static bool IsEmpty(CompressedBitVector const * cbv); + // Returns the number of set bits (population count). virtual uint64_t PopCount() const = 0; diff --git a/search/search.pro b/search/search.pro index efb91d68cc..1f62e29089 100644 --- a/search/search.pro +++ b/search/search.pro @@ -51,6 +51,7 @@ HEADERS += \ v2/geocoder.hpp \ v2/house_numbers_matcher.hpp \ v2/house_to_street_table.hpp \ + v2/mwm_context.hpp \ v2/rank_table_cache.hpp \ v2/search_model.hpp \ v2/search_query_v2.hpp \ @@ -88,6 +89,7 @@ SOURCES += \ v2/geocoder.cpp \ v2/house_numbers_matcher.cpp \ v2/house_to_street_table.cpp \ + v2/mwm_context.cpp \ v2/rank_table_cache.cpp \ v2/search_model.cpp \ v2/search_query_v2.cpp \ diff --git a/search/search_integration_tests/search_query_v2_test.cpp b/search/search_integration_tests/search_query_v2_test.cpp index fe1f24d4a6..b7c77459f8 100644 --- a/search/search_integration_tests/search_query_v2_test.cpp +++ b/search/search_integration_tests/search_query_v2_test.cpp @@ -50,17 +50,24 @@ UNIT_TEST(SearchQueryV2_Smoke) { classificator::Load(); Platform & platform = GetPlatform(); - platform::LocalCountryFile map(platform.WritableDir(), platform::CountryFile("map"), 0); + platform::LocalCountryFile wonderland(platform.WritableDir(), platform::CountryFile("wonderland"), + 0); + platform::LocalCountryFile testWorld(platform.WritableDir(), platform::CountryFile("testWorld"), + 0); + m2::RectD wonderlandRect(m2::PointD(-1.0, -1.0), m2::PointD(10.1, 10.1)); m2::RectD viewport(m2::PointD(-1.0, -1.0), m2::PointD(1.0, 1.0)); - Cleanup(map); - MY_SCOPE_GUARD(cleanup, [&]() + auto cleanup = [&]() { - Cleanup(map); - }); + Cleanup(wonderland); + Cleanup(testWorld); + }; + + cleanup(); + MY_SCOPE_GUARD(cleanupAtExit, cleanup); vector countries; - countries.emplace_back(map.GetCountryName(), viewport); + countries.emplace_back(wonderland.GetCountryName(), wonderlandRect); TestSearchEngine engine("en", make_unique(countries), make_unique()); @@ -89,7 +96,7 @@ UNIT_TEST(SearchQueryV2_Smoke) "Hilbert house", "1 unit 2", *bohrStreet, "en"); { - TestMwmBuilder builder(map, feature::DataHeader::country); + TestMwmBuilder builder(wonderland, feature::DataHeader::country); builder.Add(*mskCity); builder.Add(*busStop); builder.Add(*tramStop); @@ -103,13 +110,23 @@ UNIT_TEST(SearchQueryV2_Smoke) builder.Add(*hilbertHouse); } - auto const regResult = engine.RegisterMap(map); - TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ()); + { + TestMwmBuilder builder(testWorld, feature::DataHeader::world); + builder.Add(*mskCity); + } + + auto const wonderlandResult = engine.RegisterMap(wonderland); + TEST_EQUAL(wonderlandResult.second, MwmSet::RegResult::Success, ()); + + auto const testWorldResult = engine.RegisterMap(testWorld); + TEST_EQUAL(testWorldResult.second, MwmSet::RegResult::Success, ()); + + auto wonderlandId = wonderlandResult.first; { TestSearchRequest request(engine, "Bus stop", "en", search::SearchParams::ALL, viewport); request.Wait(); - vector> rules = {make_shared(regResult.first, busStop)}; + vector> rules = {make_shared(wonderlandId, busStop)}; TEST(MatchResults(engine, rules, request.Results()), ()); } @@ -117,9 +134,9 @@ UNIT_TEST(SearchQueryV2_Smoke) 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)}; + make_shared(wonderlandId, quantumCafe), + make_shared(wonderlandId, quantumTeleport1), + make_shared(wonderlandId, quantumTeleport2)}; TEST(MatchResults(engine, rules, request.Results()), ()); } @@ -127,8 +144,8 @@ UNIT_TEST(SearchQueryV2_Smoke) TestSearchRequest request(engine, "quantum Moscow ", "en", search::SearchParams::ALL, viewport); request.Wait(); vector> rules = { - make_shared(regResult.first, quantumCafe), - make_shared(regResult.first, quantumTeleport1)}; + make_shared(wonderlandId, quantumCafe), + make_shared(wonderlandId, quantumTeleport1)}; TEST(MatchResults(engine, rules, request.Results()), ()); } @@ -143,7 +160,7 @@ UNIT_TEST(SearchQueryV2_Smoke) viewport); request.Wait(); vector> rules = { - make_shared(regResult.first, quantumTeleport2)}; + make_shared(wonderlandId, quantumTeleport2)}; TEST(MatchResults(engine, rules, request.Results()), ()); } @@ -151,17 +168,15 @@ UNIT_TEST(SearchQueryV2_Smoke) TestSearchRequest request(engine, "feynman street 1", "en", search::SearchParams::ALL, viewport); request.Wait(); - vector> rules = { - make_shared(regResult.first, feynmanHouse)}; + vector> rules = {make_shared(wonderlandId, 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, hilbertHouse)}; + vector> rules = {make_shared(wonderlandId, bohrHouse), + make_shared(wonderlandId, hilbertHouse)}; TEST(MatchResults(engine, rules, request.Results()), ()); } diff --git a/search/v2/features_filter.cpp b/search/v2/features_filter.cpp index 934a98ea5a..10c83e9da8 100644 --- a/search/v2/features_filter.cpp +++ b/search/v2/features_filter.cpp @@ -1,71 +1,27 @@ #include "search/v2/features_filter.hpp" -#include "search/dummy_rank_table.hpp" -#include "search/retrieval.hpp" - -#include "indexer/index.hpp" -#include "indexer/scales.hpp" - namespace search { namespace v2 { -FeaturesFilter::FeaturesFilter(my::Cancellable const & cancellable) - : m_maxNumResults(0) - , m_scale(scales::GetUpperScale()) - , m_cacheIsValid(false) - , m_value(nullptr) - , m_cancellable(cancellable) +FeaturesFilter::FeaturesFilter() : m_threshold(0) {} + +FeaturesFilter::FeaturesFilter(unique_ptr filter, uint32_t threshold) + : m_filter(move(filter)), m_threshold(threshold) { } -void FeaturesFilter::SetValue(MwmValue * value, MwmSet::MwmId const & id) +bool FeaturesFilter::NeedToFilter(coding::CompressedBitVector & cbv) const { - if (m_value == value && m_id == id) - return; - m_value = value; - m_id = id; - m_cacheIsValid = false; + return cbv.PopCount() > m_threshold; } -void FeaturesFilter::SetViewport(m2::RectD const & viewport) +unique_ptr FeaturesFilter::Filter( + coding::CompressedBitVector & cbv) const { - if (viewport == m_viewport) - return; - m_viewport = viewport; - m_cacheIsValid = false; -} - -void FeaturesFilter::SetMaxNumResults(size_t maxNumResults) { m_maxNumResults = maxNumResults; } - -void FeaturesFilter::SetScale(int scale) -{ - if (m_scale == scale) - return; - m_scale = scale; - m_cacheIsValid = false; -} - -bool FeaturesFilter::NeedToFilter(vector const & features) const -{ - return features.size() > m_maxNumResults; -} - -void FeaturesFilter::UpdateCache() -{ - if (m_cacheIsValid) - return; - - if (!m_value) - { - m_featuresCache.reset(); - } - else - { - m_featuresCache = - Retrieval::RetrieveGeometryFeatures(*m_value, m_cancellable, m_viewport, m_scale); - } - m_cacheIsValid = true; + if (!m_filter) + return make_unique(); + return coding::CompressedBitVector::Intersect(*m_filter, cbv); } } // namespace v2 } // namespace search diff --git a/search/v2/features_filter.hpp b/search/v2/features_filter.hpp index 231c785396..63542c0f69 100644 --- a/search/v2/features_filter.hpp +++ b/search/v2/features_filter.hpp @@ -1,69 +1,32 @@ #pragma once -#include "indexer/mwm_set.hpp" - #include "coding/compressed_bit_vector.hpp" -#include "geometry/rect2d.hpp" - -#include "base/cancellable.hpp" - -#include "std/algorithm.hpp" #include "std/unique_ptr.hpp" -#include "std/utility.hpp" -#include "std/vector.hpp" - -class MwmValue; namespace search { namespace v2 { +// A lightweight filter of features. +// +// NOTE: this class *IS NOT* thread-safe. class FeaturesFilter { public: - FeaturesFilter(my::Cancellable const & cancellable); + FeaturesFilter(); - void SetValue(MwmValue * value, MwmSet::MwmId const & id); - void SetViewport(m2::RectD const & viewport); - void SetMaxNumResults(size_t maxNumResults); - void SetScale(int scale); + FeaturesFilter(unique_ptr filter, uint32_t threshold); - bool NeedToFilter(vector const & features) const; + inline void SetFilter(unique_ptr filter) { m_filter = move(filter); } + inline void SetThreshold(uint32_t threshold) { m_threshold = threshold; } - template - void Filter(vector const & features, TFn && fn) - { - using TRankAndFeature = pair; - using TComparer = std::greater; - - UpdateCache(); - - if (!m_featuresCache || m_featuresCache->PopCount() == 0) - return; - ASSERT(m_featuresCache.get(), ()); - - // Emit all features from the viewport. - for (uint32_t feature : features) - { - if (m_featuresCache->GetBit(feature)) - fn(feature); - } - } + bool NeedToFilter(coding::CompressedBitVector & features) const; + unique_ptr Filter(coding::CompressedBitVector & cbv) const; private: - void UpdateCache(); - - m2::RectD m_viewport; - size_t m_maxNumResults; - int m_scale; - - unique_ptr m_featuresCache; - bool m_cacheIsValid; - - MwmValue * m_value; - MwmSet::MwmId m_id; - my::Cancellable const & m_cancellable; + unique_ptr m_filter; + uint32_t m_threshold; }; } // namespace v2 } // namespace search diff --git a/search/v2/features_layer_matcher.cpp b/search/v2/features_layer_matcher.cpp index a9ecb3163b..a7077a7026 100644 --- a/search/v2/features_layer_matcher.cpp +++ b/search/v2/features_layer_matcher.cpp @@ -10,14 +10,13 @@ namespace search { namespace v2 { -FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmSet::MwmId const & mwmId, - MwmValue & value, FeaturesVector const & featuresVector, +FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable) - : m_mwmId(mwmId) + : m_context(context) , m_reverseGeocoder(index) - , m_houseToStreetTable(HouseToStreetTable::Load(value)) - , m_featuresVector(featuresVector) - , m_loader(value, featuresVector, scales::GetUpperScale(), ReverseGeocoder::kLookupRadiusM) + , m_houseToStreetTable(HouseToStreetTable::Load(m_context.m_value)) + , m_loader(context.m_value, context.m_vector, scales::GetUpperScale(), + ReverseGeocoder::kLookupRadiusM) , m_cancellable(cancellable) { ASSERT(m_houseToStreetTable.get(), ("Can't load HouseToStreetTable")); @@ -33,7 +32,7 @@ uint32_t FeaturesLayerMatcher::GetMatchingStreet(uint32_t houseId, FeatureType & uint32_t const streetIndex = m_houseToStreetTable->Get(houseId); uint32_t streetId = kInvalidId; - if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_mwmId) + if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_context.m_id) streetId = streets[streetIndex].m_id.m_index; m_matchingStreetsCache[houseId] = streetId; return streetId; @@ -46,7 +45,7 @@ vector const & FeaturesLayerMatcher::GetNearbyStreets(u return it->second; FeatureType feature; - m_featuresVector.GetByIndex(featureId, feature); + m_context.m_vector.GetByIndex(featureId, feature); auto & streets = m_nearbyStreetsCache[featureId]; m_reverseGeocoder.GetNearbyStreets(feature, streets); diff --git a/search/v2/features_layer_matcher.hpp b/search/v2/features_layer_matcher.hpp index 701239a070..4bcd800d68 100644 --- a/search/v2/features_layer_matcher.hpp +++ b/search/v2/features_layer_matcher.hpp @@ -5,6 +5,7 @@ #include "search/v2/features_layer.hpp" #include "search/v2/house_numbers_matcher.hpp" #include "search/v2/house_to_street_table.hpp" +#include "search/v2/mwm_context.hpp" #include "search/v2/search_model.hpp" #include "search/v2/street_vicinity_loader.hpp" @@ -31,7 +32,6 @@ #include "std/vector.hpp" class Index; -class MwmValue; namespace search { @@ -57,8 +57,7 @@ class FeaturesLayerMatcher public: static uint32_t const kInvalidId = numeric_limits::max(); - FeaturesLayerMatcher(Index & index, MwmSet::MwmId const & mwmId, MwmValue & value, - FeaturesVector const & featuresVector, my::Cancellable const & cancellable); + FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable); template void Match(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn) @@ -78,7 +77,7 @@ public: for (uint32_t featureId : *child.m_sortedFeatures) { FeatureType ft; - m_featuresVector.GetByIndex(featureId, ft); + m_context.m_vector.GetByIndex(featureId, ft); childCenters.push_back(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY)); } @@ -89,7 +88,7 @@ public: BailIfCancelled(m_cancellable); FeatureType ft; - m_featuresVector.GetByIndex((*parent.m_sortedFeatures)[j], ft); + m_context.m_vector.GetByIndex((*parent.m_sortedFeatures)[j], ft); m2::PointD const center = feature::GetCenter(ft, FeatureType::WORST_GEOMETRY); double const radius = ftypes::GetRadiusByPopulation(ft.GetPopulation()); m2::RectD const rect = MercatorBounds::RectByCenterXYAndSizeInMeters(center, radius); @@ -182,7 +181,8 @@ private: vector const & GetNearbyStreets(uint32_t featureId, FeatureType & feature); - MwmSet::MwmId m_mwmId; + MwmContext & m_context; + ReverseGeocoder m_reverseGeocoder; // Cache of streets in a feature's vicinity. All lists in the cache @@ -196,7 +196,6 @@ private: unique_ptr m_houseToStreetTable; - FeaturesVector const & m_featuresVector; StreetVicinityLoader m_loader; my::Cancellable const & m_cancellable; }; diff --git a/search/v2/features_layer_path_finder.cpp b/search/v2/features_layer_path_finder.cpp index a2a0d4f1ae..6b6467211c 100644 --- a/search/v2/features_layer_path_finder.cpp +++ b/search/v2/features_layer_path_finder.cpp @@ -2,7 +2,6 @@ #include "search/cancel_exception.hpp" #include "search/v2/features_layer_matcher.hpp" -#include "search/v2/features_filter.hpp" #include "indexer/features_vector.hpp" @@ -17,7 +16,7 @@ FeaturesLayerPathFinder::FeaturesLayerPathFinder(my::Cancellable const & cancell { } -void FeaturesLayerPathFinder::BuildGraph(FeaturesLayerMatcher & matcher, FeaturesFilter & filter, +void FeaturesLayerPathFinder::BuildGraph(FeaturesLayerMatcher & matcher, vector const & layers, vector & reachable) { @@ -37,14 +36,6 @@ void FeaturesLayerPathFinder::BuildGraph(FeaturesLayerMatcher & matcher, Feature if (reachable.empty()) break; - if (filter.NeedToFilter(reachable)) - { - buffer.clear(); - filter.Filter(reachable, MakeBackInsertFunctor(buffer)); - reachable.swap(buffer); - my::SortUnique(reachable); - } - buffer.clear(); auto addEdge = [&](uint32_t childFeature, uint32_t /* parentFeature */) { diff --git a/search/v2/features_layer_path_finder.hpp b/search/v2/features_layer_path_finder.hpp index 5e2f0a1b58..719025e3f7 100644 --- a/search/v2/features_layer_path_finder.hpp +++ b/search/v2/features_layer_path_finder.hpp @@ -16,7 +16,6 @@ namespace search { namespace v2 { -class FeaturesFilter; class FeaturesLayerMatcher; // This class is able to find all paths through a layered graph, with @@ -34,22 +33,22 @@ public: FeaturesLayerPathFinder(my::Cancellable const & cancellable); template - void ForEachReachableVertex(FeaturesLayerMatcher & matcher, FeaturesFilter & filter, + void ForEachReachableVertex(FeaturesLayerMatcher & matcher, vector const & layers, TFn && fn) { if (layers.empty()) return; vector reachable; - BuildGraph(matcher, filter, layers, reachable); + BuildGraph(matcher, layers, reachable); for (uint32_t featureId : reachable) fn(featureId); } private: - void BuildGraph(FeaturesLayerMatcher & matcher, FeaturesFilter & filter, - vector const & layers, vector & reachable); + void BuildGraph(FeaturesLayerMatcher & matcher, vector const & layers, + vector & reachable); my::Cancellable const & m_cancellable; }; diff --git a/search/v2/geocoder.cpp b/search/v2/geocoder.cpp index 9698a0b132..8c45a71042 100644 --- a/search/v2/geocoder.cpp +++ b/search/v2/geocoder.cpp @@ -9,6 +9,8 @@ #include "indexer/feature_decl.hpp" #include "indexer/feature_impl.hpp" #include "indexer/index.hpp" +#include "indexer/mercator.hpp" +#include "indexer/mwm_set.hpp" #include "coding/multilang_utf8_string.hpp" @@ -24,12 +26,17 @@ #include "std/algorithm.hpp" #include "std/iterator.hpp" +#include "defines.hpp" + namespace search { namespace v2 { namespace { +// 50km maximum viewport radius. +double const kMaxViewportRadiusM = 50.0 * 1000; + void JoinQueryTokens(SearchQueryParams const & params, size_t curToken, size_t endToken, string const & sep, string & res) { @@ -50,35 +57,25 @@ void JoinQueryTokens(SearchQueryParams const & params, size_t curToken, size_t e res.append(sep); } } -} // namespace -// Geocoder::Partition -Geocoder::Partition::Partition() : m_size(0) {} +bool HasSearchIndex(MwmValue const & value) { return value.m_cont.IsExist(SEARCH_INDEX_FILE_TAG); } -void Geocoder::Partition::FromFeatures(unique_ptr features, - Index::FeaturesLoaderGuard & loader, - SearchModel const & model) +bool HasGeometryIndex(MwmValue & value) { return value.m_cont.IsExist(INDEX_FILE_TAG); } + +MwmSet::MwmHandle FindWorld(Index & index, vector> & infos) { - for (auto & cluster : m_clusters) - cluster.clear(); - - auto clusterize = [&](uint64_t featureId) + MwmSet::MwmHandle handle; + for (auto const & info : infos) { - FeatureType feature; - loader.GetFeatureByIndex(featureId, feature); - feature.ParseTypes(); - SearchModel::SearchType searchType = model.GetSearchType(feature); - if (searchType != SearchModel::SEARCH_TYPE_COUNT) - m_clusters[searchType].push_back(featureId); - }; - - if (features) - coding::CompressedBitVectorEnumerator::ForEach(*features, clusterize); - - m_size = 0; - for (auto const & cluster : m_clusters) - m_size += cluster.size(); + if (info->GetType() == MwmInfo::WORLD) + { + handle = index.GetMwmHandleById(MwmSet::MwmId(info)); + break; + } + } + return handle; } +} // namespace // Geocoder::Params -------------------------------------------------------------------------------- Geocoder::Params::Params() : m_maxNumResults(0) {} @@ -88,8 +85,6 @@ Geocoder::Geocoder(Index & index) : m_index(index) , m_numTokens(0) , m_model(SearchModel::Instance()) - , m_value(nullptr) - , m_filter(static_cast(*this)) , m_finder(static_cast(*this)) , m_results(nullptr) { @@ -101,11 +96,6 @@ void Geocoder::SetParams(Params const & params) { m_params = params; m_retrievalParams = params; - - m_filter.SetViewport(m_params.m_viewport); - m_filter.SetMaxNumResults(m_params.m_maxNumResults); - m_filter.SetScale(m_params.m_scale); - m_numTokens = m_params.m_tokens.size(); if (!m_params.m_prefixTokens.empty()) ++m_numTokens; @@ -120,46 +110,52 @@ void Geocoder::Go(vector & results) try { - vector> mwmsInfo; - m_index.GetMwmsInfo(mwmsInfo); + vector> infos; + m_index.GetMwmsInfo(infos); - // Iterate through all alive mwms and perform geocoding. - for (auto const & info : mwmsInfo) + // Tries to find world and fill localities table. { - auto handle = m_index.GetMwmHandleById(MwmSet::MwmId(info)); - if (!handle.IsAlive()) - continue; + m_localities.clear(); + MwmSet::MwmHandle handle = FindWorld(m_index, infos); + if (handle.IsAlive()) + { + auto & value = *handle.GetValue(); - m_value = handle.GetValue(); - if (!m_value || !m_value->m_cont.IsExist(SEARCH_INDEX_FILE_TAG)) - continue; - - m_mwmId = handle.GetId(); + // All MwmIds are unique during the application lifetime, so + // it's ok to save MwmId. + m_worldId = handle.GetId(); + if (HasSearchIndex(value)) + FillLocalitiesTable(MwmContext(value, m_worldId)); + } + } + auto processCountry = [&](unique_ptr context) + { + ASSERT(context, ()); + m_context = move(context); MY_SCOPE_GUARD(cleanup, [&]() { m_matcher.reset(); - m_loader.reset(); - m_partitions.clear(); + m_context.reset(); + m_features.clear(); }); - m_partitions.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(), *this /* cancellable */)); - m_filter.SetValue(m_value, m_mwmId); + m_matcher.reset(new FeaturesLayerMatcher(m_index, *m_context, *this /* cancellable */)); - m_partitions.resize(m_numTokens); + // Creates a cache of posting lists for each token. + m_features.resize(m_numTokens); for (size_t i = 0; i < m_numTokens; ++i) { PrepareRetrievalParams(i, i + 1); - m_partitions[i].FromFeatures(Retrieval::RetrieveAddressFeatures( - *m_value, *this /* cancellable */, m_retrievalParams), - *m_loader, m_model); + m_features[i] = Retrieval::RetrieveAddressFeatures( + m_context->m_value, *this /* cancellable */, m_retrievalParams); } - DoGeocoding(0 /* curToken */); - } + DoGeocodingWithLocalities(); + }; + + // Iterates through all alive mwms and performs geocoding. + ForEachCountry(infos, processCountry); } catch (CancelException & e) { @@ -168,7 +164,7 @@ void Geocoder::Go(vector & results) void Geocoder::ClearCaches() { - m_partitions.clear(); + m_features.clear(); m_matcher.reset(); } @@ -192,8 +188,142 @@ void Geocoder::PrepareRetrievalParams(size_t curToken, size_t endToken) } } +void Geocoder::FillLocalitiesTable(MwmContext const & context) +{ + m_localities.clear(); + + auto addLocality = [&](size_t curToken, size_t endToken, uint32_t featureId) + { + FeatureType ft; + context.m_vector.GetByIndex(featureId, ft); + if (m_model.GetSearchType(ft) != SearchModel::SEARCH_TYPE_CITY) + return; + + m2::PointD const center = feature::GetCenter(ft, FeatureType::WORST_GEOMETRY); + double const radiusM = ftypes::GetRadiusByPopulation(ft.GetPopulation()); + + Locality locality; + locality.m_featureId = featureId; + locality.m_startToken = curToken; + locality.m_endToken = endToken; + locality.m_rect = MercatorBounds::RectByCenterXYAndSizeInMeters(center, radiusM); + m_localities[make_pair(curToken, endToken)].push_back(locality); + }; + + for (size_t curToken = 0; curToken < m_numTokens; ++curToken) + { + for (size_t endToken = curToken + 1; endToken <= m_numTokens; ++endToken) + { + PrepareRetrievalParams(curToken, endToken); + auto localities = Retrieval::RetrieveAddressFeatures( + context.m_value, static_cast(*this), m_retrievalParams); + if (coding::CompressedBitVector::IsEmpty(localities)) + break; + coding::CompressedBitVectorEnumerator::ForEach(*localities, + bind(addLocality, curToken, endToken, _1)); + } + } +} + +template +void Geocoder::ForEachCountry(vector> const & infos, TFn && fn) +{ + for (auto const & info : infos) + { + if (info->GetType() != MwmInfo::COUNTRY) + continue; + auto handle = m_index.GetMwmHandleById(MwmSet::MwmId(info)); + if (!handle.IsAlive()) + continue; + auto & value = *handle.GetValue(); + if (!HasSearchIndex(value) || !HasGeometryIndex(value)) + continue; + fn(make_unique(value, handle.GetId())); + } +} + +void Geocoder::DoGeocodingWithLocalities() +{ + ASSERT(m_context, ("Mwm context must be initialized at this moment.")); + + m_usedTokens.assign(m_numTokens, false); + + m2::RectD const countryBounds = m_context->m_value.GetHeader().GetBounds(); + + // Localities are ordered my (m_startToken, m_endToken) pairs. + for (auto const & p : m_localities) + { + size_t const startToken = p.first.first; + size_t const endToken = p.first.second; + if (startToken == 0 && endToken == m_numTokens) + { + // Localities match to search query. + for (auto const & locality : p.second) + m_results->emplace_back(m_worldId, locality.m_featureId); + continue; + } + + // Unites features from all localities and uses the resulting bit + // vector as a filter for features retrieved during geocoding. + unique_ptr allFeatures; + for (auto const & locality : p.second) + { + m2::RectD rect = countryBounds; + if (!rect.Intersect(locality.m_rect)) + continue; + auto features = Retrieval::RetrieveGeometryFeatures( + m_context->m_value, static_cast(*this), rect, m_params.m_scale); + if (!features) + continue; + + if (!allFeatures) + allFeatures = move(features); + else + allFeatures = coding::CompressedBitVector::Union(*allFeatures, *features); + } + + if (coding::CompressedBitVector::IsEmpty(allFeatures)) + continue; + + m_filter.SetFilter(move(allFeatures)); + + // Filter will be applied for all non-empty bit vectors. + m_filter.SetThreshold(0); + + // Marks all tokens matched to localities as used and performs geocoding. + fill(m_usedTokens.begin() + startToken, m_usedTokens.begin() + endToken, true); + DoGeocoding(0 /* curToken */); + fill(m_usedTokens.begin() + startToken, m_usedTokens.begin() + endToken, false); + } + + // Tries to do geocoding by viewport only. + // + // TODO (@y, @m, @vng): consider to add user position here, to + // inflate viewport if too small number of results is found, etc. + { + // Limits viewport by kMaxViewportRadiusM. + m2::RectD const viewportLimit = MercatorBounds::RectByCenterXYAndSizeInMeters( + m_params.m_viewport.Center(), kMaxViewportRadiusM); + m2::RectD rect = m_params.m_viewport; + rect.Intersect(viewportLimit); + if (!rect.IsEmptyInterior()) + { + m_filter.SetFilter(Retrieval::RetrieveGeometryFeatures( + m_context->m_value, static_cast(*this), rect, m_params.m_scale)); + + // Filter will be applied only for large bit vectors. + m_filter.SetThreshold(m_params.m_maxNumResults); + DoGeocoding(0 /* curToken */); + } + } +} + void Geocoder::DoGeocoding(size_t curToken) { + // Skip used tokens. + while (curToken != m_numTokens && m_usedTokens[curToken]) + ++curToken; + BailIfCancelled(static_cast(*this)); if (curToken == m_numTokens) @@ -207,8 +337,12 @@ void Geocoder::DoGeocoding(size_t curToken) m_layers.emplace_back(); MY_SCOPE_GUARD(cleanupGuard, bind(&vector::pop_back, &m_layers)); + vector clusters[SearchModel::SEARCH_TYPE_CITY]; + vector loopClusters[SearchModel::SEARCH_TYPE_CITY]; + vector buffer; + // Try to consume first n tokens starting at |curToken|. - for (size_t n = 1; curToken + n <= m_numTokens; ++n) + for (size_t n = 1; curToken + n <= m_numTokens && !m_usedTokens[curToken + n - 1]; ++n) { BailIfCancelled(static_cast(*this)); @@ -221,17 +355,46 @@ void Geocoder::DoGeocoding(size_t curToken) layer.m_subQuery); } + PrepareRetrievalParams(curToken, curToken + n); + BailIfCancelled(static_cast(*this)); bool const looksLikeHouseNumber = feature::IsHouseNumber(m_layers.back().m_subQuery); - auto const & partition = m_partitions[curToken + n - 1]; - if (partition.m_size == 0 && !looksLikeHouseNumber) + auto const & features = m_features[curToken + n - 1]; + if (coding::CompressedBitVector::IsEmpty(features) && !looksLikeHouseNumber) break; - vector clusters[SearchModel::SEARCH_TYPE_COUNT]; - vector buffer; + unique_ptr cbv; - for (size_t i = 0; i != SearchModel::SEARCH_TYPE_COUNT; ++i) + // Non-owning ptr. + coding::CompressedBitVector * filteredFeatures = features.get(); + + if (m_filter.NeedToFilter(*features)) + { + cbv = m_filter.Filter(*features); + filteredFeatures = cbv.get(); + } + + if (!coding::CompressedBitVector::IsEmpty(filteredFeatures)) + { + for (auto & cluster : loopClusters) + cluster.clear(); + + auto clusterize = [&](uint32_t featureId) + { + FeatureType feature; + m_context->m_vector.GetByIndex(featureId, feature); + feature.ParseTypes(); + SearchModel::SearchType searchType = m_model.GetSearchType(feature); + + // All SEARCH_TYPE_CITY features were filtered in DoGeocodingWithLocalities(). + if (searchType < SearchModel::SEARCH_TYPE_CITY) + loopClusters[searchType].push_back(featureId); + }; + coding::CompressedBitVectorEnumerator::ForEach(*filteredFeatures, clusterize); + } + + for (size_t i = 0; i != SearchModel::SEARCH_TYPE_CITY; ++i) { // ATTENTION: DO NOT USE layer after recursive calls to // DoGeocoding(). This may lead to use-after-free. @@ -241,25 +404,16 @@ void Geocoder::DoGeocoding(size_t curToken) // This can be done incrementally, as we store intersections in |clusters|. if (n == 1) { - layer.m_sortedFeatures = &partition.m_clusters[i]; - } - else if (n == 2) - { - clusters[i].clear(); - auto const & first = m_partitions[curToken].m_clusters[i]; - auto const & second = m_partitions[curToken + 1].m_clusters[i]; - set_intersection(first.begin(), first.end(), second.begin(), second.end(), - back_inserter(clusters[i])); - layer.m_sortedFeatures = &clusters[i]; + clusters[i].swap(loopClusters[i]); } else { buffer.clear(); - set_intersection(clusters[i].begin(), clusters[i].end(), partition.m_clusters[i].begin(), - partition.m_clusters[i].end(), back_inserter(buffer)); + set_intersection(clusters[i].begin(), clusters[i].end(), loopClusters[i].begin(), + loopClusters[i].end(), back_inserter(buffer)); clusters[i].swap(buffer); - layer.m_sortedFeatures = &clusters[i]; } + layer.m_sortedFeatures = &clusters[i]; if (i == SearchModel::SEARCH_TYPE_BUILDING) { @@ -326,7 +480,8 @@ bool Geocoder::IsLayerSequenceSane() const void Geocoder::FindPaths() { - ASSERT(!m_layers.empty(), ()); + if (m_layers.empty()) + return; // Layers ordered by a search type. vector sortedLayers; @@ -335,10 +490,10 @@ void Geocoder::FindPaths() sortedLayers.push_back(&layer); sort(sortedLayers.begin(), sortedLayers.end(), my::CompareBy(&FeaturesLayer::m_type)); - m_finder.ForEachReachableVertex(*m_matcher, m_filter, sortedLayers, [this](uint32_t featureId) + m_finder.ForEachReachableVertex(*m_matcher, sortedLayers, [this](uint32_t featureId) { - m_results->emplace_back(m_mwmId, featureId); - }); + m_results->emplace_back(m_context->m_id, featureId); + }); } } // namespace v2 } // namespace search diff --git a/search/v2/geocoder.hpp b/search/v2/geocoder.hpp index 502cb29f9b..bd801012a7 100644 --- a/search/v2/geocoder.hpp +++ b/search/v2/geocoder.hpp @@ -4,6 +4,7 @@ #include "search/v2/features_filter.hpp" #include "search/v2/features_layer.hpp" #include "search/v2/features_layer_path_finder.hpp" +#include "search/v2/mwm_context.hpp" #include "search/v2/search_model.hpp" #include "indexer/mwm_set.hpp" @@ -78,25 +79,33 @@ public: void ClearCaches(); private: - struct Partition + struct Locality { - Partition(); + Locality() : m_featureId(0), m_startToken(0), m_endToken(0) {} - Partition(Partition &&) = default; - - void FromFeatures(unique_ptr features, - Index::FeaturesLoaderGuard & loader, SearchModel const & model); - - vector m_clusters[SearchModel::SEARCH_TYPE_COUNT]; - size_t m_size; - - DISALLOW_COPY(Partition); + uint32_t m_featureId; + size_t m_startToken; + size_t m_endToken; + m2::RectD m_rect; }; // Fills |m_retrievalParams| with [curToken, endToken) subsequence // of search query tokens. void PrepareRetrievalParams(size_t curToken, size_t endToken); + void FillLocalitiesTable(MwmContext const & context); + + template + void ForEachCountry(vector> const & infos, TFn && fn); + + // Tries to find all localities in a search query and then performs + // geocoding in found localities. + // + // *NOTE* that localities will be looked for in a World.mwm, so, for + // now, villages won't be found on this stage. + // TODO (@y, @m, @vng): try to add villages to World.mwm. + void DoGeocodingWithLocalities(); + // Tries to find all paths in a search tree, where each edge is // marked with some substring of the query tokens. These paths are // called "layer sequence" and current path is stored in |m_layers|. @@ -126,26 +135,29 @@ private: // Following fields are set up by Search() method and can be // modified and used only from Search() or its callees. - // Value of a current mwm. - MwmValue * m_value; + MwmSet::MwmId m_worldId; - // Id of a current mwm. - MwmSet::MwmId m_mwmId; + // Context of the currently processed mwm. + unique_ptr m_context; + + // Map from [curToken, endToken) to matching localities list. + map, vector> m_localities; // Cache of posting lists for each token in the query. TODO (@y, // @m, @vng): consider to update this cache lazily, as user inputs // tokens one-by-one. - vector m_partitions; + vector> m_features; - // Features loader. - unique_ptr m_loader; + // This vector is used to indicate what tokens were matched by + // locality and can't be re-used during the geocoding process. + vector m_usedTokens; + + // This filter is used to throw away excess features. + FeaturesFilter m_filter; // Features matcher for layers intersection. unique_ptr m_matcher; - // Features filter for interpretations. - FeaturesFilter m_filter; - // Path finder for interpretations. FeaturesLayerPathFinder m_finder; diff --git a/search/v2/mwm_context.cpp b/search/v2/mwm_context.cpp new file mode 100644 index 0000000000..7382fe54fa --- /dev/null +++ b/search/v2/mwm_context.cpp @@ -0,0 +1,14 @@ +#include "search/v2/mwm_context.hpp" + +#include "indexer/index.hpp" + +namespace search +{ +namespace v2 +{ +MwmContext::MwmContext(MwmValue & value, MwmSet::MwmId const & id) + : m_value(value), m_id(id), m_vector(m_value.m_cont, m_value.GetHeader(), m_value.m_table) +{ +} +} // namespace v2 +} // namespace search diff --git a/search/v2/mwm_context.hpp b/search/v2/mwm_context.hpp new file mode 100644 index 0000000000..1a90586e31 --- /dev/null +++ b/search/v2/mwm_context.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "indexer/features_vector.hpp" +#include "indexer/mwm_set.hpp" + +#include "base/macros.hpp" + +class MwmValue; + +namespace search +{ +namespace v2 +{ +struct MwmContext +{ + MwmContext(MwmValue & value, MwmSet::MwmId const & id); + + MwmValue & m_value; + MwmSet::MwmId const m_id; + FeaturesVector m_vector; + + DISALLOW_COPY_AND_MOVE(MwmContext); +}; +} // namespace v2 +} // namespace search