#include "retrieval.hpp" #include "search/cancel_exception.hpp" #include "search/feature_offset_match.hpp" #include "search/interval_set.hpp" #include "search/mwm_context.hpp" #include "search/search_index_values.hpp" #include "search/search_trie.hpp" #include "search/token_slice.hpp" #include "indexer/classificator.hpp" #include "indexer/feature.hpp" #include "indexer/feature_algo.hpp" #include "indexer/feature_data.hpp" #include "indexer/index.hpp" #include "indexer/osm_editor.hpp" #include "indexer/scales.hpp" #include "indexer/search_delimiters.hpp" #include "indexer/search_string_utils.hpp" #include "indexer/trie_reader.hpp" #include "platform/mwm_version.hpp" #include "coding/compressed_bit_vector.hpp" #include "coding/reader_wrapper.hpp" #include "base/checked_cast.hpp" #include "std/algorithm.hpp" #include "std/utility.hpp" using namespace strings; using osm::Editor; namespace search { namespace { class FeaturesCollector { public: FeaturesCollector(my::Cancellable const & cancellable, vector & features) : m_cancellable(cancellable), m_features(features), m_counter(0) { } template void operator()(Value const & value) { if ((++m_counter & 0xFF) == 0) BailIfCancelled(m_cancellable); m_features.push_back(value.m_featureId); } inline void operator()(uint32_t feature) { m_features.push_back(feature); } inline void operator()(uint64_t feature) { m_features.push_back(feature); } private: my::Cancellable const & m_cancellable; vector & m_features; uint32_t m_counter; }; class EditedFeaturesHolder { public: EditedFeaturesHolder(MwmSet::MwmId const & id) : m_id(id) { auto & editor = Editor::Instance(); m_deleted = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Deleted); m_modified = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Modified); m_created = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Created); } bool ModifiedOrDeleted(uint32_t featureIndex) const { return binary_search(m_deleted.begin(), m_deleted.end(), featureIndex) || binary_search(m_modified.begin(), m_modified.end(), featureIndex); } template void ForEachModifiedOrCreated(Fn && fn) { ForEach(m_modified, fn); ForEach(m_created, fn); } private: template void ForEach(vector const & features, Fn & fn) { auto & editor = Editor::Instance(); for (auto const index : features) { FeatureType ft; VERIFY(editor.GetEditedFeature(m_id, index, ft), ()); fn(ft, index); } } MwmSet::MwmId const & m_id; vector m_deleted; vector m_modified; vector m_created; }; unique_ptr SortFeaturesAndBuildCBV(vector && features) { my::SortUnique(features); return coding::CompressedBitVectorBuilder::FromBitPositions(move(features)); } template bool MatchesByName(vector const & tokens, vector const & dfas) { for (auto const & dfa : dfas) { for (auto const & token : tokens) { auto it = dfa.Begin(); DFAMove(it, token); if (it.Accepts()) return true; } } return false; } template bool MatchesByType(feature::TypesHolder const & types, vector const & dfas) { if (dfas.empty()) return false; auto const & c = classif(); for (auto const & type : types) { UniString const s = FeatureTypeToString(c.GetIndexForType(type)); for (auto const & dfa : dfas) { auto it = dfa.Begin(); DFAMove(it, s); if (it.Accepts()) return true; } } return false; } template bool MatchFeatureByNameAndType(FeatureType const & ft, SearchTrieRequest const & request) { feature::TypesHolder th(ft); bool matched = false; ft.ForEachName([&](int8_t lang, string const & name) { if (name.empty() || !request.IsLangExist(lang)) return true /* continue ForEachName */; vector tokens; NormalizeAndTokenizeString(name, tokens, Delimiters()); if (!MatchesByName(tokens, request.m_names) && !MatchesByType(th, request.m_categories)) return true /* continue ForEachName */; matched = true; return false /* break ForEachName */; }); return matched; } bool MatchFeatureByPostcode(FeatureType const & ft, TokenSlice const & slice) { string const postcode = ft.GetMetadata().Get(feature::Metadata::FMD_POSTCODE); vector tokens; NormalizeAndTokenizeString(postcode, tokens, Delimiters()); if (slice.Size() > tokens.size()) return false; for (size_t i = 0; i < slice.Size(); ++i) { if (slice.IsPrefix(i)) { if (!StartsWith(tokens[i], slice.Get(i).m_original)) return false; } else if (tokens[i] != slice.Get(i).m_original) { return false; } } return true; } template unique_ptr RetrieveAddressFeaturesImpl( Retrieval::TrieRoot const & root, MwmContext const & context, my::Cancellable const & cancellable, SearchTrieRequest const & request) { EditedFeaturesHolder holder(context.GetId()); vector features; FeaturesCollector collector(cancellable, features); MatchFeaturesInTrie( request, root, [&holder](uint64_t featureIndex) { return !holder.ModifiedOrDeleted(base::asserted_cast(featureIndex)); }, collector); holder.ForEachModifiedOrCreated([&](FeatureType & ft, uint64_t index) { if (MatchFeatureByNameAndType(ft, request)) features.push_back(index); }); return SortFeaturesAndBuildCBV(move(features)); } template unique_ptr RetrievePostcodeFeaturesImpl( Retrieval::TrieRoot const & root, MwmContext const & context, my::Cancellable const & cancellable, TokenSlice const & slice) { EditedFeaturesHolder holder(context.GetId()); vector features; FeaturesCollector collector(cancellable, features); MatchPostcodesInTrie( slice, root, [&holder](uint64_t featureIndex) { return !holder.ModifiedOrDeleted(base::asserted_cast(featureIndex)); }, collector); holder.ForEachModifiedOrCreated([&](FeatureType & ft, uint64_t index) { if (MatchFeatureByPostcode(ft, slice)) features.push_back(index); }); return SortFeaturesAndBuildCBV(move(features)); } unique_ptr RetrieveGeometryFeaturesImpl( MwmContext const & context, my::Cancellable const & cancellable, m2::RectD const & rect, int scale) { EditedFeaturesHolder holder(context.GetId()); covering::IntervalsT coverage; CoverRect(rect, scale, coverage); vector features; FeaturesCollector collector(cancellable, features); context.ForEachIndex(coverage, scale, collector); holder.ForEachModifiedOrCreated([&](FeatureType & ft, uint64_t index) { auto const center = feature::GetCenter(ft); if (rect.IsPointInside(center)) features.push_back(index); }); return SortFeaturesAndBuildCBV(move(features)); } template struct RetrieveAddressFeaturesAdaptor { template unique_ptr operator()(Args &&... args) { return RetrieveAddressFeaturesImpl(forward(args)...); } }; template struct RetrievePostcodeFeaturesAdaptor { template unique_ptr operator()(Args &&... args) { return RetrievePostcodeFeaturesImpl(forward(args)...); } }; template unique_ptr> ReadTrie(MwmValue & value, ModelReaderPtr & reader) { serial::CodingParams params(trie::GetCodingParams(value.GetHeader().GetDefCodingParams())); return trie::ReadTrie, ValueList>( SubReaderWrapper(reader.GetPtr()), SingleValueSerializer(params)); } } // namespace Retrieval::Retrieval(MwmContext const & context, my::Cancellable const & cancellable) : m_context(context) , m_cancellable(cancellable) , m_reader(context.m_value.m_cont.GetReader(SEARCH_INDEX_FILE_TAG)) { auto & value = context.m_value; version::MwmTraits mwmTraits(value.GetMwmVersion()); m_format = mwmTraits.GetSearchIndexFormat(); switch (m_format) { case version::MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter: m_root0 = ReadTrie(value, m_reader); break; case version::MwmTraits::SearchIndexFormat::CompressedBitVector: m_root1 = ReadTrie(value, m_reader); break; } } unique_ptr Retrieval::RetrieveAddressFeatures( SearchTrieRequest const & request) { return Retrieve(request); } unique_ptr Retrieval::RetrieveAddressFeatures( SearchTrieRequest> const & request) { return Retrieve(request); } unique_ptr Retrieval::RetrievePostcodeFeatures( TokenSlice const & slice) { return Retrieve(slice); } unique_ptr Retrieval::RetrieveGeometryFeatures(m2::RectD const & rect, int scale) { return RetrieveGeometryFeaturesImpl(m_context, m_cancellable, rect, scale); } template