@@ -1110,7 +1110,7 @@ bool UserHistoryPredictor::LookupEntry(
11101110 const ConversionRequest& request, absl::string_view request_key,
11111111 absl::string_view key_base,
11121112 const Trie<std::string>* absl_nullable key_expanded, const Entry& entry,
1113- const Entry* absl_nullable prev_entry,
1113+ const Entry* absl_nullable prev_entry, bool exact_match_only,
11141114 EntryPriorityQueue& entry_queue) const {
11151115 Entry* result = nullptr ;
11161116
@@ -1139,15 +1139,25 @@ bool UserHistoryPredictor::LookupEntry(
11391139 return false ;
11401140 }
11411141
1142+ // `exact_match_only` is called on the conversion that only requires exact
1143+ // match result.
1144+ // RIGHT_PREFIX_MATCH automatically fills the remaining suffix with
1145+ // the logic of partial match.
1146+ if (exact_match_only && mtype != MatchType::RIGHT_PREFIX_MATCH &&
1147+ mtype != MatchType::EXACT_MATCH) {
1148+ return false ;
1149+ }
1150+
11421151 // Full sentence with low frequency is suppressed unless
11431152 // AllowLowFreqFullSentenceEntryMatch() returns true.
1144- if (IsLowFreqFullSentenceEntry (request, entry) &&
1153+ if (!exact_match_only && IsLowFreqFullSentenceEntry (request, entry) &&
11451154 !AllowLowFreqFullSentenceEntryMatch (request, request_key, mtype, entry)) {
11461155 return false ;
11471156 }
11481157
11491158 // For mobile, prefer exact match.
1150- const bool prefer_exact_match = IsMixedConversionEnabled (request);
1159+ const bool prefer_exact_match =
1160+ IsMixedConversionEnabled (request) || exact_match_only;
11511161
11521162 left_last_access_time = entry.last_access_time ();
11531163 left_most_last_access_time =
@@ -1182,13 +1192,18 @@ bool UserHistoryPredictor::LookupEntry(
11821192 uint32_t result_attribute = 0 ;
11831193 converter::InnerSegmentBoundary inner_segment_boundary;
11841194
1185- if (GetKeyValueForExactAndRightPrefixMatch (
1195+ if (!exact_match_only &&
1196+ GetKeyValueForExactAndRightPrefixMatch (
11861197 request, request_key, prefer_exact_match, entry, last_entry,
11871198 left_last_access_time, left_most_last_access_time, key, value,
1188- inner_segment_boundary) ||
1189- GetKeyValueForPartialMatch (request, request_key, entry, key, value,
1190- result_attribute, inner_segment_boundary,
1191- entry_queue)) {
1199+ inner_segment_boundary)) {
1200+ result =
1201+ AddEntryWithNewKeyValue (request, std::move (key), std::move (value),
1202+ inner_segment_boundary, entry, entry_queue);
1203+ SetAttribute (*result, result_attribute);
1204+ } else if (GetKeyValueForPartialMatch (
1205+ request, request_key, entry, key, value, result_attribute,
1206+ inner_segment_boundary, entry_queue)) {
11921207 result =
11931208 AddEntryWithNewKeyValue (request, std::move (key), std::move (value),
11941209 inner_segment_boundary, entry, entry_queue);
@@ -1268,8 +1283,8 @@ bool UserHistoryPredictor::LookupEntry(
12681283 entry_queue.Push (result);
12691284 }
12701285
1271- if ( IsMixedConversionEnabled (request)) {
1272- // For mobile, we don't generate joined result.
1286+ // For mobile or conversion mode, we don't generate joined result.
1287+ if ( IsMixedConversionEnabled (request) || exact_match_only) {
12731288 return true ;
12741289 }
12751290
@@ -1369,7 +1384,7 @@ std::vector<Result> UserHistoryPredictor::Predict(
13691384 max_prediction_size = 3 ;
13701385 }
13711386
1372- EntryPriorityQueue entry_queue = CreateEntryQueueFromHistory (
1387+ EntryPriorityQueue entry_queue = CreateEntryQueueFromHistoryForPrediction (
13731388 request, prev_entry.get (), max_prediction_size * 5 );
13741389
13751390 if (entry_queue.size () == 0 ) {
@@ -1381,48 +1396,75 @@ std::vector<Result> UserHistoryPredictor::Predict(
13811396 entry_queue);
13821397}
13831398
1384- bool UserHistoryPredictor::ShouldPredict (
1399+ std::vector<Result> UserHistoryPredictor::Convert (
13851400 const ConversionRequest& request) const {
1386- if (storage_.IsSyncerInCriticalSection ()) {
1387- MOZC_VLOG (2 ) << " Syncer is running" ;
1388- return false ;
1401+ if (!ShouldPredict (request)) {
1402+ return {};
13891403 }
13901404
1391- if (request.incognito_mode ()) {
1392- MOZC_VLOG (2 ) << " incognito mode" ;
1393- return false ;
1405+ ConstEntrySnapshot prev_entry = LookupPrevEntry (request);
1406+
1407+ constexpr int kMaxCandidatesSize = 5 ;
1408+
1409+ EntryPriorityQueue entry_queue = CreateEntryQueueFromHistoryForConversion (
1410+ request, prev_entry.get (), kMaxCandidatesSize );
1411+
1412+ if (entry_queue.size () == 0 ) {
1413+ MOZC_VLOG (2 ) << " no prefix match candidate is found." ;
1414+ return {};
13941415 }
13951416
1396- if (request.config ().history_learning_level () == config::Config::NO_HISTORY) {
1397- MOZC_VLOG (2 ) << " history learning level is NO_HISTORY" ;
1417+ return MakeResults (request, kMaxCandidatesSize ,
1418+ 0 /* max_prediction_char_coverage */ , entry_queue);
1419+ }
1420+
1421+ bool UserHistoryPredictor::ShouldPredict (
1422+ const ConversionRequest& request) const {
1423+ if (storage_.IsSyncerInCriticalSection ()) {
1424+ MOZC_VLOG (2 ) << " Syncer is running" ;
13981425 return false ;
13991426 }
14001427
1401- if (request. request_type () == ConversionRequest::CONVERSION ) {
1402- MOZC_VLOG (2 ) << " request type is CONVERSION " ;
1428+ if (storage_. IsEmpty () ) {
1429+ MOZC_VLOG (2 ) << " dic is empty " ;
14031430 return false ;
14041431 }
14051432
1406- if (! request.config (). use_history_suggest ()) {
1407- MOZC_VLOG (2 ) << " no history suggest " ;
1433+ if (request.incognito_mode ()) {
1434+ MOZC_VLOG (2 ) << " incognito mode " ;
14081435 return false ;
14091436 }
14101437
1411- if (storage_. IsEmpty () ) {
1412- MOZC_VLOG (2 ) << " dic is empty " ;
1438+ if (request. config (). history_learning_level () == config::Config::NO_HISTORY ) {
1439+ MOZC_VLOG (2 ) << " history learning level is NO_HISTORY " ;
14131440 return false ;
14141441 }
14151442
14161443 absl::string_view request_key = request.key ();
14171444
1418- if (request_key.empty () && !IsZeroQuerySuggestionEnabled (request)) {
1419- MOZC_VLOG (2 ) << " key length is 0" ;
1420- return false ;
1421- }
1445+ // Prediction/suggestion specific rules.
1446+ if (request.request_type () == ConversionRequest::SUGGESTION ||
1447+ request.request_type () == ConversionRequest::PREDICTION ||
1448+ request.request_type () == ConversionRequest::PARTIAL_SUGGESTION ||
1449+ request.request_type () == ConversionRequest::PARTIAL_PREDICTION) {
1450+ if (request_key.empty () && !IsZeroQuerySuggestionEnabled (request)) {
1451+ MOZC_VLOG (2 ) << " key length is 0" ;
1452+ return false ;
1453+ }
14221454
1423- if (StartsWithPunctuation (request_key)) {
1424- MOZC_VLOG (2 ) << " request_key starts with punctuations" ;
1425- return false ;
1455+ if (!request.config ().use_history_suggest ()) {
1456+ MOZC_VLOG (2 ) << " no history suggest" ;
1457+ return false ;
1458+ }
1459+
1460+ if (StartsWithPunctuation (request_key)) {
1461+ MOZC_VLOG (2 ) << " request_key starts with punctuations" ;
1462+ return false ;
1463+ }
1464+ } else if (request.request_type () == ConversionRequest::CONVERSION) {
1465+ if (request_key.empty ()) {
1466+ return false ;
1467+ }
14261468 }
14271469
14281470 return true ;
@@ -1481,8 +1523,8 @@ UserHistoryPredictor::ConstEntrySnapshot UserHistoryPredictor::LookupPrevEntry(
14811523}
14821524
14831525UserHistoryPredictor::EntryPriorityQueue
1484- UserHistoryPredictor::CreateEntryQueueFromHistory (
1485- const ConversionRequest& request, const Entry* prev_entry,
1526+ UserHistoryPredictor::CreateEntryQueueFromHistoryForPrediction (
1527+ const ConversionRequest& request, const Entry* absl_nullable prev_entry,
14861528 size_t max_entry_queue_size) const {
14871529 // Gets romanized input key if the given preedit looks misspelled.
14881530 const std::string roman_request_key = GetRomanMisspelledKey (request);
@@ -1521,10 +1563,12 @@ UserHistoryPredictor::CreateEntryQueueFromHistory(
15211563 return true ;
15221564 }
15231565
1566+ static constexpr bool kDisableExactMatchOnly = false ;
1567+
15241568 // Lookup key from elm_value and prev_entry.
15251569 // If a new entry is found, the entry is pushed to the entry_queue.
15261570 if (LookupEntry (request, request_key, base_key, expanded.get (), entry,
1527- prev_entry, entry_queue)) {
1571+ prev_entry, kDisableExactMatchOnly , entry_queue)) {
15281572 return true ;
15291573 }
15301574
@@ -1549,7 +1593,7 @@ UserHistoryPredictor::CreateEntryQueueFromHistory(
15491593 // in dictionary predictor.
15501594 if (c.score > 0.0 &&
15511595 LookupEntry (request, c.correction , c.correction , nullptr , entry,
1552- prev_entry, entry_queue)) {
1596+ prev_entry, kDisableExactMatchOnly , entry_queue)) {
15531597 break ;
15541598 }
15551599 }
@@ -1560,6 +1604,36 @@ UserHistoryPredictor::CreateEntryQueueFromHistory(
15601604 return entry_queue;
15611605}
15621606
1607+ UserHistoryPredictor::EntryPriorityQueue
1608+ UserHistoryPredictor::CreateEntryQueueFromHistoryForConversion (
1609+ const ConversionRequest& request, const Entry* absl_nullable prev_entry,
1610+ size_t max_entry_queue_size) const {
1611+ const std::string request_key = request.composer ().GetQueryForConversion ();
1612+ EntryPriorityQueue entry_queue;
1613+
1614+ storage_.ForEach ([&](uint64_t fp, const Entry& entry) {
1615+ // already found enough entry_queue.
1616+ if (entry_queue.size () >= max_entry_queue_size) {
1617+ return false ;
1618+ }
1619+
1620+ if (!IsValidEntryIgnoringRemovedField (entry)) {
1621+ return true ;
1622+ }
1623+
1624+ static constexpr bool kEnableExactMatchOnly = true ;
1625+
1626+ if (LookupEntry (request, request_key, request_key, nullptr , entry,
1627+ prev_entry, kEnableExactMatchOnly , entry_queue)) {
1628+ return true ;
1629+ }
1630+
1631+ return true ;
1632+ });
1633+
1634+ return entry_queue;
1635+ }
1636+
15631637// static
15641638std::tuple<std::string, std::string, std::unique_ptr<Trie<std::string>>>
15651639UserHistoryPredictor::GetInputKeyFromRequest (const ConversionRequest& request) {
0 commit comments