| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "ui/app_list/search/mixer.h" | 5 #include "ui/app_list/search/mixer.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 void UpdateResult(const SearchResult& source, SearchResult* target) { | 21 void UpdateResult(const SearchResult& source, SearchResult* target) { |
| 22 target->set_display_type(source.display_type()); | 22 target->set_display_type(source.display_type()); |
| 23 target->set_title(source.title()); | 23 target->set_title(source.title()); |
| 24 target->set_title_tags(source.title_tags()); | 24 target->set_title_tags(source.title_tags()); |
| 25 target->set_details(source.details()); | 25 target->set_details(source.details()); |
| 26 target->set_details_tags(source.details_tags()); | 26 target->set_details_tags(source.details_tags()); |
| 27 } | 27 } |
| 28 | 28 |
| 29 } // namespace | 29 } // namespace |
| 30 | 30 |
| 31 Mixer::SortData::SortData() : result(NULL), score(0.0) { | 31 Mixer::SortData::SortData() : result(nullptr), score(0.0) {} |
| 32 } | |
| 33 | 32 |
| 34 Mixer::SortData::SortData(SearchResult* result, double score) | 33 Mixer::SortData::SortData(SearchResult* result, double score) |
| 35 : result(result), score(score) { | 34 : result(result), score(score) { |
| 36 } | 35 } |
| 37 | 36 |
| 38 bool Mixer::SortData::operator<(const SortData& other) const { | 37 bool Mixer::SortData::operator<(const SortData& other) const { |
| 39 // This data precedes (less than) |other| if it has higher score. | 38 // This data precedes (less than) |other| if it has higher score. |
| 40 return score > other.score; | 39 return score > other.score; |
| 41 } | 40 } |
| 42 | 41 |
| 43 // Used to group relevant providers together for mixing their results. | 42 // Used to group relevant providers together for mixing their results. |
| 44 class Mixer::Group { | 43 class Mixer::Group { |
| 45 public: | 44 public: |
| 46 Group(size_t max_results, double multiplier) | 45 Group(size_t max_results, double multiplier) |
| 47 : max_results_(max_results), multiplier_(multiplier) {} | 46 : max_results_(max_results), multiplier_(multiplier) {} |
| 48 ~Group() {} | 47 ~Group() {} |
| 49 | 48 |
| 50 void AddProvider(SearchProvider* provider) { providers_.push_back(provider); } | 49 void AddProvider(SearchProvider* provider) { |
| 50 providers_.emplace_back(provider); |
| 51 } |
| 51 | 52 |
| 52 void FetchResults(bool is_voice_query, const KnownResults& known_results) { | 53 void FetchResults(bool is_voice_query, const KnownResults& known_results) { |
| 53 results_.clear(); | 54 results_.clear(); |
| 54 | 55 |
| 55 for (const SearchProvider* provider : providers_) { | 56 for (const SearchProvider* provider : providers_) { |
| 56 for (SearchResult* result : provider->results()) { | 57 for (const auto& result : provider->results()) { |
| 57 DCHECK(!result->id().empty()); | 58 DCHECK(!result->id().empty()); |
| 58 | 59 |
| 59 // We cannot rely on providers to give relevance scores in the range | 60 // We cannot rely on providers to give relevance scores in the range |
| 60 // [0.0, 1.0] (e.g., PeopleProvider directly gives values from the | 61 // [0.0, 1.0] (e.g., PeopleProvider directly gives values from the |
| 61 // Google+ API). Clamp to that range. | 62 // Google+ API). Clamp to that range. |
| 62 double relevance = std::min(std::max(result->relevance(), 0.0), 1.0); | 63 const double relevance = |
| 63 | 64 std::min(std::max(result->relevance(), 0.0), 1.0); |
| 64 double multiplier = multiplier_; | |
| 65 double boost = 0.0; | 65 double boost = 0.0; |
| 66 | 66 |
| 67 // Recommendations should not be affected by query-to-launch correlation | 67 // Recommendations should not be affected by query-to-launch correlation |
| 68 // from KnownResults as it causes recommendations to become dominated by | 68 // from KnownResults as it causes recommendations to become dominated by |
| 69 // previously clicked results. This happens because the recommendation | 69 // previously clicked results. This happens because the recommendation |
| 70 // query is the empty string and the clicked results get forever | 70 // query is the empty string and the clicked results get forever |
| 71 // boosted. | 71 // boosted. |
| 72 if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) { | 72 if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) { |
| 73 KnownResults::const_iterator known_it = | 73 KnownResults::const_iterator known_it = |
| 74 known_results.find(result->id()); | 74 known_results.find(result->id()); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 90 NOTREACHED() << "Unknown result in KnownResults?"; | 90 NOTREACHED() << "Unknown result in KnownResults?"; |
| 91 break; | 91 break; |
| 92 } | 92 } |
| 93 } | 93 } |
| 94 | 94 |
| 95 // If this is a voice query, voice results receive a massive boost. | 95 // If this is a voice query, voice results receive a massive boost. |
| 96 if (is_voice_query && result->voice_result()) | 96 if (is_voice_query && result->voice_result()) |
| 97 boost += 4.0; | 97 boost += 4.0; |
| 98 } | 98 } |
| 99 | 99 |
| 100 results_.push_back(SortData(result, relevance * multiplier + boost)); | 100 results_.emplace_back(result.get(), relevance * multiplier_ + boost); |
| 101 } | 101 } |
| 102 } | 102 } |
| 103 | 103 |
| 104 std::sort(results_.begin(), results_.end()); | 104 std::sort(results_.begin(), results_.end()); |
| 105 } | 105 } |
| 106 | 106 |
| 107 const SortedResults& results() const { return results_; } | 107 const SortedResults& results() const { return results_; } |
| 108 | 108 |
| 109 size_t max_results() const { return max_results_; } | 109 size_t max_results() const { return max_results_; } |
| 110 | 110 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 138 const KnownResults& known_results, | 138 const KnownResults& known_results, |
| 139 size_t num_max_results) { | 139 size_t num_max_results) { |
| 140 FetchResults(is_voice_query, known_results); | 140 FetchResults(is_voice_query, known_results); |
| 141 | 141 |
| 142 SortedResults results; | 142 SortedResults results; |
| 143 results.reserve(num_max_results); | 143 results.reserve(num_max_results); |
| 144 | 144 |
| 145 // Add results from each group. Limit to the maximum number of results in each | 145 // Add results from each group. Limit to the maximum number of results in each |
| 146 // group. | 146 // group. |
| 147 for (const Group* group : groups_) { | 147 for (const Group* group : groups_) { |
| 148 size_t num_results = | 148 const size_t num_results = |
| 149 std::min(group->results().size(), group->max_results()); | 149 std::min(group->results().size(), group->max_results()); |
| 150 results.insert(results.end(), group->results().begin(), | 150 results.insert(results.end(), group->results().begin(), |
| 151 group->results().begin() + num_results); | 151 group->results().begin() + num_results); |
| 152 } | 152 } |
| 153 // Remove results with duplicate IDs before sorting. If two providers give a | 153 // Remove results with duplicate IDs before sorting. If two providers give a |
| 154 // result with the same ID, the result from the provider with the *lower group | 154 // result with the same ID, the result from the provider with the *lower group |
| 155 // number* will be kept (e.g., an app result takes priority over a web store | 155 // number* will be kept (e.g., an app result takes priority over a web store |
| 156 // result with the same ID). | 156 // result with the same ID). |
| 157 RemoveDuplicates(&results); | 157 RemoveDuplicates(&results); |
| 158 std::sort(results.begin(), results.end()); | 158 std::sort(results.begin(), results.end()); |
| 159 | 159 |
| 160 if (results.size() < num_max_results) { | 160 const size_t original_size = results.size(); |
| 161 size_t original_size = results.size(); | 161 if (original_size < num_max_results) { |
| 162 // We didn't get enough results. Insert all the results again, and this | 162 // We didn't get enough results. Insert all the results again, and this |
| 163 // time, do not limit the maximum number of results from each group. (This | 163 // time, do not limit the maximum number of results from each group. (This |
| 164 // will result in duplicates, which will be removed by RemoveDuplicates.) | 164 // will result in duplicates, which will be removed by RemoveDuplicates.) |
| 165 for (const Group* group : groups_) { | 165 for (const Group* group : groups_) { |
| 166 results.insert(results.end(), group->results().begin(), | 166 results.insert(results.end(), group->results().begin(), |
| 167 group->results().end()); | 167 group->results().end()); |
| 168 } | 168 } |
| 169 RemoveDuplicates(&results); | 169 RemoveDuplicates(&results); |
| 170 // Sort just the newly added results. This ensures that, for example, if | 170 // Sort just the newly added results. This ensures that, for example, if |
| 171 // there are 6 Omnibox results (score = 0.8) and 1 People result (score = | 171 // there are 6 Omnibox results (score = 0.8) and 1 People result (score = |
| (...skipping 13 matching lines...) Expand all Loading... |
| 185 // to item. | 185 // to item. |
| 186 // 2. Use the order of items in |new_results| to build an ordered list. If the | 186 // 2. Use the order of items in |new_results| to build an ordered list. If the |
| 187 // result IDs exist in the map, update and use the item in the map and delete | 187 // result IDs exist in the map, update and use the item in the map and delete |
| 188 // it from the map afterwards. Otherwise, clone new items from |new_results|. | 188 // it from the map afterwards. Otherwise, clone new items from |new_results|. |
| 189 // 3. Delete the objects remaining in the map as they are unused. | 189 // 3. Delete the objects remaining in the map as they are unused. |
| 190 | 190 |
| 191 // We have to erase all results at once so that observers are notified with | 191 // We have to erase all results at once so that observers are notified with |
| 192 // meaningful indexes. | 192 // meaningful indexes. |
| 193 auto current_results = ui_results->RemoveAll(); | 193 auto current_results = ui_results->RemoveAll(); |
| 194 std::map<std::string, std::unique_ptr<SearchResult>> ui_results_map; | 194 std::map<std::string, std::unique_ptr<SearchResult>> ui_results_map; |
| 195 for (std::unique_ptr<SearchResult>& ui_result : current_results) | 195 for (auto& ui_result : current_results) |
| 196 ui_results_map[ui_result->id()] = std::move(ui_result); | 196 ui_results_map[ui_result->id()] = std::move(ui_result); |
| 197 | 197 |
| 198 // Add items back to |ui_results| in the order of |new_results|. | 198 // Add items back to |ui_results| in the order of |new_results|. |
| 199 for (const SortData& sort_data : new_results) { | 199 for (const SortData& sort_data : new_results) { |
| 200 const SearchResult& new_result = *sort_data.result; | 200 const SearchResult& new_result = *sort_data.result; |
| 201 auto ui_result_it = ui_results_map.find(new_result.id()); | 201 auto ui_result_it = ui_results_map.find(new_result.id()); |
| 202 if (ui_result_it != ui_results_map.end()) { | 202 if (ui_result_it != ui_results_map.end()) { |
| 203 // Update and use the old result if it exists. | 203 // Update and use the old result if it exists. |
| 204 std::unique_ptr<SearchResult> ui_result = std::move(ui_result_it->second); | 204 std::unique_ptr<SearchResult> ui_result = std::move(ui_result_it->second); |
| 205 UpdateResult(new_result, ui_result.get()); | 205 UpdateResult(new_result, ui_result.get()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 220 | 220 |
| 221 // Any remaining results in |ui_results_map| will be automatically deleted. | 221 // Any remaining results in |ui_results_map| will be automatically deleted. |
| 222 } | 222 } |
| 223 | 223 |
| 224 void Mixer::RemoveDuplicates(SortedResults* results) { | 224 void Mixer::RemoveDuplicates(SortedResults* results) { |
| 225 SortedResults final; | 225 SortedResults final; |
| 226 final.reserve(results->size()); | 226 final.reserve(results->size()); |
| 227 | 227 |
| 228 std::set<std::string> id_set; | 228 std::set<std::string> id_set; |
| 229 for (const SortData& sort_data : *results) { | 229 for (const SortData& sort_data : *results) { |
| 230 const std::string& id = sort_data.result->id(); | 230 if (!id_set.insert(sort_data.result->id()).second) |
| 231 if (id_set.find(id) != id_set.end()) | |
| 232 continue; | 231 continue; |
| 233 | 232 |
| 234 id_set.insert(id); | 233 final.emplace_back(sort_data); |
| 235 final.push_back(sort_data); | |
| 236 } | 234 } |
| 237 | 235 |
| 238 results->swap(final); | 236 results->swap(final); |
| 239 } | 237 } |
| 240 | 238 |
| 241 void Mixer::FetchResults(bool is_voice_query, | 239 void Mixer::FetchResults(bool is_voice_query, |
| 242 const KnownResults& known_results) { | 240 const KnownResults& known_results) { |
| 243 for (auto* group : groups_) | 241 for (auto* group : groups_) |
| 244 group->FetchResults(is_voice_query, known_results); | 242 group->FetchResults(is_voice_query, known_results); |
| 245 } | 243 } |
| 246 | 244 |
| 247 } // namespace app_list | 245 } // namespace app_list |
| OLD | NEW |