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

Side by Side Diff: components/drive/search_metadata.cc

Issue 1321553003: Files.app: split query into AND conditioned keywords in metadata search. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add test case for empty query. Created 5 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
« no previous file with comments | « components/drive/search_metadata.h ('k') | components/drive/search_metadata_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 (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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 "components/drive/search_metadata.h" 5 #include "components/drive/search_metadata.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <queue> 8 #include <queue>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/i18n/string_search.h" 11 #include "base/i18n/string_search.h"
12 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h" 16 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h" 17 #include "base/time/time.h"
15 #include "components/drive/drive_api_util.h" 18 #include "components/drive/drive_api_util.h"
16 #include "components/drive/file_system_core_util.h" 19 #include "components/drive/file_system_core_util.h"
17 #include "net/base/escape.h" 20 #include "net/base/escape.h"
18 21
19 namespace drive { 22 namespace drive {
20 namespace internal { 23 namespace internal {
21 24
22 namespace { 25 namespace {
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 137
135 private: 138 private:
136 ResourceMetadata* metadata_; 139 ResourceMetadata* metadata_;
137 140
138 // local ID to is_hidden map. 141 // local ID to is_hidden map.
139 std::map<std::string, bool> is_hiding_child_; 142 std::map<std::string, bool> is_hiding_child_;
140 }; 143 };
141 144
142 // Used to implement SearchMetadata. 145 // Used to implement SearchMetadata.
143 // Adds entry to the result when appropriate. 146 // Adds entry to the result when appropriate.
144 // In particular, if |query| is non-null, only adds files with the name matching 147 // In particular, if size of |queries| is larger than 0, only adds files with
145 // the query. 148 // the name matching the query.
146 FileError MaybeAddEntryToResult( 149 FileError MaybeAddEntryToResult(
147 ResourceMetadata* resource_metadata, 150 ResourceMetadata* resource_metadata,
148 ResourceMetadata::Iterator* it, 151 ResourceMetadata::Iterator* it,
149 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents* query, 152 const ScopedVector<
153 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents>& queries,
150 const SearchMetadataPredicate& predicate, 154 const SearchMetadataPredicate& predicate,
151 size_t at_most_num_matches, 155 size_t at_most_num_matches,
152 HiddenEntryClassifier* hidden_entry_classifier, 156 HiddenEntryClassifier* hidden_entry_classifier,
153 ScopedPriorityQueue<ResultCandidate, ResultCandidateComparator>* 157 ScopedPriorityQueue<ResultCandidate, ResultCandidateComparator>*
154 result_candidates) { 158 result_candidates) {
155 DCHECK_GE(at_most_num_matches, result_candidates->size()); 159 DCHECK_GE(at_most_num_matches, result_candidates->size());
156 160
157 const ResourceEntry& entry = it->GetValue(); 161 const ResourceEntry& entry = it->GetValue();
158 162
159 // If the candidate set is already full, and this |entry| is old, do nothing. 163 // If the candidate set is already full, and this |entry| is old, do nothing.
160 // We perform this check first in order to avoid the costly find-and-highlight 164 // We perform this check first in order to avoid the costly find-and-highlight
161 // or FilePath lookup as much as possible. 165 // or FilePath lookup as much as possible.
162 if (result_candidates->size() == at_most_num_matches && 166 if (result_candidates->size() == at_most_num_matches &&
163 !CompareByTimestamp(entry, result_candidates->top()->entry)) 167 !CompareByTimestamp(entry, result_candidates->top()->entry))
164 return FILE_ERROR_OK; 168 return FILE_ERROR_OK;
165 169
166 // Add |entry| to the result if the entry is eligible for the given 170 // Add |entry| to the result if the entry is eligible for the given
167 // |options| and matches the query. The base name of the entry must 171 // |options| and matches the query. The base name of the entry must
168 // contain |query| to match the query. 172 // contain |query| to match the query.
169 std::string highlighted; 173 std::string highlighted;
170 if (!predicate.Run(entry) || 174 if (!predicate.Run(entry) ||
171 (query && !FindAndHighlight(entry.base_name(), query, &highlighted))) 175 (queries.size() > 0 &&
hashimoto 2015/09/08 10:39:05 nit: Since queries' emptiness is checked in FindAn
yawano 2015/09/08 11:19:08 Simply removing queries.size() > 0 has not worked.
176 !FindAndHighlight(entry.base_name(), queries, &highlighted)))
172 return FILE_ERROR_OK; 177 return FILE_ERROR_OK;
173 178
174 // Hidden entry should not be returned. 179 // Hidden entry should not be returned.
175 bool hidden = false; 180 bool hidden = false;
176 FileError error = hidden_entry_classifier->IsHidden(entry, &hidden); 181 FileError error = hidden_entry_classifier->IsHidden(entry, &hidden);
177 if (error != FILE_ERROR_OK || hidden) 182 if (error != FILE_ERROR_OK || hidden)
178 return error; 183 return error;
179 184
180 // Make space for |entry| when appropriate. 185 // Make space for |entry| when appropriate.
181 if (result_candidates->size() == at_most_num_matches) 186 if (result_candidates->size() == at_most_num_matches)
182 result_candidates->pop(); 187 result_candidates->pop();
183 result_candidates->push(new ResultCandidate(it->GetID(), entry, highlighted)); 188 result_candidates->push(new ResultCandidate(it->GetID(), entry, highlighted));
184 return FILE_ERROR_OK; 189 return FILE_ERROR_OK;
185 } 190 }
186 191
187 // Implements SearchMetadata(). 192 // Implements SearchMetadata().
188 FileError SearchMetadataOnBlockingPool(ResourceMetadata* resource_metadata, 193 FileError SearchMetadataOnBlockingPool(ResourceMetadata* resource_metadata,
189 const std::string& query_text, 194 const std::string& query_text,
190 const SearchMetadataPredicate& predicate, 195 const SearchMetadataPredicate& predicate,
191 int at_most_num_matches, 196 int at_most_num_matches,
192 MetadataSearchResultVector* results) { 197 MetadataSearchResultVector* results) {
193 ScopedPriorityQueue<ResultCandidate, 198 ScopedPriorityQueue<ResultCandidate,
194 ResultCandidateComparator> result_candidates; 199 ResultCandidateComparator> result_candidates;
195 200
196 // Prepare data structure for searching. 201 // Prepare data structure for searching.
197 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query( 202 std::vector<base::string16> keywords =
198 base::UTF8ToUTF16(query_text)); 203 base::SplitString(base::UTF8ToUTF16(query_text),
204 base::StringPiece16(base::kWhitespaceUTF16),
205 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
206
207 ScopedVector<base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents>
208 queries;
209 for (const auto& keyword : keywords) {
210 queries.push_back(
211 new base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents(
212 keyword));
213 }
199 214
200 // Prepare an object to filter out hidden entries. 215 // Prepare an object to filter out hidden entries.
201 ResourceEntry mydrive; 216 ResourceEntry mydrive;
202 FileError error = resource_metadata->GetResourceEntryByPath( 217 FileError error = resource_metadata->GetResourceEntryByPath(
203 util::GetDriveMyDriveRootPath(), &mydrive); 218 util::GetDriveMyDriveRootPath(), &mydrive);
204 if (error != FILE_ERROR_OK) 219 if (error != FILE_ERROR_OK)
205 return error; 220 return error;
206 HiddenEntryClassifier hidden_entry_classifier(resource_metadata, 221 HiddenEntryClassifier hidden_entry_classifier(resource_metadata,
207 mydrive.local_id()); 222 mydrive.local_id());
208 223
209 // Iterate over entries. 224 // Iterate over entries.
210 scoped_ptr<ResourceMetadata::Iterator> it = resource_metadata->GetIterator(); 225 scoped_ptr<ResourceMetadata::Iterator> it = resource_metadata->GetIterator();
211 for (; !it->IsAtEnd(); it->Advance()) { 226 for (; !it->IsAtEnd(); it->Advance()) {
212 FileError error = MaybeAddEntryToResult( 227 FileError error = MaybeAddEntryToResult(
213 resource_metadata, it.get(), query_text.empty() ? NULL : &query, 228 resource_metadata, it.get(), queries, predicate, at_most_num_matches,
214 predicate, at_most_num_matches, &hidden_entry_classifier, 229 &hidden_entry_classifier, &result_candidates);
215 &result_candidates);
216 if (error != FILE_ERROR_OK) 230 if (error != FILE_ERROR_OK)
217 return error; 231 return error;
218 } 232 }
219 233
220 // Prepare the result. 234 // Prepare the result.
221 for (; !result_candidates.empty(); result_candidates.pop()) { 235 for (; !result_candidates.empty(); result_candidates.pop()) {
222 const ResultCandidate& candidate = *result_candidates.top(); 236 const ResultCandidate& candidate = *result_candidates.top();
223 // The path field of entries in result_candidates are empty at this point, 237 // The path field of entries in result_candidates are empty at this point,
224 // because we don't want to run the expensive metadata DB look up except for 238 // because we don't want to run the expensive metadata DB look up except for
225 // the final results. Hence, here we fill the part. 239 // the final results. Hence, here we fill the part.
(...skipping 20 matching lines...) Expand all
246 scoped_ptr<MetadataSearchResultVector> results, 260 scoped_ptr<MetadataSearchResultVector> results,
247 FileError error) { 261 FileError error) {
248 if (error != FILE_ERROR_OK) 262 if (error != FILE_ERROR_OK)
249 results.reset(); 263 results.reset();
250 callback.Run(error, results.Pass()); 264 callback.Run(error, results.Pass());
251 265
252 UMA_HISTOGRAM_TIMES("Drive.SearchMetadataTime", 266 UMA_HISTOGRAM_TIMES("Drive.SearchMetadataTime",
253 base::TimeTicks::Now() - start_time); 267 base::TimeTicks::Now() - start_time);
254 } 268 }
255 269
270 // Appends substring of |original_text| to |highlighted_text| with highlight.
271 void AppendStringWithHighlight(const base::string16& original_text,
272 size_t start,
273 size_t length,
274 bool highlight,
275 std::string* highlighted_text) {
276 if (highlight)
277 highlighted_text->append("<b>");
278
279 highlighted_text->append(net::EscapeForHTML(
280 base::UTF16ToUTF8(original_text.substr(start, length))));
281
282 if (highlight)
283 highlighted_text->append("</b>");
284 }
285
256 } // namespace 286 } // namespace
257 287
258 void SearchMetadata( 288 void SearchMetadata(
259 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, 289 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
260 ResourceMetadata* resource_metadata, 290 ResourceMetadata* resource_metadata,
261 const std::string& query, 291 const std::string& query,
262 const SearchMetadataPredicate& predicate, 292 const SearchMetadataPredicate& predicate,
263 size_t at_most_num_matches, 293 size_t at_most_num_matches,
264 const SearchMetadataCallback& callback) { 294 const SearchMetadataCallback& callback) {
265 DCHECK(!callback.is_null()); 295 DCHECK(!callback.is_null());
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 } else { 331 } else {
302 return entry.file_specific_info().cache_state().is_present(); 332 return entry.file_specific_info().cache_state().is_present();
303 } 333 }
304 } 334 }
305 335
306 return true; 336 return true;
307 } 337 }
308 338
309 bool FindAndHighlight( 339 bool FindAndHighlight(
310 const std::string& text, 340 const std::string& text,
311 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents* query, 341 const ScopedVector<
342 base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents>& queries,
312 std::string* highlighted_text) { 343 std::string* highlighted_text) {
313 DCHECK(query);
314 DCHECK(highlighted_text); 344 DCHECK(highlighted_text);
315 highlighted_text->clear(); 345 highlighted_text->clear();
316 346
317 base::string16 text16 = base::UTF8ToUTF16(text); 347 // Empty queries do not match with anything.
348 if (queries.size() == 0)
hashimoto 2015/09/08 10:39:05 nit: empty() should be more common in our codebase
yawano 2015/09/08 11:19:08 Acknowledged.
349 return false;
350
351 // Check text matches with all queries.
318 size_t match_start = 0; 352 size_t match_start = 0;
319 size_t match_length = 0; 353 size_t match_length = 0;
320 if (!query->Search(text16, &match_start, &match_length))
321 return false;
322 354
323 base::string16 pre = text16.substr(0, match_start); 355 base::string16 text16 = base::UTF8ToUTF16(text);
324 base::string16 match = text16.substr(match_start, match_length); 356 std::vector<bool> highlights(text16.size(), false);
325 base::string16 post = text16.substr(match_start + match_length); 357 for (auto* query : queries) {
326 highlighted_text->append(net::EscapeForHTML(base::UTF16ToUTF8(pre))); 358 if (!query->Search(text16, &match_start, &match_length))
327 highlighted_text->append("<b>"); 359 return false;
328 highlighted_text->append(net::EscapeForHTML(base::UTF16ToUTF8(match))); 360
329 highlighted_text->append("</b>"); 361 std::fill(highlights.begin() + match_start,
330 highlighted_text->append(net::EscapeForHTML(base::UTF16ToUTF8(post))); 362 highlights.begin() + match_start + match_length, true);
363 }
364
365 // Generate highlighted text.
366 size_t start_current_segment = 0;
367
368 for (size_t i = 0; i < text16.size(); ++i) {
369 if (highlights[start_current_segment] == highlights[i])
370 continue;
371
372 AppendStringWithHighlight(
373 text16, start_current_segment, i - start_current_segment,
374 highlights[start_current_segment], highlighted_text);
375
376 start_current_segment = i;
377 }
378
379 DCHECK_GE(text16.size(), start_current_segment);
380 AppendStringWithHighlight(
381 text16, start_current_segment, text16.size() - start_current_segment,
382 highlights[start_current_segment], highlighted_text);
383
331 return true; 384 return true;
332 } 385 }
333 386
334 } // namespace internal 387 } // namespace internal
335 } // namespace drive 388 } // namespace drive
OLDNEW
« no previous file with comments | « components/drive/search_metadata.h ('k') | components/drive/search_metadata_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698