OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/omnibox/search_provider.h" | 5 #include "components/omnibox/search_provider.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 match.allowed_to_be_default_match = true; | 258 match.allowed_to_be_default_match = true; |
259 matches_.push_back(match); | 259 matches_.push_back(match); |
260 } | 260 } |
261 Stop(true); | 261 Stop(true); |
262 return; | 262 return; |
263 } | 263 } |
264 | 264 |
265 input_ = input; | 265 input_ = input; |
266 | 266 |
267 DoHistoryQuery(minimal_changes); | 267 DoHistoryQuery(minimal_changes); |
268 DoAnswersQuery(input); | 268 // Answers needs scored history results before any suggest query has been |
| 269 // started, since the query for answer-bearing results needs additional |
| 270 // prefetch information based on the highest-scored local history result. |
| 271 if (OmniboxFieldTrial::EnableAnswersInSuggest()) { |
| 272 ScoreHistoryResults(raw_default_history_results_, |
| 273 false, |
| 274 &transformed_default_history_results_); |
| 275 ScoreHistoryResults(raw_keyword_history_results_, |
| 276 true, |
| 277 &transformed_keyword_history_results_); |
| 278 prefetch_data_ = FindAnswersPrefetchData(); |
| 279 |
| 280 // Raw results are not needed any more. |
| 281 raw_default_history_results_.clear(); |
| 282 raw_keyword_history_results_.clear(); |
| 283 } else { |
| 284 transformed_default_history_results_.clear(); |
| 285 transformed_keyword_history_results_.clear(); |
| 286 } |
| 287 |
269 StartOrStopSuggestQuery(minimal_changes); | 288 StartOrStopSuggestQuery(minimal_changes); |
270 UpdateMatches(); | 289 UpdateMatches(); |
271 } | 290 } |
272 | 291 |
273 void SearchProvider::Stop(bool clear_cached_results) { | 292 void SearchProvider::Stop(bool clear_cached_results) { |
274 StopSuggest(); | 293 StopSuggest(); |
275 done_ = true; | 294 done_ = true; |
276 | 295 |
277 if (clear_cached_results) | 296 if (clear_cached_results) |
278 ClearAllResults(); | 297 ClearAllResults(); |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 listener_->OnProviderUpdate(false); | 531 listener_->OnProviderUpdate(false); |
513 } | 532 } |
514 } | 533 } |
515 | 534 |
516 void SearchProvider::DoHistoryQuery(bool minimal_changes) { | 535 void SearchProvider::DoHistoryQuery(bool minimal_changes) { |
517 // The history query results are synchronous, so if minimal_changes is true, | 536 // The history query results are synchronous, so if minimal_changes is true, |
518 // we still have the last results and don't need to do anything. | 537 // we still have the last results and don't need to do anything. |
519 if (minimal_changes) | 538 if (minimal_changes) |
520 return; | 539 return; |
521 | 540 |
522 keyword_history_results_.clear(); | 541 raw_keyword_history_results_.clear(); |
523 default_history_results_.clear(); | 542 raw_default_history_results_.clear(); |
524 | 543 |
525 if (OmniboxFieldTrial::SearchHistoryDisable( | 544 if (OmniboxFieldTrial::SearchHistoryDisable( |
526 input_.current_page_classification())) | 545 input_.current_page_classification())) |
527 return; | 546 return; |
528 | 547 |
529 history::URLDatabase* url_db = client_->InMemoryDatabase(); | 548 history::URLDatabase* url_db = client_->InMemoryDatabase(); |
530 if (!url_db) | 549 if (!url_db) |
531 return; | 550 return; |
532 | 551 |
533 // Request history for both the keyword and default provider. We grab many | 552 // Request history for both the keyword and default provider. We grab many |
534 // more matches than we'll ultimately clamp to so that if there are several | 553 // more matches than we'll ultimately clamp to so that if there are several |
535 // recent multi-word matches who scores are lowered (see | 554 // recent multi-word matches who scores are lowered (see |
536 // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring | 555 // ScoreHistoryResults()), they won't crowd out older, higher-scoring |
537 // matches. Note that this doesn't fix the problem entirely, but merely | 556 // matches. Note that this doesn't fix the problem entirely, but merely |
538 // limits it to cases with a very large number of such multi-word matches; for | 557 // limits it to cases with a very large number of such multi-word matches; for |
539 // now, this seems OK compared with the complexity of a real fix, which would | 558 // now, this seems OK compared with the complexity of a real fix, which would |
540 // require multiple searches and tracking of "single- vs. multi-word" in the | 559 // require multiple searches and tracking of "single- vs. multi-word" in the |
541 // database. | 560 // database. |
542 int num_matches = kMaxMatches * 5; | 561 int num_matches = kMaxMatches * 5; |
543 const TemplateURL* default_url = providers_.GetDefaultProviderURL(); | 562 const TemplateURL* default_url = providers_.GetDefaultProviderURL(); |
544 if (default_url) { | 563 if (default_url) { |
545 const base::TimeTicks start_time = base::TimeTicks::Now(); | 564 const base::TimeTicks start_time = base::TimeTicks::Now(); |
546 url_db->GetMostRecentKeywordSearchTerms(default_url->id(), input_.text(), | 565 url_db->GetMostRecentKeywordSearchTerms(default_url->id(), |
547 num_matches, &default_history_results_); | 566 input_.text(), |
| 567 num_matches, |
| 568 &raw_default_history_results_); |
548 UMA_HISTOGRAM_TIMES( | 569 UMA_HISTOGRAM_TIMES( |
549 "Omnibox.SearchProvider.GetMostRecentKeywordTermsDefaultProviderTime", | 570 "Omnibox.SearchProvider.GetMostRecentKeywordTermsDefaultProviderTime", |
550 base::TimeTicks::Now() - start_time); | 571 base::TimeTicks::Now() - start_time); |
551 } | 572 } |
552 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); | 573 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
553 if (keyword_url) { | 574 if (keyword_url) { |
554 url_db->GetMostRecentKeywordSearchTerms(keyword_url->id(), | 575 url_db->GetMostRecentKeywordSearchTerms(keyword_url->id(), |
555 keyword_input_.text(), num_matches, &keyword_history_results_); | 576 keyword_input_.text(), |
| 577 num_matches, |
| 578 &raw_keyword_history_results_); |
556 } | 579 } |
557 } | 580 } |
558 | 581 |
559 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { | 582 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
560 if (!IsQuerySuitableForSuggest()) { | 583 if (!IsQuerySuitableForSuggest()) { |
561 StopSuggest(); | 584 StopSuggest(); |
562 ClearAllResults(); | 585 ClearAllResults(); |
563 return; | 586 return; |
564 } | 587 } |
565 | 588 |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
837 trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE, | 860 trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE, |
838 trimmed_verbatim, base::string16(), base::string16(), | 861 trimmed_verbatim, base::string16(), base::string16(), |
839 base::string16(), base::string16(), std::string(), std::string(), | 862 base::string16(), base::string16(), std::string(), std::string(), |
840 true, keyword_verbatim_relevance, keyword_relevance_from_server, | 863 true, keyword_verbatim_relevance, keyword_relevance_from_server, |
841 false, trimmed_verbatim); | 864 false, trimmed_verbatim); |
842 AddMatchToMap(verbatim, std::string(), | 865 AddMatchToMap(verbatim, std::string(), |
843 did_not_accept_keyword_suggestion, false, true, &map); | 866 did_not_accept_keyword_suggestion, false, true, &map); |
844 } | 867 } |
845 } | 868 } |
846 } | 869 } |
847 AddHistoryResultsToMap(keyword_history_results_, true, | 870 AddRawHistoryResultsToMap(true, did_not_accept_keyword_suggestion, &map); |
848 did_not_accept_keyword_suggestion, &map); | 871 AddRawHistoryResultsToMap(false, did_not_accept_default_suggestion, &map); |
849 AddHistoryResultsToMap(default_history_results_, false, | |
850 did_not_accept_default_suggestion, &map); | |
851 | 872 |
852 AddSuggestResultsToMap(keyword_results_.suggest_results, | 873 AddSuggestResultsToMap(keyword_results_.suggest_results, |
853 keyword_results_.metadata, &map); | 874 keyword_results_.metadata, &map); |
854 AddSuggestResultsToMap(default_results_.suggest_results, | 875 AddSuggestResultsToMap(default_results_.suggest_results, |
855 default_results_.metadata, &map); | 876 default_results_.metadata, &map); |
856 | 877 |
857 ACMatches matches; | 878 ACMatches matches; |
858 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) | 879 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) |
859 matches.push_back(i->second); | 880 matches.push_back(i->second); |
860 | 881 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
930 for (SearchSuggestionParser::NavigationResults::const_iterator it = | 951 for (SearchSuggestionParser::NavigationResults::const_iterator it = |
931 navigation_results.begin(); it != navigation_results.end(); ++it) { | 952 navigation_results.begin(); it != navigation_results.end(); ++it) { |
932 matches->push_back(NavigationToMatch(*it)); | 953 matches->push_back(NavigationToMatch(*it)); |
933 // In the absence of suggested relevance scores, use only the single | 954 // In the absence of suggested relevance scores, use only the single |
934 // highest-scoring result. (The results are already sorted by relevance.) | 955 // highest-scoring result. (The results are already sorted by relevance.) |
935 if (!it->relevance_from_server()) | 956 if (!it->relevance_from_server()) |
936 return; | 957 return; |
937 } | 958 } |
938 } | 959 } |
939 | 960 |
940 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, | 961 void SearchProvider::AddRawHistoryResultsToMap(bool is_keyword, |
941 bool is_keyword, | 962 int did_not_accept_suggestion, |
942 int did_not_accept_suggestion, | 963 MatchMap* map) { |
943 MatchMap* map) { | 964 const HistoryResults& raw_results = |
944 if (results.empty()) | 965 is_keyword ? raw_keyword_history_results_ : raw_default_history_results_; |
| 966 if (!OmniboxFieldTrial::EnableAnswersInSuggest() && raw_results.empty()) |
945 return; | 967 return; |
946 | 968 |
947 base::TimeTicks start_time(base::TimeTicks::Now()); | 969 base::TimeTicks start_time(base::TimeTicks::Now()); |
948 bool prevent_inline_autocomplete = input_.prevent_inline_autocomplete() || | |
949 (input_.type() == metrics::OmniboxInputType::URL); | |
950 const base::string16& input_text = | |
951 is_keyword ? keyword_input_.text() : input_.text(); | |
952 bool input_multiple_words = HasMultipleWords(input_text); | |
953 | 970 |
954 SearchSuggestionParser::SuggestResults scored_results; | 971 // Until Answers becomes default, scoring of history results will still happen |
955 if (!prevent_inline_autocomplete && input_multiple_words) { | 972 // here for non-Answers Chrome, to prevent scoring performance regressions |
956 // ScoreHistoryResults() allows autocompletion of multi-word, 1-visit | 973 // resulting from moving the scoring code before the suggest request is sent. |
957 // queries if the input also has multiple words. But if we were already | 974 // For users with Answers enabled, the history results have already been |
958 // scoring a multi-word, multi-visit query aggressively, and the current | 975 // scored earlier, right after calling DoHistoryQuery(). |
959 // input is still a prefix of it, then changing the suggestion suddenly | 976 SearchSuggestionParser::SuggestResults local_transformed_results; |
960 // feels wrong. To detect this case, first score as if only one word has | 977 const SearchSuggestionParser::SuggestResults* transformed_results = NULL; |
961 // been typed, then check if the best result came from aggressive search | 978 if (!OmniboxFieldTrial::EnableAnswersInSuggest()) { |
962 // history scoring. If it did, then just keep that score set. This | 979 ScoreHistoryResults(raw_results, is_keyword, &local_transformed_results); |
963 // 1200 the lowest possible score in CalculateRelevanceForHistory()'s | 980 transformed_results = &local_transformed_results; |
964 // aggressive-scoring curve. | 981 } else { |
965 scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete, | 982 transformed_results = is_keyword ? &transformed_keyword_history_results_ |
966 false, input_text, is_keyword); | 983 : &transformed_default_history_results_; |
967 if ((scored_results.front().relevance() < 1200) || | |
968 !HasMultipleWords(scored_results.front().suggestion())) | |
969 scored_results.clear(); // Didn't detect the case above, score normally. | |
970 } | 984 } |
971 if (scored_results.empty()) | 985 DCHECK(transformed_results); |
972 scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete, | 986 AddTransformedHistoryResultsToMap( |
973 input_multiple_words, input_text, | 987 *transformed_results, did_not_accept_suggestion, map); |
974 is_keyword); | |
975 for (SearchSuggestionParser::SuggestResults::const_iterator i( | |
976 scored_results.begin()); i != scored_results.end(); ++i) { | |
977 AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, | |
978 providers_.GetKeywordProviderURL() != NULL, map); | |
979 } | |
980 UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", | 988 UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", |
981 base::TimeTicks::Now() - start_time); | 989 base::TimeTicks::Now() - start_time); |
982 } | 990 } |
983 | 991 |
984 SearchSuggestionParser::SuggestResults SearchProvider::ScoreHistoryResults( | 992 void SearchProvider::AddTransformedHistoryResultsToMap( |
985 const HistoryResults& results, | 993 const SearchSuggestionParser::SuggestResults& transformed_results, |
986 bool base_prevent_inline_autocomplete, | 994 int did_not_accept_suggestion, |
987 bool input_multiple_words, | 995 MatchMap* map) { |
988 const base::string16& input_text, | 996 for (SearchSuggestionParser::SuggestResults::const_iterator i( |
989 bool is_keyword) { | 997 transformed_results.begin()); |
| 998 i != transformed_results.end(); |
| 999 ++i) { |
| 1000 AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, |
| 1001 providers_.GetKeywordProviderURL() != NULL, map); |
| 1002 } |
| 1003 } |
| 1004 |
| 1005 SearchSuggestionParser::SuggestResults |
| 1006 SearchProvider::ScoreHistoryResultsHelper(const HistoryResults& results, |
| 1007 bool base_prevent_inline_autocomplete, |
| 1008 bool input_multiple_words, |
| 1009 const base::string16& input_text, |
| 1010 bool is_keyword) { |
990 SearchSuggestionParser::SuggestResults scored_results; | 1011 SearchSuggestionParser::SuggestResults scored_results; |
991 // True if the user has asked this exact query previously. | 1012 // True if the user has asked this exact query previously. |
992 bool found_what_you_typed_match = false; | 1013 bool found_what_you_typed_match = false; |
993 const bool prevent_search_history_inlining = | 1014 const bool prevent_search_history_inlining = |
994 OmniboxFieldTrial::SearchHistoryPreventInlining( | 1015 OmniboxFieldTrial::SearchHistoryPreventInlining( |
995 input_.current_page_classification()); | 1016 input_.current_page_classification()); |
996 const base::string16& trimmed_input = | 1017 const base::string16& trimmed_input = |
997 base::CollapseWhitespace(input_text, false); | 1018 base::CollapseWhitespace(input_text, false); |
998 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); | 1019 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); |
999 ++i) { | 1020 ++i) { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1085 for (SearchSuggestionParser::SuggestResults::iterator i( | 1106 for (SearchSuggestionParser::SuggestResults::iterator i( |
1086 scored_results.begin()); i != scored_results.end(); ++i) { | 1107 scored_results.begin()); i != scored_results.end(); ++i) { |
1087 if ((last_relevance != 0) && (i->relevance() >= last_relevance)) | 1108 if ((last_relevance != 0) && (i->relevance() >= last_relevance)) |
1088 i->set_relevance(last_relevance - 1); | 1109 i->set_relevance(last_relevance - 1); |
1089 last_relevance = i->relevance(); | 1110 last_relevance = i->relevance(); |
1090 } | 1111 } |
1091 | 1112 |
1092 return scored_results; | 1113 return scored_results; |
1093 } | 1114 } |
1094 | 1115 |
| 1116 void SearchProvider::ScoreHistoryResults( |
| 1117 const HistoryResults& results, |
| 1118 bool is_keyword, |
| 1119 SearchSuggestionParser::SuggestResults* scored_results) { |
| 1120 DCHECK(scored_results); |
| 1121 if (results.empty()) { |
| 1122 scored_results->clear(); |
| 1123 return; |
| 1124 } |
| 1125 |
| 1126 bool prevent_inline_autocomplete = input_.prevent_inline_autocomplete() || |
| 1127 (input_.type() == metrics::OmniboxInputType::URL); |
| 1128 const base::string16 input_text = GetInput(is_keyword).text(); |
| 1129 bool input_multiple_words = HasMultipleWords(input_text); |
| 1130 |
| 1131 if (!prevent_inline_autocomplete && input_multiple_words) { |
| 1132 // ScoreHistoryResultsHelper() allows autocompletion of multi-word, 1-visit |
| 1133 // queries if the input also has multiple words. But if we were already |
| 1134 // scoring a multi-word, multi-visit query aggressively, and the current |
| 1135 // input is still a prefix of it, then changing the suggestion suddenly |
| 1136 // feels wrong. To detect this case, first score as if only one word has |
| 1137 // been typed, then check if the best result came from aggressive search |
| 1138 // history scoring. If it did, then just keep that score set. This |
| 1139 // 1200 the lowest possible score in CalculateRelevanceForHistory()'s |
| 1140 // aggressive-scoring curve. |
| 1141 *scored_results = ScoreHistoryResultsHelper( |
| 1142 results, prevent_inline_autocomplete, false, input_text, is_keyword); |
| 1143 if ((scored_results->front().relevance() < 1200) || |
| 1144 !HasMultipleWords(scored_results->front().suggestion())) |
| 1145 scored_results->clear(); // Didn't detect the case above, score normally. |
| 1146 } |
| 1147 if (scored_results->empty()) { |
| 1148 *scored_results = ScoreHistoryResultsHelper(results, |
| 1149 prevent_inline_autocomplete, |
| 1150 input_multiple_words, |
| 1151 input_text, |
| 1152 is_keyword); |
| 1153 } |
| 1154 } |
| 1155 |
1095 void SearchProvider::AddSuggestResultsToMap( | 1156 void SearchProvider::AddSuggestResultsToMap( |
1096 const SearchSuggestionParser::SuggestResults& results, | 1157 const SearchSuggestionParser::SuggestResults& results, |
1097 const std::string& metadata, | 1158 const std::string& metadata, |
1098 MatchMap* map) { | 1159 MatchMap* map) { |
1099 for (size_t i = 0; i < results.size(); ++i) { | 1160 for (size_t i = 0; i < results.size(); ++i) { |
1100 AddMatchToMap(results[i], metadata, i, false, | 1161 AddMatchToMap(results[i], metadata, i, false, |
1101 providers_.GetKeywordProviderURL() != NULL, map); | 1162 providers_.GetKeywordProviderURL() != NULL, map); |
1102 } | 1163 } |
1103 } | 1164 } |
1104 | 1165 |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1326 if (match->answer_contents.empty() && result.size() > 1) | 1387 if (match->answer_contents.empty() && result.size() > 1) |
1327 ++match; | 1388 ++match; |
1328 if (match->answer_contents.empty() || match->answer_type.empty() || | 1389 if (match->answer_contents.empty() || match->answer_type.empty() || |
1329 match->fill_into_edit.empty()) | 1390 match->fill_into_edit.empty()) |
1330 return; | 1391 return; |
1331 | 1392 |
1332 // Valid answer encountered, cache it for further queries. | 1393 // Valid answer encountered, cache it for further queries. |
1333 answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type); | 1394 answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type); |
1334 } | 1395 } |
1335 | 1396 |
1336 void SearchProvider::DoAnswersQuery(const AutocompleteInput& input) { | 1397 AnswersQueryData SearchProvider::FindAnswersPrefetchData() { |
1337 prefetch_data_ = answers_cache_.GetTopAnswerEntry(input.text()); | 1398 // Retrieve the top entry from scored history results. |
| 1399 MatchMap map; |
| 1400 AddTransformedHistoryResultsToMap(transformed_keyword_history_results_, |
| 1401 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, |
| 1402 &map); |
| 1403 AddTransformedHistoryResultsToMap(transformed_default_history_results_, |
| 1404 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, |
| 1405 &map); |
| 1406 |
| 1407 ACMatches matches; |
| 1408 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) |
| 1409 matches.push_back(i->second); |
| 1410 std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant); |
| 1411 |
| 1412 // If there is a top scoring entry, find the corresponding answer. |
| 1413 if (!matches.empty()) |
| 1414 return answers_cache_.GetTopAnswerEntry(matches[0].contents); |
| 1415 |
| 1416 return AnswersQueryData(); |
1338 } | 1417 } |
OLD | NEW |