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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/app_list/search/mixer.h"
12 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
13 #include "chrome/browser/ui/app_list/search/search_provider.h"
19 // Maximum number of results to show.
20 const size_t kMaxResults
= 6;
21 const size_t kMaxMainGroupResults
= 4;
22 const size_t kMaxWebstoreResults
= 2;
23 const size_t kMaxPeopleResults
= 2;
25 // A value to indicate no max number of results limit.
26 const size_t kNoMaxResultsLimit
= 0;
28 // Used for sorting and mixing results.
34 SortData(ChromeSearchResult
* result
, double score
)
39 bool operator<(const SortData
& other
) const {
40 // This data precedes (less than) |other| if it has higher score.
41 return score
> other
.score
;
44 ChromeSearchResult
* result
; // Not owned.
47 typedef std::vector
<SortData
> SortedResults
;
49 // Removes duplicates from |results|.
50 void RemoveDuplicates(SortedResults
* results
) {
52 final
.reserve(results
->size());
54 std::set
<std::string
> id_set
;
55 for (SortedResults::iterator it
= results
->begin();
58 const std::string
& id
= it
->result
->id();
59 if (id_set
.find(id
) != id_set
.end())
69 // Publishes the given |results| to |ui_results|. Reuse existing ones to avoid
71 void Publish(const SortedResults
& results
,
72 AppListModel::SearchResults
* ui_results
) {
73 for (size_t i
= 0; i
< results
.size(); ++i
) {
74 ChromeSearchResult
* result
= results
[i
].result
;
76 ChromeSearchResult
* ui_result
= i
< ui_results
->item_count() ?
77 static_cast<ChromeSearchResult
*>(ui_results
->GetItemAt(i
)) : NULL
;
78 if (ui_result
&& ui_result
->id() == result
->id()) {
79 ui_result
->set_title(result
->title());
80 ui_result
->set_title_tags(result
->title_tags());
81 ui_result
->set_details(result
->details());
82 ui_result
->set_details_tags(result
->details_tags());
83 ui_results
->NotifyItemsChanged(i
, 1);
86 ui_results
->DeleteAt(i
);
87 ui_results
->AddAt(i
, result
->Duplicate().release());
91 while (ui_results
->item_count() > results
.size())
92 ui_results
->DeleteAt(ui_results
->item_count() - 1);
97 // Used to group relevant providers together fox mixing their results.
100 Group(size_t max_results
, double boost
)
101 : max_results_(max_results
),
106 void AddProvider(SearchProvider
* provider
) {
107 providers_
.push_back(provider
);
110 void FetchResults(const KnownResults
& known_results
) {
113 for (Providers::const_iterator provider_it
= providers_
.begin();
114 provider_it
!= providers_
.end();
116 for (SearchProvider::Results::const_iterator
117 result_it
= (*provider_it
)->results().begin();
118 result_it
!= (*provider_it
)->results().end();
120 DCHECK_GE((*result_it
)->relevance(), 0.0);
121 DCHECK_LE((*result_it
)->relevance(), 1.0);
122 DCHECK(!(*result_it
)->id().empty());
124 double boost
= boost_
;
125 KnownResults::const_iterator known_it
=
126 known_results
.find((*result_it
)->id());
127 if (known_it
!= known_results
.end()) {
128 switch (known_it
->second
) {
129 case PERFECT_PRIMARY
:
135 case PERFECT_SECONDARY
:
138 case PREFIX_SECONDARY
:
142 NOTREACHED() << "Unknown result in KnownResults?";
148 SortData(*result_it
, (*result_it
)->relevance() + boost
));
152 std::sort(results_
.begin(), results_
.end());
153 if (max_results_
!= kNoMaxResultsLimit
&& results_
.size() > max_results_
)
154 results_
.resize(max_results_
);
157 const SortedResults
& results() const { return results_
; }
160 typedef std::vector
<SearchProvider
*> Providers
;
161 const size_t max_results_
;
164 Providers providers_
; // Not owned.
165 SortedResults results_
;
167 DISALLOW_COPY_AND_ASSIGN(Group
);
170 Mixer::Mixer(AppListModel::SearchResults
* ui_results
)
171 : ui_results_(ui_results
) {}
175 groups_
.push_back(new Group(kMaxMainGroupResults
, 3.0));
176 groups_
.push_back(new Group(kNoMaxResultsLimit
, 2.0));
177 groups_
.push_back(new Group(kMaxWebstoreResults
, 1.0));
178 groups_
.push_back(new Group(kMaxPeopleResults
, 0.0));
181 void Mixer::AddProviderToGroup(GroupId group
, SearchProvider
* provider
) {
182 size_t group_index
= static_cast<size_t>(group
);
183 groups_
[group_index
]->AddProvider(provider
);
186 void Mixer::MixAndPublish(const KnownResults
& known_results
) {
187 FetchResults(known_results
);
189 SortedResults results
;
190 results
.reserve(kMaxResults
);
192 // Adds main group and web store results first.
193 results
.insert(results
.end(),
194 groups_
[MAIN_GROUP
]->results().begin(),
195 groups_
[MAIN_GROUP
]->results().end());
196 results
.insert(results
.end(),
197 groups_
[WEBSTORE_GROUP
]->results().begin(),
198 groups_
[WEBSTORE_GROUP
]->results().end());
199 results
.insert(results
.end(),
200 groups_
[PEOPLE_GROUP
]->results().begin(),
201 groups_
[PEOPLE_GROUP
]->results().end());
203 // Collapse duplicate apps from local and web store.
204 RemoveDuplicates(&results
);
206 DCHECK_GE(kMaxResults
, results
.size());
207 size_t remaining_slots
= kMaxResults
- results
.size();
209 // Reserves at least one slot for the omnibox result. If there is no available
210 // slot for omnibox results, removes the last one from web store.
211 const size_t omnibox_results
= groups_
[OMNIBOX_GROUP
]->results().size();
212 if (!remaining_slots
&& omnibox_results
)
215 remaining_slots
= std::min(kMaxResults
- results
.size(), omnibox_results
);
216 results
.insert(results
.end(),
217 groups_
[OMNIBOX_GROUP
]->results().begin(),
218 groups_
[OMNIBOX_GROUP
]->results().begin() + remaining_slots
);
220 std::sort(results
.begin(), results
.end());
221 RemoveDuplicates(&results
);
222 if (results
.size() > kMaxResults
)
223 results
.resize(kMaxResults
);
225 Publish(results
, ui_results_
);
228 void Mixer::FetchResults(const KnownResults
& known_results
) {
229 for (Groups::iterator group_it
= groups_
.begin();
230 group_it
!= groups_
.end();
232 (*group_it
)->FetchResults(known_results
);
236 } // namespace app_list