Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/search
diff options
context:
space:
mode:
authorYuri Gorshenin <y@maps.me>2016-08-22 13:47:56 +0300
committerYuri Gorshenin <y@maps.me>2016-08-26 16:21:13 +0300
commitafc3d20264e8fcd99dc3cd465ce1770d0b3246d0 (patch)
tree67d2bcd855340555d742181e467cc987bd70a332 /search
parent336c137d59c12d0de9fd292bcf3db9b453670567 (diff)
[search] Added matching of edited features by type.
Diffstat (limited to 'search')
-rw-r--r--search/categories_set.hpp69
-rw-r--r--search/geocoder.cpp90
-rw-r--r--search/geocoder.hpp6
-rw-r--r--search/processor.cpp7
-rw-r--r--search/query_params.cpp79
-rw-r--r--search/query_params.hpp43
-rw-r--r--search/retrieval.cpp54
-rw-r--r--search/search.pro1
-rw-r--r--search/search_integration_tests/helpers.cpp2
-rw-r--r--search/search_integration_tests/helpers.hpp16
-rw-r--r--search/search_integration_tests/search_edited_features_test.cpp64
-rw-r--r--search/search_integration_tests/search_integration_tests.pro7
-rw-r--r--search/token_slice.cpp2
13 files changed, 257 insertions, 183 deletions
diff --git a/search/categories_set.hpp b/search/categories_set.hpp
new file mode 100644
index 0000000000..343d1c40b9
--- /dev/null
+++ b/search/categories_set.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "indexer/classificator.hpp"
+#include "indexer/search_string_utils.hpp"
+
+#include "base/macros.hpp"
+#include "base/stl_helpers.hpp"
+#include "base/string_utils.hpp"
+
+#include "std/algorithm.hpp"
+#include "std/cstdint.hpp"
+#include "std/vector.hpp"
+
+namespace search
+{
+struct Category
+{
+ Category(strings::UniString const & key, uint32_t type) : m_key(key), m_type(type) {}
+
+ bool operator==(Category const & rhs) const { return m_key == rhs.m_key && m_type == rhs.m_type; }
+
+ bool operator<(Category const & rhs) const
+ {
+ if (m_key != rhs.m_key)
+ return m_key < rhs.m_key;
+ return m_type < rhs.m_type;
+ }
+
+ strings::UniString m_key;
+ uint32_t m_type = 0;
+};
+
+class CategoriesSet
+{
+public:
+ template <typename TTypesList>
+ CategoriesSet(TTypesList const & typesList)
+ {
+ auto const & classificator = classif();
+
+ auto addCategory = [&](uint32_t type) {
+ uint32_t const index = classificator.GetIndexForType(type);
+ m_categories.emplace_back(FeatureTypeToString(index), type);
+ };
+
+ typesList.ForEachType(addCategory);
+
+ my::SortUnique(m_categories);
+ }
+
+ template <typename TFn>
+ void ForEach(TFn && fn) const
+ {
+ for_each(m_categories.cbegin(), m_categories.cend(), forward<TFn>(fn));
+ }
+
+ bool HasKey(strings::UniString const & key) const
+ {
+ auto const it =
+ lower_bound(m_categories.cbegin(), m_categories.cend(), Category(key, 0 /* type */));
+ return it != m_categories.cend() && it->m_key == key;
+ }
+
+private:
+ vector<Category> m_categories;
+
+ DISALLOW_COPY_AND_MOVE(CategoriesSet);
+};
+} // namespace search
diff --git a/search/geocoder.cpp b/search/geocoder.cpp
index cea3c3f4f9..f66d5136ea 100644
--- a/search/geocoder.cpp
+++ b/search/geocoder.cpp
@@ -170,46 +170,6 @@ private:
LazyRankTable m_ranks;
};
-class StreetCategories
-{
-public:
- static StreetCategories const & Instance()
- {
- static StreetCategories const instance;
- return instance;
- }
-
- template <typename TFn>
- void ForEach(TFn && fn) const
- {
- for_each(m_categories.cbegin(), m_categories.cend(), forward<TFn>(fn));
- }
-
- bool Contains(strings::UniString const & category) const
- {
- return binary_search(m_categories.cbegin(), m_categories.cend(), category);
- }
-
- vector<strings::UniString> const & GetCategories() const { return m_categories; }
-
-private:
- StreetCategories()
- {
- auto const & classificator = classif();
- auto addCategory = [&](uint32_t type)
- {
- uint32_t const index = classificator.GetIndexForType(type);
- m_categories.push_back(FeatureTypeToString(index));
- };
- ftypes::IsStreetChecker::Instance().ForEachType(addCategory);
- sort(m_categories.begin(), m_categories.end());
- }
-
- vector<strings::UniString> m_categories;
-
- DISALLOW_COPY_AND_MOVE(StreetCategories);
-};
-
void JoinQueryTokens(QueryParams const & params, size_t curToken, size_t endToken,
strings::UniString const & sep, strings::UniString & res)
{
@@ -245,22 +205,6 @@ WARN_UNUSED_RESULT bool GetAffiliationName(FeatureType const & ft, string & affi
return false;
}
-// todo(@m) Refactor at least here, or even at indexer/ftypes_matcher.hpp.
-vector<strings::UniString> GetVillageCategories()
-{
- vector<strings::UniString> categories;
-
- auto const & classificator = classif();
- auto addCategory = [&](uint32_t type)
- {
- uint32_t const index = classificator.GetIndexForType(type);
- categories.push_back(FeatureTypeToString(index));
- };
- ftypes::IsVillageChecker::Instance().ForEachType(addCategory);
-
- return categories;
-}
-
bool HasSearchIndex(MwmValue const & value) { return value.m_cont.IsExist(SEARCH_INDEX_FILE_TAG); }
bool HasGeometryIndex(MwmValue & value) { return value.m_cont.IsExist(INDEX_FILE_TAG); }
@@ -418,6 +362,8 @@ Geocoder::Geocoder(Index const & index, storage::CountryInfoGetter const & infoG
: m_index(index)
, m_infoGetter(infoGetter)
, m_cancellable(cancellable)
+ , m_streets(ftypes::IsStreetChecker::Instance())
+ , m_villages(ftypes::IsVillageChecker::Instance())
, m_model(SearchModel::Instance())
, m_pivotRectsCache(kPivotRectsCacheSize, m_cancellable, Processor::kMaxViewportRadiusM)
, m_localityRectsCache(kLocalityRectsCacheSize, m_cancellable)
@@ -462,9 +408,7 @@ void Geocoder::SetParams(Params const & params)
{
auto b = synonyms.begin();
auto e = synonyms.end();
- auto const & categories = StreetCategories::Instance();
- synonyms.erase(remove_if(b + 1, e, bind(&StreetCategories::Contains, cref(categories), _1)),
- e);
+ synonyms.erase(remove_if(b + 1, e, bind(&CategoriesSet::HasKey, cref(m_streets), _1)), e);
}
}
@@ -639,6 +583,7 @@ void Geocoder::PrepareRetrievalParams(size_t curToken, size_t endToken)
m_retrievalParams.m_tokens.clear();
m_retrievalParams.m_prefixTokens.clear();
+ m_retrievalParams.m_types.clear();
// TODO (@y): possibly it's not cheap to copy vectors of strings.
// Profile it, and in case of serious performance loss, refactor
@@ -649,6 +594,8 @@ void Geocoder::PrepareRetrievalParams(size_t curToken, size_t endToken)
m_retrievalParams.m_tokens.push_back(m_params.m_tokens[i]);
else
m_retrievalParams.m_prefixTokens = m_params.m_prefixTokens;
+
+ m_retrievalParams.m_types.push_back(m_params.m_types[i]);
}
}
@@ -1413,24 +1360,29 @@ void Geocoder::MatchUnclassified(BaseContext & ctx, size_t curToken)
allFeatures.ForEach(emitUnclassified);
}
-CBV Geocoder::LoadCategories(MwmContext & context, vector<strings::UniString> const & categories)
+CBV Geocoder::LoadCategories(MwmContext & context, CategoriesSet const & categories)
{
ASSERT(context.m_handle.IsAlive(), ());
ASSERT(HasSearchIndex(context.m_value), ());
m_retrievalParams.m_tokens.resize(1);
m_retrievalParams.m_tokens[0].resize(1);
+
m_retrievalParams.m_prefixTokens.clear();
+ m_retrievalParams.m_types.resize(1);
+ m_retrievalParams.m_types[0].resize(1);
+
vector<CBV> cbvs;
- for_each(categories.begin(), categories.end(), [&](strings::UniString const & category)
- {
- m_retrievalParams.m_tokens[0][0] = category;
- CBV cbv(RetrieveAddressFeatures(context, m_cancellable, m_retrievalParams));
- if (!cbv.IsEmpty())
- cbvs.push_back(move(cbv));
- });
+ categories.ForEach([&](Category const & category) {
+ m_retrievalParams.m_tokens[0][0] = category.m_key;
+ m_retrievalParams.m_types[0][0] = category.m_type;
+
+ CBV cbv(RetrieveAddressFeatures(context, m_cancellable, m_retrievalParams));
+ if (!cbv.IsEmpty())
+ cbvs.push_back(move(cbv));
+ });
UniteCBVs(cbvs);
if (cbvs.empty())
@@ -1449,7 +1401,7 @@ CBV Geocoder::LoadStreets(MwmContext & context)
if (it != m_streetsCache.cend())
return it->second;
- auto streets = LoadCategories(context, StreetCategories::Instance().GetCategories());
+ auto streets = LoadCategories(context, m_streets);
m_streetsCache[mwmId] = streets;
return streets;
}
@@ -1458,7 +1410,7 @@ CBV Geocoder::LoadVillages(MwmContext & context)
{
if (!context.m_handle.IsAlive() || !HasSearchIndex(context.m_value))
return CBV();
- return LoadCategories(context, GetVillageCategories());
+ return LoadCategories(context, m_villages);
}
CBV Geocoder::RetrievePostcodeFeatures(MwmContext const & context, TokenSlice const & slice)
diff --git a/search/geocoder.hpp b/search/geocoder.hpp
index f74105f157..715e4250a3 100644
--- a/search/geocoder.hpp
+++ b/search/geocoder.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "search/cancel_exception.hpp"
+#include "search/categories_set.hpp"
#include "search/features_layer.hpp"
#include "search/features_layer_path_finder.hpp"
#include "search/geocoder_context.hpp"
@@ -256,7 +257,7 @@ private:
// UNCLASSIFIED objects that match to all currently unused tokens.
void MatchUnclassified(BaseContext & ctx, size_t curToken);
- CBV LoadCategories(MwmContext & context, vector<strings::UniString> const & categories);
+ CBV LoadCategories(MwmContext & context, CategoriesSet const & categories);
CBV LoadStreets(MwmContext & context);
@@ -279,6 +280,9 @@ private:
my::Cancellable const & m_cancellable;
+ CategoriesSet m_streets;
+ CategoriesSet m_villages;
+
// Geocoder params.
Params m_params;
diff --git a/search/processor.cpp b/search/processor.cpp
index 7f19f6ba7e..c23de90ece 100644
--- a/search/processor.cpp
+++ b/search/processor.cpp
@@ -627,7 +627,9 @@ void Processor::InitParams(QueryParams & params)
for (size_t i = 0; i < tokensCount; ++i)
params.m_tokens[i].push_back(m_tokens[i]);
- params.m_isCategorySynonym.assign(tokensCount + (m_prefix.empty() ? 0 : 1), false);
+ params.m_types.resize(tokensCount + (m_prefix.empty() ? 0 : 1));
+ for (auto & types : params.m_types)
+ types.clear();
// Add names of categories (and synonyms).
Classificator const & c = classif();
@@ -635,9 +637,10 @@ void Processor::InitParams(QueryParams & params)
{
QueryParams::TSynonymsVector & v = params.GetTokens(i);
+ params.m_types[i].push_back(t);
+
uint32_t const index = c.GetIndexForType(t);
v.push_back(FeatureTypeToString(index));
- params.m_isCategorySynonym[i] = true;
// v2-version MWM has raw classificator types in search index prefix, so
// do the hack: add synonyms for old convention if needed.
diff --git a/search/query_params.cpp b/search/query_params.cpp
index 8762b356ff..c5aa8b9bee 100644
--- a/search/query_params.cpp
+++ b/search/query_params.cpp
@@ -1,9 +1,6 @@
#include "search/query_params.hpp"
#include "indexer/feature_impl.hpp"
-#include "indexer/scales.hpp"
-
-#include "base/assert.hpp"
#include "std/algorithm.hpp"
@@ -11,6 +8,7 @@ namespace search
{
namespace
{
+// TODO (@y, @m): reuse this class in search::Processor.
class DoAddStreetSynonyms
{
public:
@@ -57,79 +55,44 @@ private:
};
} // namespace
-QueryParams::QueryParams() : m_scale(scales::GetUpperScale()) {}
-
void QueryParams::Clear()
{
m_tokens.clear();
m_prefixTokens.clear();
- m_isCategorySynonym.clear();
+ m_types.clear();
m_langs.clear();
m_scale = scales::GetUpperScale();
}
-void QueryParams::EraseTokens(vector<size_t> & eraseInds)
+bool QueryParams::IsCategorySynonym(size_t i) const
{
- eraseInds.erase(unique(eraseInds.begin(), eraseInds.end()), eraseInds.end());
- ASSERT(is_sorted(eraseInds.begin(), eraseInds.end()), ());
-
- // fill temporary vector
- vector<TSynonymsVector> newTokens;
-
- size_t skipI = 0;
- size_t const count = m_tokens.size();
- size_t const eraseCount = eraseInds.size();
- for (size_t i = 0; i < count; ++i)
- {
- if (skipI < eraseCount && eraseInds[skipI] == i)
- ++skipI;
- else
- newTokens.push_back(move(m_tokens[i]));
- }
-
- // assign to m_tokens
- newTokens.swap(m_tokens);
-
- if (skipI < eraseCount)
- {
- // it means that we need to skip prefix tokens
- ASSERT_EQUAL(skipI + 1, eraseCount, (eraseInds));
- ASSERT_EQUAL(eraseInds[skipI], count, (eraseInds));
- m_prefixTokens.clear();
- }
+ ASSERT_LESS(i, GetNumTokens(), ());
+ return !m_types[i].empty();
}
-void QueryParams::ProcessAddressTokens()
+bool QueryParams::IsPrefixToken(size_t i) const
{
- // Erases all number tokens.
- // Assumes that USA street name numbers are end with "st, nd, rd, th" suffixes.
- vector<size_t> toErase;
- ForEachToken([&toErase](QueryParams::TString const & s, size_t i)
- {
- if (feature::IsNumber(s))
- toErase.push_back(i);
- });
- EraseTokens(toErase);
-
- // Adds synonyms for N, NE, NW, etc.
- ForEachToken(DoAddStreetSynonyms(*this));
+ ASSERT_LESS(i, GetNumTokens(), ());
+ return i == m_tokens.size();
}
QueryParams::TSynonymsVector const & QueryParams::GetTokens(size_t i) const
{
- ASSERT_LESS_OR_EQUAL(i, m_tokens.size(), ());
+ ASSERT_LESS(i, GetNumTokens(), ());
return i < m_tokens.size() ? m_tokens[i] : m_prefixTokens;
}
QueryParams::TSynonymsVector & QueryParams::GetTokens(size_t i)
{
- ASSERT_LESS_OR_EQUAL(i, m_tokens.size(), ());
+ ASSERT_LESS(i, GetNumTokens(), ());
return i < m_tokens.size() ? m_tokens[i] : m_prefixTokens;
}
bool QueryParams::IsNumberTokens(size_t start, size_t end) const
{
ASSERT_LESS(start, end, ());
+ ASSERT_LESS_OR_EQUAL(end, GetNumTokens(), ());
+
for (; start != end; ++start)
{
bool number = false;
@@ -148,24 +111,6 @@ bool QueryParams::IsNumberTokens(size_t start, size_t end) const
return true;
}
-template <class ToDo>
-void QueryParams::ForEachToken(ToDo && toDo)
-{
- size_t const count = m_tokens.size();
- for (size_t i = 0; i < count; ++i)
- {
- ASSERT(!m_tokens[i].empty(), ());
- ASSERT(!m_tokens[i].front().empty(), ());
- toDo(m_tokens[i].front(), i);
- }
-
- if (!m_prefixTokens.empty())
- {
- ASSERT(!m_prefixTokens.front().empty(), ());
- toDo(m_prefixTokens.front(), count);
- }
-}
-
string DebugPrint(search::QueryParams const & params)
{
ostringstream os;
diff --git a/search/query_params.hpp b/search/query_params.hpp
index 2f00bc6c34..4d537a7a8e 100644
--- a/search/query_params.hpp
+++ b/search/query_params.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include "indexer/scales.hpp"
+
+#include "base/assert.hpp"
#include "base/string_utils.hpp"
#include "std/cstdint.hpp"
@@ -14,39 +17,33 @@ struct QueryParams
using TSynonymsVector = vector<TString>;
using TLangsSet = unordered_set<int8_t>;
- vector<TSynonymsVector> m_tokens;
- TSynonymsVector m_prefixTokens;
- vector<bool> m_isCategorySynonym;
+ QueryParams() = default;
- TLangsSet m_langs;
- int m_scale;
+ inline size_t GetNumTokens() const
+ {
+ return m_prefixTokens.empty() ? m_tokens.size() : m_tokens.size() + 1;
+ }
- QueryParams();
+ inline bool IsEmpty() const { return GetNumTokens() == 0; }
+ inline bool IsLangExist(int8_t lang) const { return m_langs.count(lang) != 0; }
void Clear();
- /// @param[in] eraseInds Sorted vector of token's indexes.
- void EraseTokens(vector<size_t> & eraseInds);
-
- void ProcessAddressTokens();
-
- inline bool IsEmpty() const { return (m_tokens.empty() && m_prefixTokens.empty()); }
- inline bool CanSuggest() const { return (m_tokens.empty() && !m_prefixTokens.empty()); }
- inline bool IsLangExist(int8_t l) const { return (m_langs.count(l) > 0); }
-
+ bool IsCategorySynonym(size_t i) const;
+ bool IsPrefixToken(size_t i) const;
TSynonymsVector const & GetTokens(size_t i) const;
TSynonymsVector & GetTokens(size_t i);
- inline size_t GetNumTokens() const
- {
- return m_prefixTokens.empty() ? m_tokens.size() : m_tokens.size() + 1;
- }
- /// @return true if all tokens in [start, end) range has number synonym.
+ // Returns true if all tokens in [start, end) range has intergral
+ // synonyms.
bool IsNumberTokens(size_t start, size_t end) const;
-private:
- template <class ToDo>
- void ForEachToken(ToDo && toDo);
+ vector<TSynonymsVector> m_tokens;
+ TSynonymsVector m_prefixTokens;
+ vector<vector<uint32_t>> m_types;
+
+ TLangsSet m_langs;
+ int m_scale = scales::GetUpperScale();
};
string DebugPrint(search::QueryParams const & params);
diff --git a/search/retrieval.cpp b/search/retrieval.cpp
index 87051e731b..4f77bf35d5 100644
--- a/search/retrieval.cpp
+++ b/search/retrieval.cpp
@@ -10,6 +10,7 @@
#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"
@@ -106,7 +107,8 @@ unique_ptr<coding::CompressedBitVector> SortFeaturesAndBuildCBV(vector<uint64_t>
return coding::CompressedBitVectorBuilder::FromBitPositions(move(features));
}
-/// Check that any from first matches any from second.
+// Checks that any from the |second| matches any from the |first|. In
+// ambiguous case when |second| is empty, returns true.
template <class TComp, class T>
bool IsFirstMatchesSecond(vector<T> const & first, vector<T> const & second, TComp const & comp)
{
@@ -124,34 +126,54 @@ bool IsFirstMatchesSecond(vector<T> const & first, vector<T> const & second, TCo
return false;
}
-bool MatchFeatureByName(FeatureType const & ft, QueryParams const & params)
+bool MatchFeatureByNameAndType(FeatureType const & ft, QueryParams const & params)
{
using namespace strings;
+ auto const prefixMatch = [](UniString const & s1, UniString const & s2) {
+ return StartsWith(s1, s2);
+ };
+
+ auto const fullMatch = [](UniString const & s1, UniString const & s2) { return s1 == s2; };
+
+ feature::TypesHolder th(ft);
+
bool matched = false;
ft.ForEachName([&](int8_t lang, string const & utf8Name)
{
- if (utf8Name.empty() || params.m_langs.count(lang) == 0)
- return true;
+ if (utf8Name.empty() || !params.IsLangExist(lang))
+ return true /* continue ForEachName */;
vector<UniString> nameTokens;
NormalizeAndTokenizeString(utf8Name, nameTokens, Delimiters());
- auto const matchPrefix = [](UniString const & s1, UniString const & s2)
+ for (size_t i = 0; i < params.GetNumTokens(); ++i)
{
- return StartsWith(s1, s2);
- };
- if (!IsFirstMatchesSecond(nameTokens, params.m_prefixTokens, matchPrefix))
- return true;
-
- for (auto const & synonyms : params.m_tokens)
- {
- if (!IsFirstMatchesSecond(nameTokens, synonyms, equal_to<UniString>()))
- return true;
+ bool const isPrefix = params.IsPrefixToken(i);
+ auto const & syms = params.GetTokens(i);
+
+ if (!IsFirstMatchesSecond(nameTokens, syms, isPrefix ? prefixMatch : fullMatch))
+ {
+ // Checks types in case of names mismatch.
+ auto const & types = params.m_types[i];
+ bool typeMatched = false;
+
+ for (auto const type : types)
+ {
+ if (th.Has(type))
+ {
+ typeMatched = true;
+ break;
+ }
+ }
+
+ if (!typeMatched)
+ return true /* continue ForEachName */;
+ }
}
matched = true;
- return false;
+ return false /* break ForEachName */;
});
return matched;
@@ -211,7 +233,7 @@ unique_ptr<coding::CompressedBitVector> RetrieveAddressFeaturesImpl(
collector);
});
holder.ForEachModifiedOrCreated([&](FeatureType & ft, uint64_t index) {
- if (MatchFeatureByName(ft, params))
+ if (MatchFeatureByNameAndType(ft, params))
features.push_back(index);
});
return SortFeaturesAndBuildCBV(move(features));
diff --git a/search/search.pro b/search/search.pro
index 4fe9079e2d..da1ee701ea 100644
--- a/search/search.pro
+++ b/search/search.pro
@@ -12,6 +12,7 @@ HEADERS += \
algos.hpp \
approximate_string_match.hpp \
cancel_exception.hpp \
+ categories_set.hpp \
cbv.hpp \
common.hpp \
displayed_categories.hpp \
diff --git a/search/search_integration_tests/helpers.cpp b/search/search_integration_tests/helpers.cpp
index 1aa205d0b0..79768d0f2d 100644
--- a/search/search_integration_tests/helpers.cpp
+++ b/search/search_integration_tests/helpers.cpp
@@ -4,6 +4,7 @@
#include "search/search_tests_support/test_search_request.hpp"
#include "indexer/classificator_loader.hpp"
+#include "indexer/indexer_tests_support/helpers.hpp"
#include "indexer/map_style.hpp"
#include "indexer/map_style_reader.hpp"
#include "indexer/scales.hpp"
@@ -26,6 +27,7 @@ SearchTest::SearchTest()
, m_engine(make_unique<storage::CountryInfoGetterForTesting>(), make_unique<ProcessorFactory>(),
Engine::Params())
{
+ indexer::tests_support::SetUpEditorForTesting(m_engine);
}
SearchTest::~SearchTest()
diff --git a/search/search_integration_tests/helpers.hpp b/search/search_integration_tests/helpers.hpp
index 3dfafe85d5..0160bbfe7f 100644
--- a/search/search_integration_tests/helpers.hpp
+++ b/search/search_integration_tests/helpers.hpp
@@ -8,6 +8,10 @@
#include "storage/country_decl.hpp"
#include "storage/country_info_getter.hpp"
+#include "indexer/feature.hpp"
+#include "indexer/feature_decl.hpp"
+#include "indexer/index.hpp"
+#include "indexer/indexer_tests_support/helpers.hpp"
#include "indexer/mwm_set.hpp"
#include "geometry/rect2d.hpp"
@@ -16,7 +20,7 @@
#include "platform/local_country_file.hpp"
#include "platform/local_country_file_utils.hpp"
-#include "base/logging.hpp"
+#include "base/assert.hpp"
#include "std/unique_ptr.hpp"
#include "std/vector.hpp"
@@ -37,6 +41,7 @@ public:
using TRules = vector<shared_ptr<tests_support::MatchingRule>>;
SearchTest();
+
~SearchTest();
// Registers country in internal records. Note that physical country
@@ -86,6 +91,15 @@ public:
return BuildMwm(name, feature::DataHeader::country, forward<TBuildFn>(fn));
}
+ template <typename TEditorFn>
+ void EditFeature(FeatureID const & id, TEditorFn && fn)
+ {
+ Index::FeaturesLoaderGuard loader(m_engine, id.m_mwmId);
+ FeatureType ft;
+ CHECK(loader.GetFeatureByIndex(id.m_index, ft), ());
+ indexer::tests_support::EditFeature(ft, forward<TEditorFn>(fn));
+ }
+
inline void SetViewport(m2::RectD const & viewport) { m_viewport = viewport; }
bool ResultsMatch(string const & query, TRules const & rules);
diff --git a/search/search_integration_tests/search_edited_features_test.cpp b/search/search_integration_tests/search_edited_features_test.cpp
new file mode 100644
index 0000000000..5f8d70eb6a
--- /dev/null
+++ b/search/search_integration_tests/search_edited_features_test.cpp
@@ -0,0 +1,64 @@
+#include "testing/testing.hpp"
+
+#include "generator/generator_tests_support/test_feature.hpp"
+
+#include "search/search_integration_tests/helpers.hpp"
+#include "search/search_tests_support/test_results_matching.hpp"
+
+#include "indexer/editable_map_object.hpp"
+
+#include "geometry/point2d.hpp"
+
+#include "coding/multilang_utf8_string.hpp"
+
+#include "std/shared_ptr.hpp"
+
+using namespace generator::tests_support;
+using namespace search::tests_support;
+using namespace search;
+
+using TRules = vector<shared_ptr<MatchingRule>>;
+
+namespace
+{
+class SearchEditedFeaturesTest : public SearchTest
+{
+};
+
+UNIT_CLASS_TEST(SearchEditedFeaturesTest, Smoke)
+{
+ TestCity city(m2::PointD(0, 0), "Quahog", "default", 100 /* rank */);
+ TestPOI cafe(m2::PointD(0, 0), "Bar", "default");
+
+ BuildWorld([&](TestMwmBuilder & builder) { builder.Add(city); });
+
+ auto const id = BuildCountry("Wonderland", [&](TestMwmBuilder & builder) { builder.Add(cafe); });
+
+ FeatureID cafeId(id, 0 /* index */);
+
+ {
+ TRules const rules = {ExactMatch(id, cafe)};
+
+ TEST(ResultsMatch("Bar", rules), ());
+ TEST(ResultsMatch("Drunken", TRules{}), ());
+
+ EditFeature(cafeId, [](osm::EditableMapObject & emo) {
+ emo.SetName("The Drunken Clam", StringUtf8Multilang::kEnglishCode);
+ });
+
+ TEST(ResultsMatch("Bar", rules), ());
+ TEST(ResultsMatch("Drunken", rules), ());
+ }
+
+ {
+ TRules const rules = {ExactMatch(id, cafe)};
+
+ TEST(ResultsMatch("Wifi", TRules{}), ());
+
+ EditFeature(cafeId, [](osm::EditableMapObject & emo) { emo.SetInternet(osm::Internet::Wlan); });
+
+ TEST(ResultsMatch("Wifi", rules), ());
+ TEST(ResultsMatch("wifi bar quahog", rules), ());
+ }
+}
+} // namespace
diff --git a/search/search_integration_tests/search_integration_tests.pro b/search/search_integration_tests/search_integration_tests.pro
index e8ae629a85..1d2165831f 100644
--- a/search/search_integration_tests/search_integration_tests.pro
+++ b/search/search_integration_tests/search_integration_tests.pro
@@ -7,9 +7,9 @@ TEMPLATE = app
ROOT_DIR = ../..
-DEPENDENCIES = search_tests_support generator_tests_support generator routing search storage \
- stats_client indexer platform editor geometry coding base tess2 protobuf \
- tomcrypt jansson succinct pugixml opening_hours
+DEPENDENCIES = generator_tests_support search_tests_support indexer_tests_support generator \
+ routing search storage stats_client indexer platform editor geometry coding base \
+ tess2 protobuf tomcrypt jansson succinct pugixml opening_hours
include($$ROOT_DIR/common.pri)
@@ -25,6 +25,7 @@ SOURCES += \
interactive_search_test.cpp \
pre_ranker_test.cpp \
processor_test.cpp \
+ search_edited_features_test.cpp \
smoke_test.cpp \
HEADERS += \
diff --git a/search/token_slice.cpp b/search/token_slice.cpp
index 6705b884fe..33ab649498 100644
--- a/search/token_slice.cpp
+++ b/search/token_slice.cpp
@@ -51,7 +51,7 @@ TokenSliceNoCategories::TokenSliceNoCategories(QueryParams const & params, size_
m_indexes.reserve(endToken - startToken);
for (size_t i = startToken; i < endToken; ++i)
{
- if (!m_params.m_isCategorySynonym[i])
+ if (!m_params.IsCategorySynonym(i))
m_indexes.push_back(i);
}
}