diff options
author | Maxim Pimenov <m@maps.me> | 2017-10-25 16:46:03 +0300 |
---|---|---|
committer | Yuri Gorshenin <mipt.vi002@gmail.com> | 2017-10-25 19:08:13 +0300 |
commit | 7a37590888d6f62537b1239f53b398fe853277c1 (patch) | |
tree | db0aa0a0d03bde9d504e587ff46902ee3c7baeba | |
parent | b34c94038ba0628a38ee073c2e512577eebaf336 (diff) |
Review fixes.
-rw-r--r-- | indexer/search_string_utils.cpp | 5 | ||||
-rw-r--r-- | indexer/search_string_utils.hpp | 4 | ||||
-rw-r--r-- | iphone/Maps/UI/PlacePage/PlacePageLayout/Content/BookingCells/MWMPPReviewCell.mm | 2 | ||||
-rw-r--r-- | search/common.hpp | 3 | ||||
-rw-r--r-- | search/highlighting.cpp | 5 | ||||
-rw-r--r-- | search/intermediate_result.cpp | 151 | ||||
-rw-r--r-- | search/intermediate_result.hpp | 36 | ||||
-rw-r--r-- | search/pre_ranker.cpp | 8 | ||||
-rw-r--r-- | search/processor.hpp | 5 | ||||
-rw-r--r-- | search/ranker.cpp | 26 | ||||
-rw-r--r-- | search/search_quality/search_quality_tool/search_quality_tool.cpp | 3 | ||||
-rw-r--r-- | search/search_tests/highlighting_tests.cpp | 20 | ||||
-rw-r--r-- | search/suggest.cpp | 2 |
13 files changed, 134 insertions, 136 deletions
diff --git a/indexer/search_string_utils.cpp b/indexer/search_string_utils.cpp index c1ecfaa816..231231f317 100644 --- a/indexer/search_string_utils.cpp +++ b/indexer/search_string_utils.cpp @@ -298,7 +298,7 @@ private: StreetsSynonymsHolder g_streets; } // namespace -void GetStringPrefix(string const & str, string & res) +string DropLastToken(string const & str) { search::Delimiters delims; using Iter = utf8::unchecked::iterator<string::const_iterator>; @@ -316,8 +316,7 @@ void GetStringPrefix(string const & str, string & res) iter = prev; } - // Assign the input string without prefix to result. - res.assign(str.begin(), iter.base()); + return string(str.begin(), iter.base()); } UniString GetStreetNameAsKey(string const & name) diff --git a/indexer/search_string_utils.hpp b/indexer/search_string_utils.hpp index cad9a1e361..a8692d60f0 100644 --- a/indexer/search_string_utils.hpp +++ b/indexer/search_string_utils.hpp @@ -55,8 +55,8 @@ bool TokenizeStringAndCheckIfLastTokenIsPrefix(std::string const & s, Tokens & t return TokenizeStringAndCheckIfLastTokenIsPrefix(NormalizeAndSimplifyString(s), tokens, delims); } -// Chops off the last query token (the "prefix" one) from |str| and stores the result in |res|. -void GetStringPrefix(std::string const & str, std::string & res); +// Chops off the last query token (the "prefix" one) from |str|. +std::string DropLastToken(std::string const & str); strings::UniString GetStreetNameAsKey(std::string const & name); diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/BookingCells/MWMPPReviewCell.mm b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/BookingCells/MWMPPReviewCell.mm index 1bc9dba24c..02e342f256 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/BookingCells/MWMPPReviewCell.mm +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Content/BookingCells/MWMPPReviewCell.mm @@ -33,7 +33,7 @@ formatter.dateStyle = NSDateFormatterLongStyle; self.date.text = [formatter stringFromDate: - [NSDate dateWithTimeIntervalSince1970:duration_cast<seconds>(review.m_date.time_since_epoch()).count()]]; + [NSDate dateWithTimeIntervalSince1970:std::chrono::duration_cast<std::chrono::seconds>(review.m_date.time_since_epoch()).count()]]; self.positiveReview.text = @(review.m_pros.c_str()); self.negativeReview.text = @(review.m_cons.c_str()); diff --git a/search/common.hpp b/search/common.hpp index c735c4e0ad..37839e5c0d 100644 --- a/search/common.hpp +++ b/search/common.hpp @@ -13,7 +13,8 @@ namespace search // the prefix and non-prefix tokens. using QueryTokens = buffer_vector<strings::UniString, 32>; -using Locales = base::SafeSmallSet<CategoriesHolder::kMaxSupportedLocaleIndex + 1>; +using Locales = + base::SafeSmallSet<static_cast<uint64_t>(CategoriesHolder::kMaxSupportedLocaleIndex) + 1>; /// Upper bound for max count of tokens for indexing and scoring. int constexpr MAX_TOKENS = 32; diff --git a/search/highlighting.cpp b/search/highlighting.cpp index bfaee0e4ab..cd92942c8c 100644 --- a/search/highlighting.cpp +++ b/search/highlighting.cpp @@ -44,14 +44,13 @@ public: namespace search { -// static void HighlightResult(QueryTokens const & tokens, strings::UniString const & prefix, Result & res) { using Iter = QueryTokens::const_iterator; using CombinedIter = CombinedIterator<Iter, strings::UniString>; - CombinedIter beg(tokens.begin(), tokens.end(), prefix.empty() ? 0 : &prefix); - CombinedIter end(tokens.end() /* cur */, tokens.end() /* end */, 0); + CombinedIter beg(tokens.begin(), tokens.end(), prefix.empty() ? nullptr : &prefix); + CombinedIter end(tokens.end() /* cur */, tokens.end() /* end */, nullptr); auto assignHighlightRange = [&](pair<uint16_t, uint16_t> const & range) { res.AddHighlightRange(range); }; diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index 71f55a89ac..dca4c26dad 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -19,16 +19,22 @@ #include "base/string_utils.hpp" #include "base/logging.hpp" +#include <cstddef> +#include <cstdint> + #include "3party/opening_hours/opening_hours.hpp" namespace search { namespace { +char const * const kEmptyRatingSymbol = "-"; +char const * const kPricingSymbol = "$"; + class SkipRegionInfo { - static size_t const m_count = 2; - uint32_t m_types[m_count]; + static size_t const kCount = 2; + uint32_t m_types[kCount]; public: SkipRegionInfo() @@ -37,10 +43,10 @@ public: {"place", "continent"}, {"place", "country"} }; - static_assert(m_count == ARRAY_SIZE(arr), ""); + static_assert(kCount == ARRAY_SIZE(arr), ""); Classificator const & c = classif(); - for (size_t i = 0; i < m_count; ++i) + for (size_t i = 0; i < kCount; ++i) m_types[i] = c.GetTypeByPath(vector<string>(arr[i], arr[i] + 2)); } @@ -56,60 +62,9 @@ public: }; } // namespace -char const * const kEmptyRatingSymbol = "-"; -char const * const kPricingSymbol = "$"; - -void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) -{ - if (meta.m_isInitialized) - return; - - feature::Metadata const & src = ft.GetMetadata(); - - meta.m_cuisine = src.Get(feature::Metadata::FMD_CUISINE); - - string const openHours = src.Get(feature::Metadata::FMD_OPEN_HOURS); - if (!openHours.empty()) - { - osmoh::OpeningHours const oh(openHours); - // TODO: We should check closed/open time for specific feature's timezone. - time_t const now = time(nullptr); - if (oh.IsValid() && !oh.IsUnknown(now)) - meta.m_isOpenNow = oh.IsOpen(now) ? osm::Yes : osm::No; - // In else case value us osm::Unknown, it's set in preview's constructor. - } - - if (strings::to_int(src.Get(feature::Metadata::FMD_STARS), meta.m_stars)) - meta.m_stars = my::clamp(meta.m_stars, 0, 5); - else - meta.m_stars = 0; - - bool const isSponsoredHotel = ftypes::IsBookingChecker::Instance()(ft); - meta.m_isSponsoredHotel = isSponsoredHotel; - meta.m_isHotel = ftypes::IsHotelChecker::Instance()(ft); - - if (isSponsoredHotel) - { - auto const r = src.Get(feature::Metadata::FMD_RATING); - char const * const rating = r.empty() ? kEmptyRatingSymbol : r.c_str(); - meta.m_hotelRating = rating; - - int pricing; - if (!strings::to_int(src.Get(feature::Metadata::FMD_PRICE_RATE), pricing)) - pricing = 0; - string pricingStr; - CHECK_GREATER_OR_EQUAL(pricing, 0, ("Pricing must be positive!")); - for (auto i = 0; i < pricing; i++) - pricingStr.append(kPricingSymbol); - - meta.m_hotelApproximatePricing = pricingStr; - } - - meta.m_isInitialized = true; -} - -PreRankerResult::PreRankerResult(FeatureID const & fID, PreRankingInfo const & info) - : m_id(fID), m_info(info) +// PreRankerResult --------------------------------------------------------------------------------- +PreRankerResult::PreRankerResult(FeatureID const & id, PreRankingInfo const & info) + : m_id(id), m_info(info) { ASSERT(m_id.IsValid(), ()); } @@ -130,6 +85,7 @@ bool PreRankerResult::LessDistance(PreRankerResult const & r1, PreRankerResult c return r1.m_info.m_rank > r2.m_info.m_rank; } +// RankerResult ------------------------------------------------------------------------------------ RankerResult::RankerResult(FeatureType const & f, m2::PointD const & center, m2::PointD const & pivot, string const & displayName, string const & fileName) @@ -157,10 +113,10 @@ RankerResult::RankerResult(double lat, double lon) } string RankerResult::GetRegionName(storage::CountryInfoGetter const & infoGetter, - uint32_t fType) const + uint32_t ftype) const { static SkipRegionInfo const checker; - if (checker.IsSkip(fType)) + if (checker.IsSkip(ftype)) return string(); storage::CountryInfo info; @@ -178,18 +134,6 @@ bool RankerResult::IsStreet() const return m_geomType == feature::GEOM_LINE && ftypes::IsStreetChecker::Instance()(m_types); } -string RankerResult::DebugPrint() const -{ - stringstream ss; - ss << "IntermediateResult [ " - << "Name: " << m_str - << "; Type: " << GetBestType() - << "; " << search::DebugPrint(m_info) - << "; Linear model rank: " << m_info.GetLinearModelRank() - << " ]"; - return ss.str(); -} - uint32_t RankerResult::GetBestType(set<uint32_t> const * pPrefferedTypes) const { if (pPrefferedTypes) @@ -208,6 +152,7 @@ uint32_t RankerResult::GetBestType(set<uint32_t> const * pPrefferedTypes) const return type; } +// RankerResult::RegionInfo ------------------------------------------------------------------------ void RankerResult::RegionInfo::GetRegion(storage::CountryInfoGetter const & infoGetter, storage::CountryInfo & info) const { @@ -216,4 +161,66 @@ void RankerResult::RegionInfo::GetRegion(storage::CountryInfoGetter const & info else infoGetter.GetRegionInfo(m_point, info); } + +// Functions --------------------------------------------------------------------------------------- +void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta) +{ + if (meta.m_isInitialized) + return; + + feature::Metadata const & src = ft.GetMetadata(); + + meta.m_cuisine = src.Get(feature::Metadata::FMD_CUISINE); + + string const openHours = src.Get(feature::Metadata::FMD_OPEN_HOURS); + if (!openHours.empty()) + { + osmoh::OpeningHours const oh(openHours); + // TODO: We should check closed/open time for specific feature's timezone. + time_t const now = time(nullptr); + if (oh.IsValid() && !oh.IsUnknown(now)) + meta.m_isOpenNow = oh.IsOpen(now) ? osm::Yes : osm::No; + // In else case value us osm::Unknown, it's set in preview's constructor. + } + + if (strings::to_int(src.Get(feature::Metadata::FMD_STARS), meta.m_stars)) + meta.m_stars = my::clamp(meta.m_stars, 0, 5); + else + meta.m_stars = 0; + + bool const isSponsoredHotel = ftypes::IsBookingChecker::Instance()(ft); + meta.m_isSponsoredHotel = isSponsoredHotel; + meta.m_isHotel = ftypes::IsHotelChecker::Instance()(ft); + + if (isSponsoredHotel) + { + auto const r = src.Get(feature::Metadata::FMD_RATING); + char const * const rating = r.empty() ? kEmptyRatingSymbol : r.c_str(); + meta.m_hotelRating = rating; + + int pricing; + if (!strings::to_int(src.Get(feature::Metadata::FMD_PRICE_RATE), pricing)) + pricing = 0; + string pricingStr; + CHECK_GREATER_OR_EQUAL(pricing, 0, ("Pricing must be positive!")); + for (auto i = 0; i < pricing; i++) + pricingStr.append(kPricingSymbol); + + meta.m_hotelApproximatePricing = pricingStr; + } + + meta.m_isInitialized = true; +} + +string DebugPrint(RankerResult const & r) +{ + stringstream ss; + ss << "RankerResult [" + << "Name: " << r.GetName() + << "; Type: " << r.GetBestType() + << "; " << DebugPrint(r.GetRankingInfo()) + << "; Linear model rank: " << r.GetLinearModelRank() + << "]"; + return ss.str(); +} } // namespace search diff --git a/search/intermediate_result.hpp b/search/intermediate_result.hpp index c16fa1e062..989c1e91d8 100644 --- a/search/intermediate_result.hpp +++ b/search/intermediate_result.hpp @@ -27,7 +27,7 @@ class ReverseGeocoder; class PreRankerResult { public: - PreRankerResult(FeatureID const & fID, PreRankingInfo const & info); + PreRankerResult(FeatureID const & id, PreRankingInfo const & info); static bool LessRank(PreRankerResult const & r1, PreRankerResult const & r2); static bool LessDistance(PreRankerResult const & r1, PreRankerResult const & r2); @@ -64,7 +64,9 @@ public: /// For RESULT_LATLON. RankerResult(double lat, double lon); - inline search::RankingInfo const & GetRankingInfo() const { return m_info; } + bool IsStreet() const; + + search::RankingInfo const & GetRankingInfo() const { return m_info; } template <typename TInfo> inline void SetRankingInfo(TInfo && info) @@ -72,23 +74,19 @@ public: m_info = forward<TInfo>(info); } - string DebugPrint() const; - - bool IsStreet() const; + FeatureID const & GetID() const { return m_id; } + string const & GetName() const { return m_str; } + feature::TypesHolder const & GetTypes() const { return m_types; } + Type const & GetResultType() const { return m_resultType; } + m2::PointD GetCenter() const { return m_region.m_point; } + double GetDistance() const { return m_distance; } + feature::EGeomType GetGeomType() const { return m_geomType; } + Result::Metadata GetMetadata() const { return m_metadata; } - inline FeatureID const & GetID() const { return m_id; } - inline string const & GetName() const { return m_str; } - inline feature::TypesHolder const & GetTypes() const { return m_types; } - inline Type const & GetResultType() const { return m_resultType; } - inline m2::PointD GetCenter() const { return m_region.m_point; } - inline double GetDistance() const { return m_distance; } - inline feature::EGeomType GetGeomType() const { return m_geomType; } - inline Result::Metadata GetMetadata() const { return m_metadata; } + double GetDistanceToPivot() const { return m_info.m_distanceToPivot; } + double GetLinearModelRank() const { return m_info.GetLinearModelRank(); } - inline double GetDistanceToPivot() const { return m_info.m_distanceToPivot; } - inline double GetLinearModelRank() const { return m_info.GetLinearModelRank(); } - - string GetRegionName(storage::CountryInfoGetter const & infoGetter, uint32_t fType) const; + string GetRegionName(storage::CountryInfoGetter const & infoGetter, uint32_t ftype) const; bool IsEqualCommon(RankerResult const & r) const; @@ -123,7 +121,7 @@ private: Result::Metadata m_metadata; }; -inline string DebugPrint(RankerResult const & t) { return t.DebugPrint(); } - void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta); + +string DebugPrint(RankerResult const & r); } // namespace search diff --git a/search/pre_ranker.cpp b/search/pre_ranker.cpp index 4b470c4cca..2052e8aac2 100644 --- a/search/pre_ranker.cpp +++ b/search/pre_ranker.cpp @@ -13,7 +13,10 @@ #include "base/random.hpp" #include "base/stl_helpers.hpp" -#include "std/iterator.hpp" +#include <iterator> +#include <set> + +using namespace std; namespace search { @@ -175,8 +178,7 @@ void PreRanker::Filter(bool viewportSearch) } } - using TSet = set<PreRankerResult, LessFeatureID>; - TSet filtered; + set<PreRankerResult, LessFeatureID> filtered; filtered.insert(m_results.begin(), m_results.begin() + min(m_results.size(), BatchSize())); diff --git a/search/processor.hpp b/search/processor.hpp index c09df8b3f8..61f0952196 100644 --- a/search/processor.hpp +++ b/search/processor.hpp @@ -135,11 +135,8 @@ protected: void SetViewportByIndex(m2::RectD const & viewport, size_t idx, bool forceUpdate); void ClearCache(size_t ind); - /// @name Get ranking params. - //@{ - /// @return Rect for viewport-distance calculation. + // Returns a Rect for viewport-distance calculations. m2::RectD const & GetViewport(ViewportID vID = DEFAULT_V) const; - //@} void SetLanguage(int id, int8_t lang); int8_t GetLanguage(int id) const; diff --git a/search/ranker.cpp b/search/ranker.cpp index ae341a4b5c..3408fc4ec6 100644 --- a/search/ranker.cpp +++ b/search/ranker.cpp @@ -16,6 +16,8 @@ #include "std/iterator.hpp" #include "std/unique_ptr.hpp" +#include <boost/optional.hpp> + namespace search { namespace @@ -83,7 +85,7 @@ void RemoveDuplicatingLinear(vector<RankerResult> & results) if (t1 != t2) return t1 < t2; - // Should stay the best feature, after unique, so add this criteria: + // After unique, the better feature should be kept. return r1.GetDistance() < r2.GetDistance(); }; @@ -119,7 +121,6 @@ ftypes::Type GetLocalityIndex(feature::TypesHolder const & types) // TODO: Format street and house number according to local country's rules. string FormatStreetAndHouse(ReverseGeocoder::Address const & addr) { - ASSERT_GREATER_OR_EQUAL(addr.GetDistance(), 0, ()); return addr.GetStreetName() + ", " + addr.GetHouseNumber(); } @@ -276,14 +277,13 @@ class RankerResultMaker } public: - explicit RankerResultMaker(Ranker & ranker, Index const & index, - storage::CountryInfoGetter const & infoGetter, - Geocoder::Params const & params) + RankerResultMaker(Ranker & ranker, Index const & index, + storage::CountryInfoGetter const & infoGetter, Geocoder::Params const & params) : m_ranker(ranker), m_index(index), m_params(params), m_infoGetter(infoGetter) { } - unique_ptr<RankerResult> operator()(PreRankerResult const & preRankerResult) + boost::optional<RankerResult> operator()(PreRankerResult const & preRankerResult) { FeatureType ft; m2::PointD center; @@ -293,15 +293,14 @@ public: if (!LoadFeature(preRankerResult.GetId(), ft, center, name, country)) return {}; - auto p = make_unique<RankerResult>(ft, center, m_ranker.m_params.m_position /* pivot */, name, - country); + RankerResult r(ft, center, m_ranker.m_params.m_position /* pivot */, name, country); search::RankingInfo info; InitRankingInfo(ft, center, preRankerResult, info); info.m_rank = NormalizeRank(info.m_rank, info.m_type, center, country); - p->SetRankingInfo(move(info)); + r.SetRankingInfo(move(info)); - return p; + return r; } }; @@ -352,8 +351,7 @@ void Ranker::SuggestStrings() if (m_params.m_prefix.empty() || !m_params.m_suggestsEnabled) return; - string prologue; - GetStringPrefix(m_params.m_query, prologue); + string prologue = DropLastToken(m_params.m_query); for (auto const & locale : m_params.m_categoryLocales) MatchForSuggestions(m_params.m_prefix, locale, prologue); @@ -438,7 +436,7 @@ void Ranker::MakeRankerResults(Geocoder::Params const & geocoderParams, } if (!ResultExists(*p, results, m_params.m_minDistanceOnMapBetweenResults)) - results.push_back(move(*p.release())); + results.push_back(move(*p)); }; } @@ -516,7 +514,7 @@ void Ranker::MatchForSuggestions(strings::UniString const & token, int8_t locale if (suggest.m_prefixLength <= token.size() && token != s // do not push suggestion if it already equals to token && suggest.m_locale == locale // push suggestions only for needed language - && strings::StartsWith(s.begin(), s.end(), token.begin(), token.end())) + && strings::StartsWith(s, token)) { string const utf8Str = strings::ToUtf8(s); Result r(utf8Str, prologue + utf8Str + " "); diff --git a/search/search_quality/search_quality_tool/search_quality_tool.cpp b/search/search_quality/search_quality_tool/search_quality_tool.cpp index 66ab53db41..ba7f16d22f 100644 --- a/search/search_quality/search_quality_tool/search_quality_tool.cpp +++ b/search/search_quality/search_quality_tool/search_quality_tool.cpp @@ -378,8 +378,7 @@ int main(int argc, char * argv[]) Engine::Params params; params.m_locale = FLAGS_locale; params.m_numThreads = FLAGS_num_threads; - TestSearchEngine engine(move(infoGetter), make_unique<ProcessorFactory>(), Engine::Params{}); - engine.SetLocale(FLAGS_locale); + TestSearchEngine engine(move(infoGetter), make_unique<ProcessorFactory>(), params); vector<platform::LocalCountryFile> mwms; if (!FLAGS_mwm_list_path.empty()) diff --git a/search/search_tests/highlighting_tests.cpp b/search/search_tests/highlighting_tests.cpp index 24090f11f7..75f1ec4af1 100644 --- a/search/search_tests/highlighting_tests.cpp +++ b/search/search_tests/highlighting_tests.cpp @@ -5,14 +5,17 @@ #include "indexer/feature_covering.hpp" #include "base/logging.hpp" +#include "base/string_utils.hpp" #include "std/cstdarg.hpp" +#include "std/string.hpp" +#include "std/vector.hpp" namespace { -typedef pair<uint16_t, uint16_t> TestResult; -typedef vector<strings::UniString> TokensVector; -typedef vector<TestResult> TestResultVector; +using TestResult = pair<uint16_t, uint16_t>; +using TokensVector = vector<strings::UniString>; +using TestResultVector = vector<TestResult>; struct TestData { @@ -37,9 +40,8 @@ struct TestData va_end(ap); } - void AddResult(uint16_t pos, uint16_t len) { m_results.push_back(TestResult(pos, len)); } + void AddResult(uint16_t pos, uint16_t len) { m_results.emplace_back(pos, len); } }; -typedef vector<TestData> TestVector; class CheckRange { @@ -86,7 +88,7 @@ UNIT_TEST(SearchStringTokensIntersectionRange) char const * lowTokens8[] = {"ул", "кар"}; char const * lowTokens9[] = {"ул", "бог"}; - TestVector tests; + vector<TestData> tests; // fill test data tests.push_back(TestData(str0, lowTokens0, 2, 2, 6, 5, 12, 6)); tests.push_back(TestData(str1, lowTokens0, 2, 2, 4, 5, 10, 6)); @@ -112,12 +114,8 @@ UNIT_TEST(SearchStringTokensIntersectionRange) tests.push_back(TestData(str9, lowTokens8, 2, 2, 0, 2, 6, 3)); tests.push_back(TestData(str11, lowTokens9, 2, 2, 0, 2, 14, 3)); - // run tests - size_t count = 0; - for (TestVector::iterator it = tests.begin(); it != tests.end(); ++it, ++count) + for (TestData const & data : tests) { - TestData const & data = *it; - search::SearchStringTokensIntersectionRanges( data.m_input, data.m_lowTokens.begin(), data.m_lowTokens.end(), CheckRange(data.m_results)); } diff --git a/search/suggest.cpp b/search/suggest.cpp index a26ca57342..5272337d66 100644 --- a/search/suggest.cpp +++ b/search/suggest.cpp @@ -47,7 +47,7 @@ void GetSuggestion(RankerResult const & res, string const & query, QueryTokens c if (!prefixMatched || fullPrefixMatched) return; - GetStringPrefix(query, suggest); + suggest = DropLastToken(query); // Appends unmatched result's tokens to the suggestion. for (size_t i = 0; i < tokens.size(); ++i) |