diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index edb87542d5..3c86bbd360 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -290,12 +290,13 @@ extern "C" } JNIEXPORT jboolean JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunSearch( - JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp, - jboolean hasPosition, jdouble lat, jdouble lon) + JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory, + jstring lang, jlong timestamp, jboolean hasPosition, jdouble lat, jdouble lon) { search::EverywhereSearchParams params; params.m_query = jni::ToNativeString(env, bytes); params.m_inputLocale = jni::ToNativeString(env, lang); + params.m_isCategory = isCategory; params.m_onResults = bind(&OnResults, _1, _2, timestamp, false, hasPosition, lat, lon); bool const searchStarted = g_framework->NativeFramework()->GetSearchAPI().SearchEverywhere(params); if (searchStarted) @@ -304,12 +305,13 @@ extern "C" } JNIEXPORT void JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunInteractiveSearch( - JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp, - jboolean isMapAndTable) + JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory, + jstring lang, jlong timestamp, jboolean isMapAndTable) { search::ViewportSearchParams vparams; vparams.m_query = jni::ToNativeString(env, bytes); vparams.m_inputLocale = jni::ToNativeString(env, lang); + vparams.m_isCategory = isCategory; // TODO (@alexzatsepin): set up vparams.m_onCompleted here and use // HotelsClassifier for hotel queries detection. diff --git a/android/src/com/mapswithme/maps/routing/SearchWheel.java b/android/src/com/mapswithme/maps/routing/SearchWheel.java index 5241234b95..4085920c5c 100644 --- a/android/src/com/mapswithme/maps/routing/SearchWheel.java +++ b/android/src/com/mapswithme/maps/routing/SearchWheel.java @@ -280,7 +280,8 @@ class SearchWheel implements View.OnClickListener { mCurrentOption = searchOption; final String query = mFrame.getContext().getString(searchOption.mQueryId); - SearchEngine.INSTANCE.searchInteractive(mFrame.getContext(), query, System.nanoTime(), false /* isMapAndTable */); + // Category request from navigation search wheel. + SearchEngine.INSTANCE.searchInteractive(mFrame.getContext(), query, true, System.nanoTime(), false); SearchEngine.INSTANCE.setQuery(query); refreshSearchButtonImage(); toggleSearchLayout(); diff --git a/android/src/com/mapswithme/maps/search/SearchEngine.java b/android/src/com/mapswithme/maps/search/SearchEngine.java index ae2c97197a..94078ac24f 100644 --- a/android/src/com/mapswithme/maps/search/SearchEngine.java +++ b/android/src/com/mapswithme/maps/search/SearchEngine.java @@ -127,25 +127,26 @@ public enum SearchEngine implements NativeSearchListener, * @return whether search was actually started. */ @MainThread - public boolean search(@NonNull Context context, String query, long timestamp, boolean hasLocation, - double lat, double lon) + public boolean search(@NonNull Context context, String query, boolean isCategory, + long timestamp, boolean hasLocation, double lat, double lon) { - return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), Language.getKeyboardLocale(context), - timestamp, hasLocation, lat, lon); + return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), isCategory, + Language.getKeyboardLocale(context), timestamp, hasLocation, lat, lon); } @MainThread - public void searchInteractive(@NonNull String query, @NonNull String locale, long timestamp, - boolean isMapAndTable) + public void searchInteractive(@NonNull String query, boolean isCategory, @NonNull String locale, + long timestamp, boolean isMapAndTable) { - nativeRunInteractiveSearch(query.getBytes(StandardCharsets.UTF_8), locale, timestamp, isMapAndTable); + nativeRunInteractiveSearch(query.getBytes(StandardCharsets.UTF_8), isCategory, + locale, timestamp, isMapAndTable); } @MainThread - public void searchInteractive(@NonNull Context context, @NonNull String query, long timestamp, - boolean isMapAndTable) + public void searchInteractive(@NonNull Context context, @NonNull String query, boolean isCategory, + long timestamp, boolean isMapAndTable) { - searchInteractive(query, Language.getKeyboardLocale(context), timestamp, isMapAndTable); + searchInteractive(query, isCategory, Language.getKeyboardLocale(context), timestamp, isMapAndTable); } @MainThread @@ -223,13 +224,15 @@ public enum SearchEngine implements NativeSearchListener, /** * @param bytes utf-8 formatted bytes of query. */ - private static native boolean nativeRunSearch(byte[] bytes, String language, long timestamp, boolean hasLocation, + private static native boolean nativeRunSearch(byte[] bytes, boolean isCategory, + String language, long timestamp, boolean hasLocation, double lat, double lon); /** * @param bytes utf-8 formatted query bytes */ - private static native void nativeRunInteractiveSearch(byte[] bytes, String language, long timestamp, + private static native void nativeRunInteractiveSearch(byte[] bytes, boolean isCategory, + String language, long timestamp, boolean isMapAndTable); /** diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 9529aed4dc..7e6a0829f3 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -348,15 +348,9 @@ public class SearchFragment extends BaseMwmFragment super.onDestroy(); } - private String getQuery() - { - return mToolbarController.getQuery(); - } - - void setQuery(String text) - { - mToolbarController.setQuery(text); - } + private String getQuery() { return mToolbarController.getQuery(); } + private boolean isCategory() { return mToolbarController.isCategory(); } + void setQuery(String text) { mToolbarController.setQuery(text); } private void readArguments() { @@ -436,12 +430,12 @@ public class SearchFragment extends BaseMwmFragment mLastQueryTimestamp = System.nanoTime(); SearchEngine.INSTANCE.searchInteractive( - query, !TextUtils.isEmpty(mInitialLocale) + query, isCategory(), !TextUtils.isEmpty(mInitialLocale) ? mInitialLocale : com.mapswithme.util.Language.getKeyboardLocale(requireContext()), mLastQueryTimestamp, false /* isMapAndTable */); + SearchEngine.INSTANCE.setQuery(query); Utils.navigateToParent(getActivity()); - } private void onSearchEnd() @@ -479,12 +473,13 @@ public class SearchFragment extends BaseMwmFragment mLastQueryTimestamp = System.nanoTime(); if (isTabletSearch()) { - SearchEngine.INSTANCE.searchInteractive(requireContext(), getQuery(), mLastQueryTimestamp, true /* isMapAndTable */); + SearchEngine.INSTANCE.searchInteractive(requireContext(), getQuery(), isCategory(), + mLastQueryTimestamp, true /* isMapAndTable */); } else { - if (!SearchEngine.INSTANCE.search(requireContext(), getQuery(), mLastQueryTimestamp, mLastPosition.valid, - mLastPosition.lat, mLastPosition.lon)) + if (!SearchEngine.INSTANCE.search(requireContext(), getQuery(), isCategory(), + mLastQueryTimestamp, mLastPosition.valid, mLastPosition.lat, mLastPosition.lon)) { return; } @@ -518,7 +513,7 @@ public class SearchFragment extends BaseMwmFragment @Override public void onSearchCategorySelected(@Nullable String category) { - mToolbarController.setQuery(category); + mToolbarController.setQuery(category, true); } private void refreshSearchResults(@NonNull SearchResult[] results) diff --git a/android/src/com/mapswithme/maps/widget/SearchToolbarController.java b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java index 90b9d164f1..12db4194de 100644 --- a/android/src/com/mapswithme/maps/widget/SearchToolbarController.java +++ b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java @@ -31,6 +31,7 @@ public class SearchToolbarController extends ToolbarController implements View.O private final View mBack; @NonNull private final EditText mQuery; + private boolean mFromCategory = false; @NonNull private final View mProgress; @NonNull @@ -157,13 +158,16 @@ public class SearchToolbarController extends ToolbarController implements View.O { return (UiUtils.isVisible(mSearchContainer) ? mQuery.getText().toString() : ""); } + public boolean isCategory() { return mFromCategory; } - public void setQuery(CharSequence query) + public void setQuery(CharSequence query, boolean fromCategory) { + mFromCategory = fromCategory; mQuery.setText(query); if (!TextUtils.isEmpty(query)) mQuery.setSelection(query.length()); } + public void setQuery(CharSequence query) { setQuery(query, false); } public void clear() { diff --git a/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm b/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm index 2f13ff609c..b8b4f83aa9 100644 --- a/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm +++ b/iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm @@ -29,7 +29,8 @@ API_AVAILABLE(ios(12.0)) self.inputLocale = inputLocale; self.lastResults = @[]; self.completionHandler = completionHandler; - [MWMSearch searchQuery:text forInputLocale:inputLocale]; + /// @todo Didn't find pure category request in CarPlay. + [MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:NO]; } - (void)saveLastQuery { diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm index 6dcc05838e..74107c49a5 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm @@ -106,7 +106,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; return NO; self.searchManager.state = MWMSearchManagerStateTableSearch; - [self.searchManager searchText:text forInputLocale:locale]; + [self.searchManager searchText:text forInputLocale:locale withCategory:NO]; return YES; } diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm index 7c1bdbe05e..02f879ed97 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm @@ -193,7 +193,8 @@ BOOL defaultOrientation(CGSize const &size) { [MWMSearch setSearchOnMap:YES]; NSString *query = [kSearchButtonRequest.at(state) stringByAppendingString:@" "]; NSString *locale = [[AppInfo sharedInfo] languageId]; - [MWMSearch searchQuery:query forInputLocale:locale]; + // Category request from navigation search wheel. + [MWMSearch searchQuery:query forInputLocale:locale withCategory:YES]; [self setSearchState:state animated:YES]; }; diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 3bc8ea3e0b..edc489da2a 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -14,7 +14,7 @@ struct ProductInfo; + (void)removeObserver:(id)observer; + (void)saveQuery:(NSString *)query forInputLocale:(NSString *)inputLocale; -+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale; ++ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory; + (void)showResult:(search::Result const &)result; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index 4b99ddb60a..b16c51382f 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -132,7 +132,7 @@ using Observers = NSHashTable; GetFramework().GetSearchAPI().SaveSearchQuery(make_pair(locale, text)); } -+ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale { ++ (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory { if (!query) return; @@ -142,11 +142,15 @@ using Observers = NSHashTable; manager->m_everywhereParams.m_inputLocale = locale; manager->m_viewportParams.m_inputLocale = locale; } + manager.lastQuery = query.precomposedStringWithCompatibilityMapping; std::string const text = manager.lastQuery.UTF8String; manager->m_everywhereParams.m_query = text; manager->m_viewportParams.m_query = text; manager.textChanged = YES; + + manager->m_everywhereParams.m_isCategory = manager->m_viewportParams.m_isCategory = (isCategory == YES); + [manager update]; } diff --git a/iphone/Maps/UI/Search/MWMSearchManager.h b/iphone/Maps/UI/Search/MWMSearchManager.h index 0018d916e6..7b5ed4346e 100644 --- a/iphone/Maps/UI/Search/MWMSearchManager.h +++ b/iphone/Maps/UI/Search/MWMSearchManager.h @@ -22,7 +22,7 @@ typedef NS_ENUM(NSInteger, MWMSearchManagerRoutingTooltipSearch) { @property(nonnull, nonatomic) IBOutletCollection(UIView) NSArray *topViews; -- (void)searchText:(nonnull NSString *)text forInputLocale:(nullable NSString *)locale; +- (void)searchText:(nonnull NSString *)text forInputLocale:(nullable NSString *)locale withCategory:(BOOL)isCategory; #pragma mark - Layout diff --git a/iphone/Maps/UI/Search/MWMSearchManager.mm b/iphone/Maps/UI/Search/MWMSearchManager.mm index 3b8d834c18..d78d39a872 100644 --- a/iphone/Maps/UI/Search/MWMSearchManager.mm +++ b/iphone/Maps/UI/Search/MWMSearchManager.mm @@ -97,7 +97,7 @@ using Observers = NSHashTable; NSString *text = textField.text; if (text.length > 0) { [self beginSearch]; - [MWMSearch searchQuery:text forInputLocale:textField.textInputMode.primaryLanguage]; + [MWMSearch searchQuery:text forInputLocale:textField.textInputMode.primaryLanguage withCategory:NO]; } else { [self endSearch]; } @@ -135,11 +135,11 @@ using Observers = NSHashTable; #pragma mark - MWMSearchTabbedViewProtocol -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale { +- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory { [self beginSearch]; self.searchTextField.text = text; NSString *inputLocale = locale ?: self.searchTextField.textInputMode.primaryLanguage; - [MWMSearch searchQuery:text forInputLocale:inputLocale]; + [MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:isCategory]; } - (void)dismissKeyboard { @@ -176,7 +176,7 @@ using Observers = NSHashTable; if (self.state == MWMSearchManagerStateTableSearch || self.state == MWMSearchManagerStateMapSearch) { NSString *text = self.searchTextField.text; if (text.length != 0) - [MWMSearch searchQuery:text forInputLocale:self.searchTextField.textInputMode.primaryLanguage]; + [MWMSearch searchQuery:text forInputLocale:self.searchTextField.textInputMode.primaryLanguage withCategory:NO]; } } @@ -413,8 +413,11 @@ using Observers = NSHashTable; } } -- (void)searchTabController:(MWMSearchTabViewController *)viewController didSearch:(NSString *)didSearch { - [self searchText:didSearch forInputLocale:[[AppInfo sharedInfo] languageId]]; +- (void)searchTabController:(MWMSearchTabViewController *)viewController + didSearch:(NSString *)didSearch + withCategory:(BOOL)isCategory +{ + [self searchText:didSearch forInputLocale:[[AppInfo sharedInfo] languageId] withCategory:isCategory]; } - (MWMSearchTableViewController *)tableViewController { diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm index ed72af127f..0ce971bfe1 100644 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm +++ b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm @@ -129,7 +129,8 @@ NSString *GetLocalizedTypeName(search::Result const &result) { case MWMSearchItemTypeSuggestion: { auto const &suggestion = [MWMSearch resultWithContainerIndex:containerIndex]; NSString *suggestionString = @(suggestion.GetSuggestionString().c_str()); - [delegate searchText:suggestionString forInputLocale:nil]; + /// @todo Pass withCategory:YES if we tap on category suggestion (not street or city)? + [delegate searchText:suggestionString forInputLocale:nil withCategory:NO]; } } } diff --git a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h b/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h index aca5de9643..98147899b9 100644 --- a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h +++ b/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h @@ -6,7 +6,7 @@ @property(nonatomic) MWMSearchManagerState state; -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale; +- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory; - (void)dismissKeyboard; @end diff --git a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift index 3ccb06f370..5d60214d05 100644 --- a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift +++ b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift @@ -1,6 +1,7 @@ +import CoreFoundation @objc(MWMSearchTabViewControllerDelegate) protocol SearchTabViewControllerDelegate: AnyObject { - func searchTabController(_ viewContoller: SearchTabViewController, didSearch: String) + func searchTabController(_ viewContoller: SearchTabViewController, didSearch: String, withCategory: Bool) } @objc(MWMSearchTabViewController) @@ -53,13 +54,13 @@ extension SearchTabViewController: SearchCategoriesViewControllerDelegate { func categoriesViewController(_ viewController: SearchCategoriesViewController, didSelect category: String) { let query = L(category) + " " - delegate?.searchTabController(self, didSearch: query) + delegate?.searchTabController(self, didSearch: query, withCategory: true) } } extension SearchTabViewController: SearchHistoryViewControllerDelegate { func searchHistoryViewController(_ viewController: SearchHistoryViewController, didSelect query: String) { - delegate?.searchTabController(self, didSearch: query) + delegate?.searchTabController(self, didSearch: query, withCategory: false) } } diff --git a/map/everywhere_search_params.hpp b/map/everywhere_search_params.hpp index d26a0a7221..7a98bf6a8c 100644 --- a/map/everywhere_search_params.hpp +++ b/map/everywhere_search_params.hpp @@ -21,6 +21,7 @@ struct EverywhereSearchParams std::string m_query; std::string m_inputLocale; std::optional m_timeout; + bool m_isCategory = false; OnResults m_onResults; }; diff --git a/map/search_api.cpp b/map/search_api.cpp index 62ca2fbfbb..f11d6c5939 100644 --- a/map/search_api.cpp +++ b/map/search_api.cpp @@ -184,6 +184,7 @@ bool SearchAPI::SearchEverywhere(EverywhereSearchParams const & params) p.m_suggestsEnabled = true; p.m_needAddress = true; p.m_needHighlighting = true; + p.m_categorialRequest = params.m_isCategory; if (params.m_timeout) p.m_timeout = *params.m_timeout; @@ -211,6 +212,7 @@ bool SearchAPI::SearchInViewport(ViewportSearchParams const & params) p.m_suggestsEnabled = false; p.m_needAddress = false; p.m_needHighlighting = false; + p.m_categorialRequest = params.m_isCategory; if (params.m_timeout) p.m_timeout = *params.m_timeout; diff --git a/map/viewport_search_params.hpp b/map/viewport_search_params.hpp index c13c9199d4..87b23619a1 100644 --- a/map/viewport_search_params.hpp +++ b/map/viewport_search_params.hpp @@ -17,6 +17,7 @@ struct ViewportSearchParams std::string m_query; std::string m_inputLocale; std::optional m_timeout; + bool m_isCategory = false; OnStarted m_onStarted; OnCompleted m_onCompleted; diff --git a/search/processor.cpp b/search/processor.cpp index 1da98acdaf..3f67a94166 100644 --- a/search/processor.cpp +++ b/search/processor.cpp @@ -253,7 +253,7 @@ void Processor::SetInputLocale(string const & locale) m_inputLocaleCode = CategoriesHolder::MapLocaleToInteger(locale); } -void Processor::SetQuery(string const & query) +void Processor::SetQuery(string const & query, bool categorialRequest /* = false */) { m_query = query; m_tokens.clear(); @@ -319,20 +319,23 @@ void Processor::SetQuery(string const & query) // Get preferred types to show in results. m_preferredTypes.clear(); auto const tokenSlice = QuerySliceOnRawStrings(m_tokens, m_prefix); - m_isCategorialRequest = FillCategories(tokenSlice, GetCategoryLocales(), m_categories, m_preferredTypes); - if (!m_isCategorialRequest) + m_isCategorialRequest = categorialRequest; + + auto const locales = GetCategoryLocales(); + if (!FillCategories(tokenSlice, locales, m_categories, m_preferredTypes)) { // Try to match query to cuisine categories. - bool const isCuisineRequest = FillCategories( - tokenSlice, GetCategoryLocales(), GetDefaultCuisineCategories(), m_cuisineTypes); - - if (isCuisineRequest) + if (FillCategories(tokenSlice, locales, GetDefaultCuisineCategories(), m_cuisineTypes)) { + /// @todo What if I'd like to find "Burger" street? @see "BurgerStreet" test. m_isCategorialRequest = true; m_preferredTypes = ftypes::IsEatChecker::Instance().GetTypes(); } + } + if (!m_isCategorialRequest) + { // Assign tokens and prefix to scorer. m_keywordsScorer.SetKeywords(m_tokens.data(), m_tokens.size(), m_prefix); @@ -601,7 +604,7 @@ void Processor::Search(SearchParams const & params) SetInputLocale(params.m_inputLocale); - SetQuery(params.m_query); + SetQuery(params.m_query, params.m_categorialRequest); SetViewport(viewport); // Used to store the earliest available cancellation status: @@ -802,12 +805,9 @@ void Processor::InitParams(QueryParams & params) const else params.InitWithPrefix(m_tokens.begin(), m_tokens.end(), m_prefix); - // Add names of categories (and synonyms). Classificator const & c = classif(); - auto addCategorySynonyms = [&](size_t i, uint32_t t) { - uint32_t const index = c.GetIndexForType(t); - params.GetTypeIndices(i).push_back(index); - }; + + // Add names of categories (and synonyms). auto const tokenSlice = QuerySliceOnRawStrings(m_tokens, m_prefix); params.SetCategorialRequest(m_isCategorialRequest); if (m_isCategorialRequest) @@ -822,11 +822,14 @@ void Processor::InitParams(QueryParams & params) const else { // todo(@m, @y). Shall we match prefix tokens for categories? - ForEachCategoryTypeFuzzy(tokenSlice, addCategorySynonyms); + ForEachCategoryTypeFuzzy(tokenSlice, [&c, ¶ms](size_t i, uint32_t t) + { + uint32_t const index = c.GetIndexForType(t); + params.GetTypeIndices(i).push_back(index); + }); } - // Remove all type indices for streets, as they're considired - // individually. + // Remove all type indices for streets, as they're considired individually. for (size_t i = 0; i < params.GetNumTokens(); ++i) { auto & token = params.GetToken(i); @@ -837,8 +840,10 @@ void Processor::InitParams(QueryParams & params) const for (size_t i = 0; i < params.GetNumTokens(); ++i) base::SortUnique(params.GetTypeIndices(i)); - m_keywordsScorer.ForEachLanguage( - [&](int8_t lang) { params.GetLangs().Insert(static_cast(lang)); }); + m_keywordsScorer.ForEachLanguage([¶ms](int8_t lang) + { + params.GetLangs().Insert(static_cast(lang)); + }); } void Processor::InitGeocoder(Geocoder::Params & geocoderParams, SearchParams const & searchParams) diff --git a/search/processor.hpp b/search/processor.hpp index 564bfbc314..5c6cea03ab 100644 --- a/search/processor.hpp +++ b/search/processor.hpp @@ -70,7 +70,7 @@ public: void SetViewport(m2::RectD const & viewport); void SetPreferredLocale(std::string const & locale); void SetInputLocale(std::string const & locale); - void SetQuery(std::string const & query); + void SetQuery(std::string const & query, bool categorialRequest = false); inline std::string const & GetPivotRegion() const { return m_region; } inline bool IsEmptyQuery() const { return m_prefix.empty() && m_tokens.empty(); } diff --git a/search/search_integration_tests/processor_test.cpp b/search/search_integration_tests/processor_test.cpp index c9f6c0678d..45ce3bbbee 100644 --- a/search/search_integration_tests/processor_test.cpp +++ b/search/search_integration_tests/processor_test.cpp @@ -848,8 +848,8 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) { Rules const rules = {ExactMatch(wonderlandId, hotel1), ExactMatch(wonderlandId, hotel2)}; - TEST(ResultsMatch("hotel ", rules), ()); - TEST(ResultsMatch("hôTeL ", rules), ()); + TEST(CategoryMatch("hotel ", rules), ()); + TEST(CategoryMatch("hôTeL ", rules), ()); } { @@ -858,14 +858,13 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) // A category with a rare name. The word "Entertainment" // occurs exactly once in the list of categories and starts // with a capital letter. This is a test for normalization. - TEST(ResultsMatch("entertainment ", rules), ()); + TEST(CategoryMatch("entertainment ", rules), ()); } { Rules const rules = {ExactMatch(wonderlandId, hotel1), ExactMatch(wonderlandId, hotel2)}; - auto request = MakeRequest("гостиница ", "ru"); - TEST(ResultsMatch(request->Results(), rules), ()); + TEST(CategoryMatch("гостиница ", rules, "ru"), ()); } { @@ -874,8 +873,8 @@ UNIT_CLASS_TEST(ProcessorTest, TestCategorialSearch) // Hotel unicode character: both a synonym and and emoji. uint32_t const hotelEmojiCodepoint = 0x0001F3E8; strings::UniString const hotelUniString(1, hotelEmojiCodepoint); - auto request = MakeRequest(ToUtf8(hotelUniString)); - TEST(ResultsMatch(request->Results(), rules), ()); + + TEST(CategoryMatch(ToUtf8(hotelUniString), rules), ()); } { @@ -3027,4 +3026,31 @@ UNIT_CLASS_TEST(ProcessorTest, TestRankingInfo_MultipleOldNames) checkResult("Ленинград", "Санкт-Петербург (Ленинград)"); checkResult("Петроград", "Санкт-Петербург (Петроград)"); } + +/// @todo We are not ready for this test yet. +/* +UNIT_CLASS_TEST(ProcessorTest, BurgerStreet) +{ + string const countryName = "Wonderland"; + + TestPOI burger({1.0, 1.0}, "Dummy", "en"); + burger.SetTypes({{"amenity", "fast_food"}, {"cuisine", "burger"}}); + + TestStreet street({{2.0, 2.0}, {3.0, 3.0}}, "Burger street", "en"); + street.SetHighwayType("residential"); + + auto countryId = BuildCountry(countryName, [&](TestMwmBuilder & builder) + { + builder.Add(burger); + builder.Add(street); + }); + + SetViewport(m2::RectD(0, 0, 3, 3)); + + { + Rules rules{ExactMatch(countryId, burger), ExactMatch(countryId, street)}; + TEST(ResultsMatch("burger", rules), ()); + } +} +*/ } // namespace processor_test diff --git a/search/search_integration_tests/smoke_test.cpp b/search/search_integration_tests/smoke_test.cpp index cf187cde85..49d55c2247 100644 --- a/search/search_integration_tests/smoke_test.cpp +++ b/search/search_integration_tests/smoke_test.cpp @@ -68,8 +68,6 @@ public: { SetTypes({{"shop", "alcohol"}}); } - - ~AlcoShop() override = default; }; class SubwayStation : public TestPOI @@ -80,8 +78,6 @@ public: { SetTypes({{"railway", "station", "subway"}}); } - - ~SubwayStation() override = default; }; class SubwayStationMoscow : public TestPOI @@ -92,8 +88,6 @@ public: { SetTypes({{"railway", "station", "subway", "moscow"}}); } - - ~SubwayStationMoscow() override = default; }; UNIT_CLASS_TEST(SmokeTest, Smoke) @@ -105,7 +99,8 @@ UNIT_CLASS_TEST(SmokeTest, Smoke) AlcoShop brandyShop(m2::PointD(0, 1), "Brandy shop", "en"); AlcoShop vodkaShop(m2::PointD(1, 1), "Russian vodka shop", "en"); - auto id = BuildMwm(kCountryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) { + auto id = BuildMwm(kCountryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) + { builder.Add(wineShop); builder.Add(tequilaShop); builder.Add(brandyShop); @@ -117,18 +112,16 @@ UNIT_CLASS_TEST(SmokeTest, Smoke) SetViewport(m2::RectD(m2::PointD(0, 0), m2::PointD(100, 100))); { - Rules rules = {ExactMatch(id, tequilaShop)}; - /// @todo Passing "wine" will interpret request as "categorial" only - /// (see IsCategorialRequest() after adding craft-winery category). - /// Should avoid this strange logic in search core and pass "categorial" request flag via input SearchParams. - TEST(ResultsMatch("tequila ", rules), ()); + Rules rules = {ExactMatch(id, wineShop)}; + TEST(ResultsMatch("wine ", rules), ()); } - { - Rules rules = {ExactMatch(id, wineShop), ExactMatch(id, tequilaShop), - ExactMatch(id, brandyShop), ExactMatch(id, vodkaShop)}; - TEST(ResultsMatch("shop ", rules), ()); - } + Rules const allRule = { ExactMatch(id, wineShop), ExactMatch(id, tequilaShop), + ExactMatch(id, brandyShop), ExactMatch(id, vodkaShop) }; + + TEST(ResultsMatch("shop ", allRule), ()); + TEST(ResultsMatch("alcohol ", allRule), ()); + TEST(CategoryMatch("алкоголь", allRule, "ru"), ()); } UNIT_CLASS_TEST(SmokeTest, DeepCategoryTest) @@ -232,6 +225,7 @@ UNIT_CLASS_TEST(SmokeTest, CategoriesTest) notSupportedTypes.insert(cl.GetTypeByPath(tags)); auto const & holder = GetDefaultCategories(); + auto testCategory = [&](uint32_t type, CategoriesHolder::Category const &) { if (invisibleTypes.find(type) != invisibleTypes.end()) @@ -250,11 +244,10 @@ UNIT_CLASS_TEST(SmokeTest, CategoriesTest) auto id = BuildMwm(countryName, DataHeader::MapType::Country, [&](TestMwmBuilder & builder) { builder.AddSafe(poi); }); - SetViewport(m2::RectD(m2::PointD(0.0, 0.0), m2::PointD(2.0, 2.0))); { Rules rules = {ExactMatch(id, poi)}; - auto const query = holder.GetReadableFeatureType(type, CategoriesHolder::kEnglishCode) + " "; - TEST(ResultsMatch(query, categoryIsSearchable ? rules : Rules{}), (query)); + auto const query = holder.GetReadableFeatureType(type, CategoriesHolder::kEnglishCode); + TEST(CategoryMatch(query, categoryIsSearchable ? rules : Rules{}), (query)); } DeregisterMap(countryName); }; diff --git a/search/search_params.hpp b/search/search_params.hpp index 63323c21bb..ed4cfc9560 100644 --- a/search/search_params.hpp +++ b/search/search_params.hpp @@ -30,7 +30,7 @@ struct SearchParams static double constexpr kDefaultVillageSearchRadiusM = 2e5; using TimeDurationT = base::Timer::DurationT; - /// @todo Short timeouts leads to a non-working search on slow devices. Design a better solution. + /// @todo Short timeouts lead to a non-working search on slow devices. Design a better solution. static TimeDurationT constexpr kDefaultTimeout = std::chrono::seconds(8); static TimeDurationT constexpr kDefaultDesktopTimeout = std::chrono::seconds(8); @@ -92,6 +92,9 @@ struct SearchParams // Needed to highlight matching parts of search result names. bool m_needHighlighting = false; + /// True if you need *pure* category results, without names/addresses/etc matching. + bool m_categorialRequest = false; + bookmarks::GroupId m_bookmarksGroupId = bookmarks::kInvalidGroupId; // Amount of time after which the search is aborted. diff --git a/search/search_tests_support/helpers.cpp b/search/search_tests_support/helpers.cpp index d5954f3354..8f5560b5b0 100644 --- a/search/search_tests_support/helpers.cpp +++ b/search/search_tests_support/helpers.cpp @@ -10,6 +10,7 @@ namespace search { using namespace std; +using namespace tests_support; SearchTest::SearchTest() : m_scopedLog(LDEBUG) @@ -25,24 +26,30 @@ void SearchTest::RegisterCountry(string const & name, m2::RectD const & rect) infoGetter.AddCountry(storage::CountryDef(name, rect)); } -bool SearchTest::ResultsMatch(string const & query, - vector> const & rules) +bool SearchTest::ResultsMatch(string const & query, Rules const & rules) { return ResultsMatch(query, "en" /* locale */, rules); } -bool SearchTest::ResultsMatch(string const & query, string const & locale, - vector> const & rules) +bool SearchTest::CategoryMatch(std::string const & query, Rules const & rules, string const & locale) { - tests_support::TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + request.SetCategorial(); + request.Run(); return MatchResults(m_dataSource, rules, request.Results()); } -bool SearchTest::ResultsMatch(string const & query, Mode mode, - vector> const & rules) +bool SearchTest::ResultsMatch(string const & query, string const & locale, Rules const & rules) { - tests_support::TestSearchRequest request(m_engine, query, "en", mode, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + request.Run(); + return MatchResults(m_dataSource, rules, request.Results()); +} + +bool SearchTest::ResultsMatch(string const & query, Mode mode, Rules const & rules) +{ + TestSearchRequest request(m_engine, query, "en", mode, m_viewport); request.Run(); return MatchResults(m_dataSource, rules, request.Results()); } @@ -54,7 +61,7 @@ bool SearchTest::ResultsMatch(vector const & results, Rules cons bool SearchTest::ResultsMatch(SearchParams const & params, Rules const & rules) { - tests_support::TestSearchRequest request(m_engine, params); + TestSearchRequest request(m_engine, params); request.Run(); return ResultsMatch(request.Results(), rules); } @@ -66,26 +73,26 @@ bool SearchTest::ResultMatches(search::Result const & result, Rule const & rule) bool SearchTest::AlternativeMatch(string const & query, vector const & rulesList) { - tests_support::TestSearchRequest request(m_engine, query, "en", Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, "en", Mode::Everywhere, m_viewport); request.Run(); return tests_support::AlternativeMatch(m_dataSource, rulesList, request.Results()); } size_t SearchTest::GetResultsNumber(string const & query, string const & locale) { - tests_support::TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); + TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport); request.Run(); return request.Results().size(); } -unique_ptr SearchTest::MakeRequest(SearchParams params) +unique_ptr SearchTest::MakeRequest(SearchParams params) { - auto request = make_unique(m_engine, params); + auto request = make_unique(m_engine, params); request->Run(); return request; } -unique_ptr SearchTest::MakeRequest( +unique_ptr SearchTest::MakeRequest( string const & query, string const & locale /* = "en" */) { SearchParams params; @@ -95,11 +102,10 @@ unique_ptr SearchTest::MakeRequest( params.m_mode = Mode::Everywhere; params.m_needAddress = true; params.m_suggestsEnabled = false; - params.m_streetSearchRadiusM = tests_support::TestSearchRequest::kDefaultTestStreetSearchRadiusM; - params.m_villageSearchRadiusM = - tests_support::TestSearchRequest::kDefaultTestVillageSearchRadiusM; + params.m_streetSearchRadiusM = TestSearchRequest::kDefaultTestStreetSearchRadiusM; + params.m_villageSearchRadiusM = TestSearchRequest::kDefaultTestVillageSearchRadiusM; - auto request = make_unique(m_engine, params); + auto request = make_unique(m_engine, params); request->Run(); return request; } diff --git a/search/search_tests_support/helpers.hpp b/search/search_tests_support/helpers.hpp index 81754f2631..1dccfccbe7 100644 --- a/search/search_tests_support/helpers.hpp +++ b/search/search_tests_support/helpers.hpp @@ -32,6 +32,7 @@ public: inline void SetViewport(m2::RectD const & viewport) { m_viewport = viewport; } bool ResultsMatch(std::string const & query, Rules const & rules); + bool CategoryMatch(std::string const & query, Rules const & rules, std::string const & locale = "en"); bool ResultsMatch(std::string const & query, std::string const & locale, Rules const & rules); diff --git a/search/search_tests_support/test_search_engine.cpp b/search/search_tests_support/test_search_engine.cpp index 616a655534..f993d88144 100644 --- a/search/search_tests_support/test_search_engine.cpp +++ b/search/search_tests_support/test_search_engine.cpp @@ -26,7 +26,7 @@ TestSearchEngine::TestSearchEngine(DataSource & dataSource, Engine::Params const { } -weak_ptr<::search::ProcessorHandle> TestSearchEngine::Search(::search::SearchParams const & params) +weak_ptr TestSearchEngine::Search(SearchParams const & params) { return m_engine.Search(params); } diff --git a/search/search_tests_support/test_search_engine.hpp b/search/search_tests_support/test_search_engine.hpp index 8fbfb3e05d..aa76886c24 100644 --- a/search/search_tests_support/test_search_engine.hpp +++ b/search/search_tests_support/test_search_engine.hpp @@ -26,13 +26,13 @@ public: void LoadCitiesBoundaries() { m_engine.LoadCitiesBoundaries(); } - std::weak_ptr Search(search::SearchParams const & params); + std::weak_ptr Search(SearchParams const & params); storage::CountryInfoGetter & GetCountryInfoGetter() { return *m_infoGetter; } private: std::unique_ptr m_infoGetter; - search::Engine m_engine; + Engine m_engine; }; } // namespace tests_support } // namespace search diff --git a/search/search_tests_support/test_search_request.hpp b/search/search_tests_support/test_search_request.hpp index 5c5dca41a7..bf0f9ceb47 100644 --- a/search/search_tests_support/test_search_request.hpp +++ b/search/search_tests_support/test_search_request.hpp @@ -32,6 +32,8 @@ public: std::string const & locale, Mode mode, m2::RectD const & viewport); TestSearchRequest(TestSearchEngine & engine, SearchParams const & params); + void SetCategorial() { m_params.m_categorialRequest = true; } + // Initiates the search and waits for it to finish. void Run();