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