Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(716)

Side by Side Diff: ui/app_list/search/mixer.cc

Issue 1136363003: Remove AppListMixer field trial. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@applist-mixer-priority-finch-test
Patch Set: Rebase. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/app_list/search/mixer.h ('k') | ui/app_list/search/mixer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/command_line.h"
14 #include "base/macros.h" 13 #include "base/macros.h"
15 #include "base/metrics/field_trial.h"
16 #include "ui/app_list/app_list_switches.h"
17 #include "ui/app_list/search_provider.h" 14 #include "ui/app_list/search_provider.h"
18 #include "ui/app_list/search_result.h" 15 #include "ui/app_list/search_result.h"
19 16
20 namespace app_list { 17 namespace app_list {
21 18
22 namespace { 19 namespace {
23 20
24 // Maximum number of results to show. Ignored if the AppListMixer field trial is 21 // Maximum number of results to show. Ignored if the AppListMixer field trial is
25 // "Blended". 22 // "Blended".
calamity 2016/07/13 06:57:27 Update comment.
26 const size_t kMaxResults = 6; 23 const size_t kMinResults = 6;
27
28 // The minimum number of results to show, if the AppListMixer field trial is
29 // "Blended". If this quota is not reached, the per-group limitations are
30 // removed and we try again. (We may still not reach the minumum, but at least
31 // we tried.) Ignored if the field trial is off.
32 const size_t kMinBlendedResults = 6;
33
34 const char kAppListMixerFieldTrialName[] = "AppListMixer";
35 const char kAppListMixerFieldTrialEnabled[] = "Blended";
36 const char kAppListMixerFieldTrialDisabled[] = "Control";
37 24
38 void UpdateResult(const SearchResult& source, SearchResult* target) { 25 void UpdateResult(const SearchResult& source, SearchResult* target) {
39 target->set_display_type(source.display_type()); 26 target->set_display_type(source.display_type());
40 target->set_title(source.title()); 27 target->set_title(source.title());
41 target->set_title_tags(source.title_tags()); 28 target->set_title_tags(source.title_tags());
42 target->set_details(source.details()); 29 target->set_details(source.details());
43 target->set_details_tags(source.details_tags()); 30 target->set_details_tags(source.details_tags());
44 } 31 }
45 32
46 // Returns true if the "AppListMixer" trial is set to "Blended". This is an
47 // experiment on the new Mixer logic that allows results from different groups
48 // to be blended together, rather than stratified.
49 bool IsBlendedMixerTrialEnabled() {
50 // Note: It's important to query the field trial state first, to ensure that
51 // UMA reports the correct group.
52 const std::string group_name =
53 base::FieldTrialList::FindFullName(kAppListMixerFieldTrialName);
54
55 // Respect command-line flags first.
56 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
57 switches::kDisableNewAppListMixer)) {
58 return false;
59 }
60
61 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
62 switches::kEnableNewAppListMixer)) {
63 return true;
64 }
65
66 // Next, respect field-trial groups.
67 if (group_name == kAppListMixerFieldTrialEnabled)
68 return true;
69
70 if (group_name == kAppListMixerFieldTrialDisabled)
71 return false;
72
73 // By default, enable the new logic if the experimental app list is enabled.
74 return app_list::switches::IsExperimentalAppListEnabled();
75 }
76
77 } // namespace 33 } // namespace
78 34
79 Mixer::SortData::SortData() : result(NULL), score(0.0) { 35 Mixer::SortData::SortData() : result(NULL), score(0.0) {
80 } 36 }
81 37
82 Mixer::SortData::SortData(SearchResult* result, double score) 38 Mixer::SortData::SortData(SearchResult* result, double score)
83 : result(result), score(score) { 39 : result(result), score(score) {
84 } 40 }
85 41
86 bool Mixer::SortData::operator<(const SortData& other) const { 42 bool Mixer::SortData::operator<(const SortData& other) const {
87 // This data precedes (less than) |other| if it has higher score. 43 // This data precedes (less than) |other| if it has higher score.
88 return score > other.score; 44 return score > other.score;
89 } 45 }
90 46
91 // Used to group relevant providers together for mixing their results. 47 // Used to group relevant providers together for mixing their results.
92 class Mixer::Group { 48 class Mixer::Group {
93 public: 49 public:
94 Group(size_t max_results, double boost, double multiplier) 50 Group(size_t max_results, double multiplier)
95 : max_results_(max_results), boost_(boost), multiplier_(multiplier) {} 51 : max_results_(max_results), multiplier_(multiplier) {}
96 ~Group() {} 52 ~Group() {}
97 53
98 void AddProvider(SearchProvider* provider) { providers_.push_back(provider); } 54 void AddProvider(SearchProvider* provider) { providers_.push_back(provider); }
99 55
100 void FetchResults(bool is_voice_query, const KnownResults& known_results) { 56 void FetchResults(bool is_voice_query, const KnownResults& known_results) {
101 results_.clear(); 57 results_.clear();
102 58
103 for (const SearchProvider* provider : providers_) { 59 for (const SearchProvider* provider : providers_) {
104 for (SearchResult* result : provider->results()) { 60 for (SearchResult* result : provider->results()) {
105 DCHECK(!result->id().empty()); 61 DCHECK(!result->id().empty());
106 62
107 // We cannot rely on providers to give relevance scores in the range 63 // We cannot rely on providers to give relevance scores in the range
108 // [0.0, 1.0] (e.g., PeopleProvider directly gives values from the 64 // [0.0, 1.0] (e.g., PeopleProvider directly gives values from the
109 // Google+ API). Clamp to that range. 65 // Google+ API). Clamp to that range.
110 double relevance = std::min(std::max(result->relevance(), 0.0), 1.0); 66 double relevance = std::min(std::max(result->relevance(), 0.0), 1.0);
111 67
112 double multiplier = multiplier_; 68 double multiplier = multiplier_;
113 double boost = boost_; 69 double boost = 0.0;
114 70
115 // Recommendations should not be affected by query-to-launch correlation 71 // Recommendations should not be affected by query-to-launch correlation
116 // from KnownResults as it causes recommendations to become dominated by 72 // from KnownResults as it causes recommendations to become dominated by
117 // previously clicked results. This happens because the recommendation 73 // previously clicked results. This happens because the recommendation
118 // query is the empty string and the clicked results get forever 74 // query is the empty string and the clicked results get forever
119 // boosted. 75 // boosted.
120 if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) { 76 if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) {
121 KnownResults::const_iterator known_it = 77 KnownResults::const_iterator known_it =
122 known_results.find(result->id()); 78 known_results.find(result->id());
123 if (known_it != known_results.end()) { 79 if (known_it != known_results.end()) {
(...skipping 28 matching lines...) Expand all
152 std::sort(results_.begin(), results_.end()); 108 std::sort(results_.begin(), results_.end());
153 } 109 }
154 110
155 const SortedResults& results() const { return results_; } 111 const SortedResults& results() const { return results_; }
156 112
157 size_t max_results() const { return max_results_; } 113 size_t max_results() const { return max_results_; }
158 114
159 private: 115 private:
160 typedef std::vector<SearchProvider*> Providers; 116 typedef std::vector<SearchProvider*> Providers;
161 const size_t max_results_; 117 const size_t max_results_;
162 const double boost_;
163 const double multiplier_; 118 const double multiplier_;
164 119
165 Providers providers_; // Not owned. 120 Providers providers_; // Not owned.
166 SortedResults results_; 121 SortedResults results_;
167 122
168 DISALLOW_COPY_AND_ASSIGN(Group); 123 DISALLOW_COPY_AND_ASSIGN(Group);
169 }; 124 };
170 125
171 Mixer::Mixer(AppListModel::SearchResults* ui_results) 126 Mixer::Mixer(AppListModel::SearchResults* ui_results)
172 : ui_results_(ui_results) { 127 : ui_results_(ui_results) {
173 } 128 }
174 Mixer::~Mixer() { 129 Mixer::~Mixer() {
175 } 130 }
176 131
177 size_t Mixer::AddGroup(size_t max_results, double boost, double multiplier) { 132 size_t Mixer::AddGroup(size_t max_results, double multiplier) {
178 // Only consider |boost| if the AppListMixer field trial is default. 133 groups_.push_back(new Group(max_results, multiplier));
179 // Only consider |multiplier| if the AppListMixer field trial is "Blended".
180 if (IsBlendedMixerTrialEnabled())
181 boost = 0.0;
182 else
183 multiplier = 1.0;
184 groups_.push_back(new Group(max_results, boost, multiplier));
185 return groups_.size() - 1; 134 return groups_.size() - 1;
186 } 135 }
187 136
188 size_t Mixer::AddOmniboxGroup(size_t max_results,
189 double boost,
190 double multiplier) {
191 // There should not already be an omnibox group.
192 DCHECK(!has_omnibox_group_);
193 size_t id = AddGroup(max_results, boost, multiplier);
194 omnibox_group_ = id;
195 has_omnibox_group_ = true;
196 return id;
197 }
198
199 void Mixer::AddProviderToGroup(size_t group_id, SearchProvider* provider) { 137 void Mixer::AddProviderToGroup(size_t group_id, SearchProvider* provider) {
200 groups_[group_id]->AddProvider(provider); 138 groups_[group_id]->AddProvider(provider);
201 } 139 }
202 140
203 void Mixer::MixAndPublish(bool is_voice_query, 141 void Mixer::MixAndPublish(bool is_voice_query,
204 const KnownResults& known_results) { 142 const KnownResults& known_results) {
205 FetchResults(is_voice_query, known_results); 143 FetchResults(is_voice_query, known_results);
206 144
207 SortedResults results; 145 SortedResults results;
146 results.reserve(kMinResults);
208 147
209 if (IsBlendedMixerTrialEnabled()) { 148 // Add results from each group. Limit to the maximum number of results in each
210 results.reserve(kMinBlendedResults); 149 // group.
150 for (const Group* group : groups_) {
151 size_t num_results =
152 std::min(group->results().size(), group->max_results());
153 results.insert(results.end(), group->results().begin(),
154 group->results().begin() + num_results);
155 }
156 // Remove results with duplicate IDs before sorting. If two providers give a
157 // result with the same ID, the result from the provider with the *lower group
158 // number* will be kept (e.g., an app result takes priority over a web store
159 // result with the same ID).
160 RemoveDuplicates(&results);
161 std::sort(results.begin(), results.end());
211 162
212 // Add results from each group. Limit to the maximum number of results in 163 if (results.size() < kMinResults) {
213 // each group. 164 size_t original_size = results.size();
165 // We didn't get enough results. Insert all the results again, and this
166 // time, do not limit the maximum number of results from each group. (This
167 // will result in duplicates, which will be removed by RemoveDuplicates.)
214 for (const Group* group : groups_) { 168 for (const Group* group : groups_) {
215 size_t num_results =
216 std::min(group->results().size(), group->max_results());
217 results.insert(results.end(), group->results().begin(), 169 results.insert(results.end(), group->results().begin(),
218 group->results().begin() + num_results); 170 group->results().end());
219 } 171 }
220 // Remove results with duplicate IDs before sorting. If two providers give a
221 // result with the same ID, the result from the provider with the *lower
222 // group number* will be kept (e.g., an app result takes priority over a web
223 // store result with the same ID).
224 RemoveDuplicates(&results); 172 RemoveDuplicates(&results);
225 std::sort(results.begin(), results.end()); 173 // Sort just the newly added results. This ensures that, for example, if
226 174 // there are 6 Omnibox results (score = 0.8) and 1 People result (score =
227 if (results.size() < kMinBlendedResults) { 175 // 0.4) that the People result will be 5th, not 7th, because the Omnibox
228 size_t original_size = results.size(); 176 // group has a soft maximum of 4 results. (Otherwise, the People result
229 // We didn't get enough results. Insert all the results again, and this 177 // would not be seen at all once the result list is truncated.)
230 // time, do not limit the maximum number of results from each group. (This 178 std::sort(results.begin() + original_size, results.end());
231 // will result in duplicates, which will be removed by RemoveDuplicates.)
232 for (const Group* group : groups_) {
233 results.insert(results.end(), group->results().begin(),
234 group->results().end());
235 }
236 RemoveDuplicates(&results);
237 // Sort just the newly added results. This ensures that, for example, if
238 // there are 6 Omnibox results (score = 0.8) and 1 People result (score =
239 // 0.4) that the People result will be 5th, not 7th, because the Omnibox
240 // group has a soft maximum of 4 results. (Otherwise, the People result
241 // would not be seen at all once the result list is truncated.)
242 std::sort(results.begin() + original_size, results.end());
243 }
244 } else {
245 results.reserve(kMaxResults);
246
247 // Add results from non-omnibox groups first. Limit to the maximum number of
248 // results in each group.
249 for (size_t i = 0; i < groups_.size(); ++i) {
250 if (!has_omnibox_group_ || i != omnibox_group_) {
251 const Group& group = *groups_[i];
252 size_t num_results =
253 std::min(group.results().size(), group.max_results());
254 results.insert(results.end(), group.results().begin(),
255 group.results().begin() + num_results);
256 }
257 }
258
259 // Collapse duplicate apps from local and web store.
260 RemoveDuplicates(&results);
261
262 // Fill the remaining slots with omnibox results. Always add at least one
263 // omnibox result (even if there are no more slots; if we over-fill the
264 // vector, the web store and people results will be removed in a later
265 // step). Note: max_results() is ignored for the omnibox group.
266 if (has_omnibox_group_) {
267 CHECK_LT(omnibox_group_, groups_.size());
268 const Group& omnibox_group = *groups_[omnibox_group_];
269 const size_t omnibox_results = std::min(
270 omnibox_group.results().size(),
271 results.size() < kMaxResults ? kMaxResults - results.size() : 1);
272 results.insert(results.end(), omnibox_group.results().begin(),
273 omnibox_group.results().begin() + omnibox_results);
274 }
275
276 std::sort(results.begin(), results.end());
277 RemoveDuplicates(&results);
278 if (results.size() > kMaxResults)
279 results.resize(kMaxResults);
280 } 179 }
281 180
282 Publish(results, ui_results_); 181 Publish(results, ui_results_);
283 } 182 }
284 183
285 void Mixer::Publish(const SortedResults& new_results, 184 void Mixer::Publish(const SortedResults& new_results,
286 AppListModel::SearchResults* ui_results) { 185 AppListModel::SearchResults* ui_results) {
287 typedef std::map<std::string, SearchResult*> IdToResultMap; 186 typedef std::map<std::string, SearchResult*> IdToResultMap;
288 187
289 // The following algorithm is used: 188 // The following algorithm is used:
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 results->swap(final); 249 results->swap(final);
351 } 250 }
352 251
353 void Mixer::FetchResults(bool is_voice_query, 252 void Mixer::FetchResults(bool is_voice_query,
354 const KnownResults& known_results) { 253 const KnownResults& known_results) {
355 for (auto* group : groups_) 254 for (auto* group : groups_)
356 group->FetchResults(is_voice_query, known_results); 255 group->FetchResults(is_voice_query, known_results);
357 } 256 }
358 257
359 } // namespace app_list 258 } // namespace app_list
OLDNEW
« no previous file with comments | « ui/app_list/search/mixer.h ('k') | ui/app_list/search/mixer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698