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

Side by Side Diff: chrome/browser/autocomplete/contact_provider_chromeos.cc

Issue 10916304: autocomplete: Make ContactProvider rank contacts by affinity (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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 "chrome/browser/autocomplete/contact_provider_chromeos.h" 5 #include "chrome/browser/autocomplete/contact_provider_chromeos.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath>
8 9
9 #include "base/i18n/break_iterator.h" 10 #include "base/i18n/break_iterator.h"
10 #include "base/i18n/string_search.h" 11 #include "base/i18n/string_search.h"
11 #include "base/string_split.h" 12 #include "base/string_split.h"
12 #include "base/string16.h" 13 #include "base/string16.h"
13 #include "base/utf_string_conversions.h" 14 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/autocomplete/autocomplete_input.h" 15 #include "chrome/browser/autocomplete/autocomplete_input.h"
15 #include "chrome/browser/chromeos/contacts/contact.pb.h" 16 #include "chrome/browser/chromeos/contacts/contact.pb.h"
16 #include "chrome/browser/chromeos/contacts/contact_manager.h" 17 #include "chrome/browser/chromeos/contacts/contact_manager.h"
17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
18 19
19 namespace { 20 namespace {
20 21
22 // Default affinity assigned to contacts whose |affinity| field is unset.
23 float kDefaultAffinity = 1.0;
Peter Kasting 2012/09/13 23:32:37 Isn't this kind of high? I guess I don't totally
Daniel Erat 2012/09/14 15:31:50 Dirty secret: We're not getting affinity data yet,
24
21 // Returns true if |word_to_find| is a prefix of |name_to_search| and marks the 25 // Returns true if |word_to_find| is a prefix of |name_to_search| and marks the
22 // matching text in |classifications| (which corresponds to the contact's full 26 // matching text in |classifications| (which corresponds to the contact's full
23 // name). |name_index_in_full_name| contains |name_to_search|'s index within 27 // name). |name_index_in_full_name| contains |name_to_search|'s index within
24 // the full name or string16::npos if it doesn't appear in it. 28 // the full name or string16::npos if it doesn't appear in it.
25 bool WordIsNamePrefix(const string16& word_to_find, 29 bool WordIsNamePrefix(const string16& word_to_find,
26 const string16& name_to_search, 30 const string16& name_to_search,
27 size_t name_index_in_full_name, 31 size_t name_index_in_full_name,
28 size_t full_name_length, 32 size_t full_name_length,
29 ACMatchClassifications* classifications) { 33 ACMatchClassifications* classifications) {
30 DCHECK(classifications); 34 DCHECK(classifications);
31 35
32 size_t match_index = 0; 36 size_t match_index = 0;
33 size_t match_length = 0; 37 size_t match_length = 0;
34 if (!base::i18n::StringSearchIgnoringCaseAndAccents(word_to_find, 38 if (!base::i18n::StringSearchIgnoringCaseAndAccents(word_to_find,
35 name_to_search, &match_index, &match_length) || (match_index != 0)) 39 name_to_search, &match_index, &match_length) || (match_index != 0))
36 return false; 40 return false;
37 41
38 if (name_index_in_full_name != string16::npos) { 42 if (name_index_in_full_name != string16::npos) {
39 AutocompleteMatch::ACMatchClassifications new_class; 43 AutocompleteMatch::ACMatchClassifications new_class;
40 AutocompleteMatch::ClassifyLocationInString(name_index_in_full_name, 44 AutocompleteMatch::ClassifyLocationInString(name_index_in_full_name,
41 match_length, full_name_length, 0, &new_class); 45 match_length, full_name_length, 0, &new_class);
42 *classifications = AutocompleteMatch::MergeClassifications( 46 *classifications = AutocompleteMatch::MergeClassifications(
43 *classifications, new_class); 47 *classifications, new_class);
44 } 48 }
45 49
46 return true; 50 return true;
47 } 51 }
48 52
53 // Returns true if |a|'s affinity is greater than |b|'s affinity.
54 bool CompareContactAffinity(const contacts::Contact* a,
55 const contacts::Contact* b) {
56 float affinity_a = a->has_affinity() ? a->affinity() : kDefaultAffinity;
Peter Kasting 2012/09/13 23:32:37 Super tiny nit: Seems like |a_affinity| would be j
Daniel Erat 2012/09/14 15:31:50 Done.
57 float affinity_b = b->has_affinity() ? b->affinity() : kDefaultAffinity;
58 return affinity_a > affinity_b;
59 }
60
49 } // namespace 61 } // namespace
50 62
51 // static 63 // static
52 const char ContactProvider::kMatchContactIdKey[] = "contact_id"; 64 const char ContactProvider::kMatchContactIdKey[] = "contact_id";
65 const int ContactProvider::kBaseRelevance = 1300;
Daniel Erat 2012/09/13 23:10:08 I just pulled numbers out of the air for this. Si
66 const int ContactProvider::kAffinityRelevanceBoost = 200;
53 67
54 // Cached information about a contact. 68 // Cached information about a contact.
55 struct ContactProvider::ContactData { 69 struct ContactProvider::ContactData {
56 ContactData(const string16& full_name, 70 ContactData(const string16& full_name,
57 const string16& given_name, 71 const string16& given_name,
58 const string16& family_name, 72 const string16& family_name,
59 const std::string& contact_id) 73 const std::string& contact_id,
74 float affinity)
60 : full_name(full_name), 75 : full_name(full_name),
61 given_name(given_name), 76 given_name(given_name),
62 family_name(family_name), 77 family_name(family_name),
63 given_name_index(string16::npos), 78 given_name_index(string16::npos),
64 family_name_index(string16::npos), 79 family_name_index(string16::npos),
65 contact_id(contact_id) { 80 contact_id(contact_id),
81 affinity(affinity) {
66 base::i18n::StringSearchIgnoringCaseAndAccents( 82 base::i18n::StringSearchIgnoringCaseAndAccents(
67 given_name, full_name, &given_name_index, NULL); 83 given_name, full_name, &given_name_index, NULL);
68 base::i18n::StringSearchIgnoringCaseAndAccents( 84 base::i18n::StringSearchIgnoringCaseAndAccents(
69 family_name, full_name, &family_name_index, NULL); 85 family_name, full_name, &family_name_index, NULL);
70 } 86 }
71 87
72 string16 full_name; 88 string16 full_name;
73 string16 given_name; 89 string16 given_name;
74 string16 family_name; 90 string16 family_name;
75 91
76 // Indices into |full_name| where |given_name| and |family_name| first appear, 92 // Indices into |full_name| where |given_name| and |family_name| first appear,
77 // or string16::npos if they don't appear in it. 93 // or string16::npos if they don't appear in it.
78 size_t given_name_index; 94 size_t given_name_index;
79 size_t family_name_index; 95 size_t family_name_index;
80 96
81 // Unique ID used to look up additional contact information. 97 // Unique ID used to look up additional contact information.
82 std::string contact_id; 98 std::string contact_id;
99
100 // Affinity between the user and this contact, in the range [0.0, 1.0].
101 float affinity;
83 }; 102 };
84 103
85 ContactProvider::ContactProvider( 104 ContactProvider::ContactProvider(
86 AutocompleteProviderListener* listener, 105 AutocompleteProviderListener* listener,
87 Profile* profile, 106 Profile* profile,
88 base::WeakPtr<contacts::ContactManagerInterface> contact_manager) 107 base::WeakPtr<contacts::ContactManagerInterface> contact_manager)
89 : AutocompleteProvider(listener, profile, TYPE_CONTACT), 108 : AutocompleteProvider(listener, profile, TYPE_CONTACT),
90 contact_manager_(contact_manager) { 109 contact_manager_(contact_manager) {
91 contact_manager_->AddObserver(this, profile); 110 contact_manager_->AddObserver(this, profile);
92 RefreshContacts(); 111 RefreshContacts();
(...skipping 15 matching lines...) Expand all
108 base::i18n::BreakIterator break_iterator( 127 base::i18n::BreakIterator break_iterator(
109 input.text(), 128 input.text(),
110 base::i18n::BreakIterator::BREAK_WORD); 129 base::i18n::BreakIterator::BREAK_WORD);
111 if (break_iterator.Init()) { 130 if (break_iterator.Init()) {
112 while (break_iterator.Advance()) { 131 while (break_iterator.Advance()) {
113 if (break_iterator.IsWord()) 132 if (break_iterator.IsWord())
114 input_words.push_back(break_iterator.GetString()); 133 input_words.push_back(break_iterator.GetString());
115 } 134 }
116 } 135 }
117 136
137 // |contacts_| is ordered by descending affinity. Since affinity is currently
138 // the only signal used for computing relevance, we can stop after we've found
139 // kMaxMatches results.
118 for (ContactDataVector::const_iterator it = contacts_.begin(); 140 for (ContactDataVector::const_iterator it = contacts_.begin();
119 it != contacts_.end(); ++it) 141 it != contacts_.end() && matches_.size() < kMaxMatches; ++it)
120 AddContactIfMatched(input, input_words, *it); 142 AddContactIfMatched(input, input_words, *it);
121 } 143 }
122 144
123 void ContactProvider::OnContactsUpdated(Profile* profile) { 145 void ContactProvider::OnContactsUpdated(Profile* profile) {
124 DCHECK_EQ(profile, profile_); 146 DCHECK_EQ(profile, profile_);
125 RefreshContacts(); 147 RefreshContacts();
126 } 148 }
127 149
128 ContactProvider::~ContactProvider() { 150 ContactProvider::~ContactProvider() {
129 // Like ContactProvider, ContactManager gets destroyed at profile destruction. 151 // Like ContactProvider, ContactManager gets destroyed at profile destruction.
130 // Make sure that this class doesn't try to access ContactManager after 152 // Make sure that this class doesn't try to access ContactManager after
131 // ContactManager is gone. 153 // ContactManager is gone.
132 if (contact_manager_.get()) 154 if (contact_manager_.get())
133 contact_manager_->RemoveObserver(this, profile_); 155 contact_manager_->RemoveObserver(this, profile_);
134 } 156 }
135 157
136 void ContactProvider::RefreshContacts() { 158 void ContactProvider::RefreshContacts() {
137 if (!contact_manager_.get()) 159 if (!contact_manager_.get())
138 return; 160 return;
139 161
140 scoped_ptr<contacts::ContactPointers> contacts = 162 scoped_ptr<contacts::ContactPointers> contacts =
141 contact_manager_->GetAllContacts(profile_); 163 contact_manager_->GetAllContacts(profile_);
164 std::sort(contacts->begin(), contacts->end(), CompareContactAffinity);
Peter Kasting 2012/09/13 23:32:37 Nit: If we move this down to the bottom of the fun
Daniel Erat 2012/09/14 15:31:50 I went back and forth on this. Sorting pointers s
142 165
143 contacts_.clear(); 166 contacts_.clear();
144 for (contacts::ContactPointers::const_iterator it = contacts->begin(); 167 for (contacts::ContactPointers::const_iterator it = contacts->begin();
145 it != contacts->end(); ++it) { 168 it != contacts->end(); ++it) {
146 const contacts::Contact& contact = **it; 169 const contacts::Contact& contact = **it;
147 string16 full_name = 170 string16 full_name =
148 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name())); 171 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name()));
149 string16 given_name = 172 string16 given_name =
150 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name())); 173 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name()));
151 string16 family_name = 174 string16 family_name =
152 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name())); 175 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name()));
176 float affinity =
177 contact.has_affinity() ? contact.affinity() : kDefaultAffinity;
153 178
154 if (!full_name.empty()) { 179 if (!full_name.empty()) {
155 contacts_.push_back( 180 contacts_.push_back(
156 ContactData( 181 ContactData(full_name, given_name, family_name, contact.contact_id(),
157 full_name, given_name, family_name, contact.contact_id())); 182 affinity));
158 } 183 }
159 } 184 }
160 } 185 }
161 186
162 void ContactProvider::AddContactIfMatched( 187 void ContactProvider::AddContactIfMatched(
163 const AutocompleteInput& input, 188 const AutocompleteInput& input,
164 const std::vector<string16>& input_words, 189 const std::vector<string16>& input_words,
165 const ContactData& contact) { 190 const ContactData& contact) {
166 // First, check if the whole input string is a prefix of the full name. 191 // First, check if the whole input string is a prefix of the full name.
167 // TODO(derat): Consider additionally segmenting the full name so we can match 192 // TODO(derat): Consider additionally segmenting the full name so we can match
(...skipping 25 matching lines...) Expand all
193 matches_.back().contents_class = classifications; 218 matches_.back().contents_class = classifications;
194 } 219 }
195 220
196 AutocompleteMatch ContactProvider::CreateAutocompleteMatch( 221 AutocompleteMatch ContactProvider::CreateAutocompleteMatch(
197 const AutocompleteInput& input, 222 const AutocompleteInput& input,
198 const ContactData& contact) { 223 const ContactData& contact) {
199 AutocompleteMatch match(this, 0, false, AutocompleteMatch::CONTACT); 224 AutocompleteMatch match(this, 0, false, AutocompleteMatch::CONTACT);
200 match.inline_autocomplete_offset = string16::npos; 225 match.inline_autocomplete_offset = string16::npos;
201 match.contents = contact.full_name; 226 match.contents = contact.full_name;
202 match.fill_into_edit = match.contents; 227 match.fill_into_edit = match.contents;
203 // TODO(derat): Implement ranking. 228 match.relevance = kBaseRelevance +
204 match.relevance = 1; 229 static_cast<int>(roundf(kAffinityRelevanceBoost * contact.affinity));
205 match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id); 230 match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id);
206 return match; 231 return match;
207 } 232 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698