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

Side by Side Diff: chrome/browser/history/url_index_private_data.cc

Issue 10541045: Move ScoredHistoryMatch into Its Own Set of Files (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 6 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
« no previous file with comments | « chrome/browser/history/url_index_private_data.h ('k') | chrome/chrome_browser.gypi » ('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) 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/history/url_index_private_data.h" 5 #include "chrome/browser/history/url_index_private_data.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <functional> 8 #include <functional>
9 #include <iterator> 9 #include <iterator>
10 #include <limits> 10 #include <limits>
11 #include <numeric> 11 #include <numeric>
12 #include <vector> 12 #include <vector>
13 13
14 #include <math.h>
15
16 #include "base/basictypes.h" 14 #include "base/basictypes.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h" 15 #include "base/file_util.h"
19 #include "base/i18n/case_conversion.h" 16 #include "base/i18n/case_conversion.h"
20 #include "base/metrics/histogram.h" 17 #include "base/metrics/histogram.h"
21 #include "base/string_util.h" 18 #include "base/string_util.h"
22 #include "base/time.h" 19 #include "base/time.h"
23 #include "base/utf_string_conversions.h" 20 #include "base/utf_string_conversions.h"
24 #include "chrome/browser/autocomplete/autocomplete.h" 21 #include "chrome/browser/autocomplete/autocomplete.h"
25 #include "chrome/browser/autocomplete/url_prefix.h" 22 #include "chrome/browser/autocomplete/url_prefix.h"
26 #include "chrome/browser/history/history_database.h" 23 #include "chrome/browser/history/history_database.h"
27 #include "chrome/browser/history/in_memory_url_index.h" 24 #include "chrome/browser/history/in_memory_url_index.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "content/public/browser/browser_thread.h" 25 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_details.h" 26 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h" 28 #include "content/public/browser/notification_source.h"
33 #include "net/base/net_util.h" 29 #include "net/base/net_util.h"
34 #include "third_party/protobuf/src/google/protobuf/repeated_field.h" 30 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
35 31
36 using google::protobuf::RepeatedField; 32 using google::protobuf::RepeatedField;
37 using google::protobuf::RepeatedPtrField; 33 using google::protobuf::RepeatedPtrField;
38 using in_memory_url_index::InMemoryURLIndexCacheItem; 34 using in_memory_url_index::InMemoryURLIndexCacheItem;
(...skipping 11 matching lines...) Expand all
50 typedef imui:: 46 typedef imui::
51 InMemoryURLIndexCacheItem_WordIDHistoryMapItem_WordIDHistoryMapEntry 47 InMemoryURLIndexCacheItem_WordIDHistoryMapItem_WordIDHistoryMapEntry
52 WordIDHistoryMapEntry; 48 WordIDHistoryMapEntry;
53 typedef imui::InMemoryURLIndexCacheItem_HistoryInfoMapItem HistoryInfoMapItem; 49 typedef imui::InMemoryURLIndexCacheItem_HistoryInfoMapItem HistoryInfoMapItem;
54 typedef imui::InMemoryURLIndexCacheItem_HistoryInfoMapItem_HistoryInfoMapEntry 50 typedef imui::InMemoryURLIndexCacheItem_HistoryInfoMapItem_HistoryInfoMapEntry
55 HistoryInfoMapEntry; 51 HistoryInfoMapEntry;
56 typedef imui::InMemoryURLIndexCacheItem_WordStartsMapItem WordStartsMapItem; 52 typedef imui::InMemoryURLIndexCacheItem_WordStartsMapItem WordStartsMapItem;
57 typedef imui::InMemoryURLIndexCacheItem_WordStartsMapItem_WordStartsMapEntry 53 typedef imui::InMemoryURLIndexCacheItem_WordStartsMapItem_WordStartsMapEntry
58 WordStartsMapEntry; 54 WordStartsMapEntry;
59 55
60 // The maximum score any candidate result can achieve.
61 const int kMaxTotalScore = 1425;
62
63 // Score ranges used to get a 'base' score for each of the scoring factors
64 // (such as recency of last visit, times visited, times the URL was typed,
65 // and the quality of the string match). There is a matching value range for
66 // each of these scores for each factor. Note that the top score is greater
67 // than |kMaxTotalScore|. The score for each candidate will be capped in the
68 // final calculation.
69 const int kScoreRank[] = { 1450, 1200, 900, 400 };
70
71 // SearchTermCacheItem --------------------------------------------------------- 56 // SearchTermCacheItem ---------------------------------------------------------
72 57
73 URLIndexPrivateData::SearchTermCacheItem::SearchTermCacheItem( 58 URLIndexPrivateData::SearchTermCacheItem::SearchTermCacheItem(
74 const WordIDSet& word_id_set, 59 const WordIDSet& word_id_set,
75 const HistoryIDSet& history_id_set) 60 const HistoryIDSet& history_id_set)
76 : word_id_set_(word_id_set), 61 : word_id_set_(word_id_set),
77 history_id_set_(history_id_set), 62 history_id_set_(history_id_set),
78 used_(true) {} 63 used_(true) {}
79 64
80 URLIndexPrivateData::SearchTermCacheItem::SearchTermCacheItem() 65 URLIndexPrivateData::SearchTermCacheItem::SearchTermCacheItem()
81 : used_(true) {} 66 : used_(true) {}
82 67
83 URLIndexPrivateData::SearchTermCacheItem::~SearchTermCacheItem() {} 68 URLIndexPrivateData::SearchTermCacheItem::~SearchTermCacheItem() {}
84 69
85 // Algorithm Functions --------------------------------------------------------- 70 // Algorithm Functions ---------------------------------------------------------
86 71
87 // Comparison function for sorting search terms by descending length. 72 // Comparison function for sorting search terms by descending length.
88 bool LengthGreater(const string16& string_a, const string16& string_b) { 73 bool LengthGreater(const string16& string_a, const string16& string_b) {
89 return string_a.length() > string_b.length(); 74 return string_a.length() > string_b.length();
90 } 75 }
91 76
92 // std::accumulate helper function to add up TermMatches' lengths.
93 int AccumulateMatchLength(int total, const TermMatch& match) {
94 return total + match.length;
95 }
96
97 // Converts a raw value for some particular scoring factor into a score
98 // component for that factor. The conversion function is piecewise linear, with
99 // input values provided in |value_ranks| and resulting output scores from
100 // |kScoreRank| (mathematically, f(value_rank[i]) = kScoreRank[i]). A score
101 // cannot be higher than kScoreRank[0], and drops directly to 0 if lower than
102 // kScoreRank[3].
103 //
104 // For example, take |value| == 70 and |value_ranks| == { 100, 50, 30, 10 }.
105 // Because 70 falls between ranks 0 (100) and 1 (50), the score is given by the
106 // linear function:
107 // score = m * value + b, where
108 // m = (kScoreRank[0] - kScoreRank[1]) / (value_ranks[0] - value_ranks[1])
109 // b = value_ranks[1]
110 // Any value higher than 100 would be scored as if it were 100, and any value
111 // lower than 10 scored 0.
112 int ScoreForValue(int value, const int* value_ranks) {
113 int i = 0;
114 int rank_count = arraysize(kScoreRank);
115 while ((i < rank_count) && ((value_ranks[0] < value_ranks[1]) ?
116 (value > value_ranks[i]) : (value < value_ranks[i])))
117 ++i;
118 if (i >= rank_count)
119 return 0;
120 int score = kScoreRank[i];
121 if (i > 0) {
122 score += (value - value_ranks[i]) *
123 (kScoreRank[i - 1] - kScoreRank[i]) /
124 (value_ranks[i - 1] - value_ranks[i]);
125 }
126 return score;
127 }
128
129 // InMemoryURLIndex's Private Data --------------------------------------------- 77 // InMemoryURLIndex's Private Data ---------------------------------------------
130 78
131 URLIndexPrivateData::URLIndexPrivateData() 79 URLIndexPrivateData::URLIndexPrivateData()
132 : restored_cache_version_(0), 80 : restored_cache_version_(0),
133 use_new_scoring_(false),
134 saved_cache_version_(kCurrentCacheFileVersion), 81 saved_cache_version_(kCurrentCacheFileVersion),
135 pre_filter_item_count_(0), 82 pre_filter_item_count_(0),
136 post_filter_item_count_(0), 83 post_filter_item_count_(0),
137 post_scoring_item_count_(0) { 84 post_scoring_item_count_(0) {
138 const std::string switch_value = CommandLine::ForCurrentProcess()->
139 GetSwitchValueASCII(switches::kOmniboxHistoryQuickProviderNewScoring);
140 if (switch_value == switches::kOmniboxHistoryQuickProviderNewScoringEnabled)
141 use_new_scoring_ = true;
142 } 85 }
143 86
144 URLIndexPrivateData::~URLIndexPrivateData() {} 87 URLIndexPrivateData::~URLIndexPrivateData() {}
145 88
146 void URLIndexPrivateData::Clear() { 89 void URLIndexPrivateData::Clear() {
147 word_list_.clear(); 90 word_list_.clear();
148 available_words_.clear(); 91 available_words_.clear();
149 word_map_.clear(); 92 word_map_.clear();
150 char_word_map_.clear(); 93 char_word_map_.clear();
151 word_id_history_map_.clear(); 94 word_id_history_map_.clear();
(...skipping 428 matching lines...) Expand 10 before | Expand all | Expand 10 after
580 lower_string_(lower_string), 523 lower_string_(lower_string),
581 lower_terms_(lower_terms), 524 lower_terms_(lower_terms),
582 now_(now) {} 525 now_(now) {}
583 526
584 URLIndexPrivateData::AddHistoryMatch::~AddHistoryMatch() {} 527 URLIndexPrivateData::AddHistoryMatch::~AddHistoryMatch() {}
585 528
586 void URLIndexPrivateData::AddHistoryMatch::operator()( 529 void URLIndexPrivateData::AddHistoryMatch::operator()(
587 const HistoryID history_id) { 530 const HistoryID history_id) {
588 HistoryInfoMap::const_iterator hist_pos = 531 HistoryInfoMap::const_iterator hist_pos =
589 private_data_.history_info_map_.find(history_id); 532 private_data_.history_info_map_.find(history_id);
590 // Note that a history_id may be present in the word_id_history_map_ yet not
591 // be found in the history_info_map_. This occurs when an item has been
592 // deleted by the user or the item no longer qualifies as a quick result.
593 if (hist_pos != private_data_.history_info_map_.end()) { 533 if (hist_pos != private_data_.history_info_map_.end()) {
594 const URLRow& hist_item = hist_pos->second; 534 const URLRow& hist_item = hist_pos->second;
595 WordStartsMap::const_iterator starts_pos = 535 WordStartsMap::const_iterator starts_pos =
596 private_data_.word_starts_map_.find(history_id); 536 private_data_.word_starts_map_.find(history_id);
597 DCHECK(starts_pos != private_data_.word_starts_map_.end()); 537 DCHECK(starts_pos != private_data_.word_starts_map_.end());
598 ScoredHistoryMatch match(private_data_.ScoredMatchForURL( 538 ScoredHistoryMatch match(hist_item, lower_string_, lower_terms_,
599 hist_item, lower_string_, lower_terms_, starts_pos->second, now_)); 539 starts_pos->second, now_);
600 if (match.raw_score > 0) 540 if (match.raw_score > 0)
601 scored_matches_.push_back(match); 541 scored_matches_.push_back(match);
602 } 542 }
603 } 543 }
604 544
605 // TODO(mrossetti): This can be made a ctor for ScoredHistoryMatch.
606 ScoredHistoryMatch URLIndexPrivateData::ScoredMatchForURL(
607 const URLRow& row,
608 const string16& lower_string,
609 const String16Vector& terms,
610 const RowWordStarts& word_starts,
611 const base::Time now) const {
612 ScoredHistoryMatch match(row);
613 GURL gurl = row.url();
614 if (!gurl.is_valid())
615 return match;
616
617 // Figure out where each search term appears in the URL and/or page title
618 // so that we can score as well as provide autocomplete highlighting.
619 string16 url = base::i18n::ToLower(UTF8ToUTF16(gurl.spec()));
620 string16 title = base::i18n::ToLower(row.title());
621 int term_num = 0;
622 for (String16Vector::const_iterator iter = terms.begin(); iter != terms.end();
623 ++iter, ++term_num) {
624 string16 term = *iter;
625 TermMatches url_term_matches = MatchTermInString(term, url, term_num);
626 TermMatches title_term_matches = MatchTermInString(term, title, term_num);
627 if (url_term_matches.empty() && title_term_matches.empty())
628 return match; // A term was not found in either URL or title - reject.
629 match.url_matches.insert(match.url_matches.end(), url_term_matches.begin(),
630 url_term_matches.end());
631 match.title_matches.insert(match.title_matches.end(),
632 title_term_matches.begin(),
633 title_term_matches.end());
634 }
635
636 // Sort matches by offset and eliminate any which overlap.
637 // TODO(mpearson): Investigate whether this has any meaningful
638 // effect on scoring. (It's necessary at some point: removing
639 // overlaps and sorting is needed to decide what to highlight in the
640 // suggestion string. But this sort and de-overlap doesn't have to
641 // be done before scoring.)
642 match.url_matches = SortAndDeoverlapMatches(match.url_matches);
643 match.title_matches = SortAndDeoverlapMatches(match.title_matches);
644
645 // We can inline autocomplete a result if:
646 // 1) there is only one search term
647 // 2) AND EITHER:
648 // 2a) the first match starts at the beginning of the candidate URL, OR
649 // 2b) the candidate URL starts with one of the standard URL prefixes with
650 // the URL match immediately following that prefix.
651 // 3) AND the search string does not end in whitespace (making it look to
652 // the IMUI as though there is a single search term when actually there
653 // is a second, empty term).
654 match.can_inline = !match.url_matches.empty() && terms.size() == 1 &&
655 (match.url_matches[0].offset == 0 ||
656 URLPrefix::IsURLPrefix(url.substr(0, match.url_matches[0].offset))) &&
657 !IsWhitespace(*(lower_string.rbegin()));
658 match.match_in_scheme = match.can_inline && match.url_matches[0].offset == 0;
659
660 if (use_new_scoring_) {
661 const float topicality_score = GetTopicalityScore(
662 terms.size(), url, match.url_matches, match.title_matches, word_starts);
663 const float recency_score = GetRecencyScore(
664 (now - row.last_visit()).InDays());
665 const float popularity_score = GetPopularityScore(
666 row.typed_count(), row.visit_count());
667
668 // Combine recency, popularity, and topicality scores into one.
669 // Example of how this functions: Suppose the omnibox has one
670 // input term. Suppose we have a URL that has 4 typed visits with
671 // the most recent being within a day and the omnibox input term
672 // has a single URL hostname hit at a word boundary. Then this
673 // URL will score 1400 ( = 4 * 350), which is exactly the value of
674 // search what you type. That is, it's the boundary of what might
675 // end up being inlined.
676 const float raw_score =
677 350 * topicality_score * recency_score * popularity_score;
678 match.raw_score =
679 (raw_score <= kint32max) ? static_cast<int>(raw_score) : kint32max;
680 } else { // "old" scoring
681 // Get partial scores based on term matching. Note that the score for
682 // each of the URL and title are adjusted by the fraction of the
683 // terms appearing in each.
684 int url_score = ScoreComponentForMatches(match.url_matches, url.length()) *
685 std::min(match.url_matches.size(), terms.size()) / terms.size();
686 int title_score =
687 ScoreComponentForMatches(match.title_matches, title.length()) *
688 std::min(match.title_matches.size(), terms.size()) / terms.size();
689 // Arbitrarily pick the best.
690 // TODO(mrossetti): It might make sense that a term which appears
691 // in both the URL and the Title should boost the score a bit.
692 int term_score = std::max(url_score, title_score);
693 if (term_score == 0)
694 return match;
695
696 // Determine scoring factors for the recency of visit, visit count
697 // and typed count attributes of the URLRow.
698 const int kDaysAgoLevel[] = { 1, 10, 20, 30 };
699 int days_ago_value = ScoreForValue(
700 (now - row.last_visit()).InDays(), kDaysAgoLevel);
701 const int kVisitCountLevel[] = { 50, 30, 10, 5 };
702 int visit_count_value = ScoreForValue(row.visit_count(), kVisitCountLevel);
703 const int kTypedCountLevel[] = { 50, 30, 10, 5 };
704 int typed_count_value = ScoreForValue(row.typed_count(), kTypedCountLevel);
705
706 // The final raw score is calculated by:
707 // - multiplying each factor by a 'relevance'
708 // - calculating the average.
709 // Note that visit_count is reduced by typed_count because both are bumped
710 // when a typed URL is recorded thus giving visit_count too much weight.
711 const int kTermScoreRelevance = 4;
712 const int kDaysAgoRelevance = 2;
713 const int kVisitCountRelevance = 2;
714 const int kTypedCountRelevance = 5;
715 int effective_visit_count_value =
716 std::max(0, visit_count_value - typed_count_value);
717 match.raw_score = term_score * kTermScoreRelevance +
718 days_ago_value * kDaysAgoRelevance +
719 effective_visit_count_value * kVisitCountRelevance +
720 typed_count_value * kTypedCountRelevance;
721 match.raw_score /= (kTermScoreRelevance + kDaysAgoRelevance +
722 kVisitCountRelevance + kTypedCountRelevance);
723 match.raw_score = std::min(kMaxTotalScore, match.raw_score);
724 }
725 return match;
726 }
727
728 int URLIndexPrivateData::ScoreComponentForMatches(const TermMatches& matches,
729 size_t max_length) {
730 if (matches.empty())
731 return 0;
732
733 // Score component for whether the input terms (if more than one) were found
734 // in the same order in the match. Start with kOrderMaxValue points divided
735 // equally among (number of terms - 1); then discount each of those terms that
736 // is out-of-order in the match.
737 const int kOrderMaxValue = 1000;
738 int order_value = kOrderMaxValue;
739 if (matches.size() > 1) {
740 int max_possible_out_of_order = matches.size() - 1;
741 int out_of_order = 0;
742 for (size_t i = 1; i < matches.size(); ++i) {
743 if (matches[i - 1].term_num > matches[i].term_num)
744 ++out_of_order;
745 }
746 order_value = (max_possible_out_of_order - out_of_order) * kOrderMaxValue /
747 max_possible_out_of_order;
748 }
749
750 // Score component for how early in the match string the first search term
751 // appears. Start with kStartMaxValue points and discount by
752 // kStartMaxValue/kMaxSignificantChars points for each character later than
753 // the first at which the term begins. No points are earned if the start of
754 // the match occurs at or after kMaxSignificantChars.
755 const int kStartMaxValue = 1000;
756 int start_value = (kMaxSignificantChars -
757 std::min(kMaxSignificantChars, matches[0].offset)) * kStartMaxValue /
758 kMaxSignificantChars;
759
760 // Score component for how much of the matched string the input terms cover.
761 // kCompleteMaxValue points times the fraction of the URL/page title string
762 // that was matched.
763 size_t term_length_total = std::accumulate(matches.begin(), matches.end(),
764 0, AccumulateMatchLength);
765 const size_t kMaxSignificantLength = 50;
766 size_t max_significant_length =
767 std::min(max_length, std::max(term_length_total, kMaxSignificantLength));
768 const int kCompleteMaxValue = 1000;
769 int complete_value =
770 term_length_total * kCompleteMaxValue / max_significant_length;
771
772 const int kOrderRelevance = 1;
773 const int kStartRelevance = 6;
774 const int kCompleteRelevance = 3;
775 int raw_score = order_value * kOrderRelevance +
776 start_value * kStartRelevance +
777 complete_value * kCompleteRelevance;
778 raw_score /= (kOrderRelevance + kStartRelevance + kCompleteRelevance);
779
780 // Scale the raw score into a single score component in the same manner as
781 // used in ScoredMatchForURL().
782 const int kTermScoreLevel[] = { 1000, 750, 500, 200 };
783 return ScoreForValue(raw_score, kTermScoreLevel);
784 }
785
786 // static
787 float URLIndexPrivateData::GetTopicalityScore(
788 const int num_terms,
789 const string16& url,
790 const TermMatches& url_matches,
791 const TermMatches& title_matches,
792 const RowWordStarts& word_starts) {
793 // Because the below thread is not thread safe, we check that we're
794 // only calling it from one thread: the UI thread. Specifically,
795 // we check "if we've heard of the UI thread then we'd better
796 // be on it." The first part is necessary so unit tests pass. (Many
797 // unit tests don't set up the threading naming system; hence
798 // CurrentlyOn(UI thread) will fail.)
799 DCHECK(
800 !content::BrowserThread::IsWellKnownThread(content::BrowserThread::UI) ||
801 content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
802 if (raw_term_score_to_topicality_score_ == NULL) {
803 raw_term_score_to_topicality_score_ = new float[kMaxRawTermScore];
804 FillInTermScoreToTopicalityScoreArray();
805 }
806 // A vector that accumulates per-term scores. The strongest match--a
807 // match in the hostname at a word boundary--is worth 10 points.
808 // Everything else is less. In general, a match that's not at a word
809 // boundary is worth about 1/4th or 1/5th of a match at the word boundary
810 // in the same part of the URL/title.
811 std::vector<int> term_scores(num_terms, 0);
812 std::vector<size_t>::const_iterator next_word_starts =
813 word_starts.url_word_starts_.begin();
814 std::vector<size_t>::const_iterator end_word_starts =
815 word_starts.url_word_starts_.end();
816 const size_t question_mark_pos = url.find('?');
817 const size_t colon_pos = url.find(':');
818 // The + 3 skips the // that probably appears in the protocol
819 // after the colon. If the protocol doesn't have two slashes after
820 // the colon, that's okay--all this ends up doing is starting our
821 // search for the next / a few characters into the hostname. The
822 // only times this can cause problems is if we have a protocol without
823 // a // after the colon and the hostname is only one or two characters.
824 // This isn't worth worrying about.
825 const size_t end_of_hostname_pos = (colon_pos != std::string::npos) ?
826 url.find('/', colon_pos + 3) : url.find('/');
827 // Loop through all URL matches and score them appropriately.
828 for (TermMatches::const_iterator iter = url_matches.begin();
829 iter != url_matches.end(); ++iter) {
830 // Advance next_word_starts until it's >= the position of the term
831 // we're considering.
832 while ((next_word_starts != end_word_starts) &&
833 (*next_word_starts < iter->offset)) {
834 ++next_word_starts;
835 }
836 const bool at_word_boundary = (next_word_starts != end_word_starts) &&
837 (*next_word_starts == iter->offset);
838 if ((question_mark_pos != std::string::npos) &&
839 (iter->offset > question_mark_pos)) {
840 // match in CGI ?... fragment
841 term_scores[iter->term_num] += at_word_boundary ? 5 : 0;
842 } else if ((end_of_hostname_pos != std::string::npos) &&
843 (iter->offset > end_of_hostname_pos)) {
844 // match in path
845 term_scores[iter->term_num] += at_word_boundary ? 8 : 1;
846 } else if ((colon_pos == std::string::npos) ||
847 (iter->offset > colon_pos)) {
848 // match in hostname
849 term_scores[iter->term_num] += at_word_boundary ? 10 : 2;
850 } // else: match in protocol. Do not count this match for scoring.
851 }
852 // Now do the analogous loop over all matches in the title.
853 next_word_starts = word_starts.title_word_starts_.begin();
854 end_word_starts = word_starts.title_word_starts_.end();
855 int word_num = 0;
856 for (TermMatches::const_iterator iter = title_matches.begin();
857 iter != title_matches.end(); ++iter) {
858 // Advance next_word_starts until it's >= the position of the term
859 // we're considering.
860 while ((next_word_starts != end_word_starts) &&
861 (*next_word_starts < iter->offset)) {
862 ++next_word_starts;
863 ++word_num;
864 }
865 if (word_num >= 10) break; // only count the first ten words
866 const bool at_word_boundary = (next_word_starts != end_word_starts) &&
867 (*next_word_starts == iter->offset);
868 term_scores[iter->term_num] += at_word_boundary ? 8 : 2;
869 }
870 // TODO(mpearson): Restore logic for penalizing out-of-order matches.
871 // (Perhaps discount them by 0.8?)
872 // TODO(mpearson): Consider: if the earliest match occurs late in the string,
873 // should we discount it?
874 // TODO(mpearson): Consider: do we want to score based on how much of the
875 // input string the input covers? (I'm leaning toward no.)
876
877 // Compute the topicality_score as the sum of transformed term_scores.
878 float topicality_score = 0;
879 for (size_t i = 0; i < term_scores.size(); ++i) {
880 topicality_score += raw_term_score_to_topicality_score_[
881 (term_scores[i] >= kMaxRawTermScore)? kMaxRawTermScore - 1:
882 term_scores[i]];
883 }
884 // TODO(mpearson): If there are multiple terms, consider taking the
885 // geometric mean of per-term scores rather than sum as we're doing now
886 // (which is equivalent to the arthimatic mean).
887
888 return topicality_score;
889 }
890
891 // static
892 float* URLIndexPrivateData::raw_term_score_to_topicality_score_ = NULL;
893
894 // static
895 void URLIndexPrivateData::FillInTermScoreToTopicalityScoreArray() {
896 for (int term_score = 0; term_score < kMaxRawTermScore; ++term_score) {
897 float topicality_score;
898 if (term_score < 10) {
899 // If the term scores less than 10 points (no full-credit hit, or
900 // no combination of hits that score that well), then the topicality
901 // score is linear in the term score.
902 topicality_score = 0.1 * term_score;
903 } else {
904 // For term scores of at least ten points, pass them through a log
905 // function so a score of 10 points gets a 1.0 (to meet up exactly
906 // with the linear component) and increases logarithmically until
907 // maxing out at 30 points, with computes to a score around 2.1.
908 topicality_score = (1.0 + 2.25 * log10(0.1 *
909 ((term_score <= 30) ? term_score : 30)));
910 }
911 raw_term_score_to_topicality_score_[term_score] = topicality_score;
912 }
913 }
914
915 // static
916 float* URLIndexPrivateData::days_ago_to_recency_score_ = NULL;
917
918 // static
919 float URLIndexPrivateData::GetRecencyScore(int last_visit_days_ago) {
920 // Because the below thread is not thread safe, we check that we're
921 // only calling it from one thread: the UI thread. Specifically,
922 // we check "if we've heard of the UI thread then we'd better
923 // be on it." The first part is necessary so unit tests pass. (Many
924 // unit tests don't set up the threading naming system; hence
925 // CurrentlyOn(UI thread) will fail.)
926 DCHECK(
927 !content::BrowserThread::IsWellKnownThread(content::BrowserThread::UI) ||
928 content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
929 if (days_ago_to_recency_score_ == NULL) {
930 days_ago_to_recency_score_ = new float[kDaysToPrecomputeRecencyScoresFor];
931 FillInDaysAgoToRecencyScoreArray();
932 }
933 // Lookup the score in days_ago_to_recency_score_, treating
934 // everything older than what we've precomputed as the oldest thing
935 // we've precomputed. The std::max is to protect against corruption
936 // in the database (in case last_visit_days_ago is negative).
937 return days_ago_to_recency_score_[
938 std::max(
939 std::min(last_visit_days_ago, kDaysToPrecomputeRecencyScoresFor - 1),
940 0)];
941 }
942
943 void URLIndexPrivateData::FillInDaysAgoToRecencyScoreArray() {
944 for (int days_ago = 0; days_ago < kDaysToPrecomputeRecencyScoresFor;
945 days_ago++) {
946 int unnormalized_recency_score;
947 if (days_ago <= 1) {
948 unnormalized_recency_score = 100;
949 } else if (days_ago <= 7) {
950 // Linearly extrapolate between 1 and 7 days so 7 days has a score of 70.
951 unnormalized_recency_score = 70 + (7 - days_ago) * (100 - 70) / (7 - 1);
952 } else if (days_ago <= 30) {
953 // Linearly extrapolate between 7 and 30 days so 30 days has a score
954 // of 50.
955 unnormalized_recency_score = 50 + (30 - days_ago) * (70 - 50) / (30 - 7);
956 } else if (days_ago <= 90) {
957 // Linearly extrapolate between 30 and 90 days so 90 days has a score
958 // of 20.
959 unnormalized_recency_score = 20 + (90 - days_ago) * (50 - 20) / (90 - 30);
960 } else if (days_ago <= 365) {
961 // Linearly extrapolate between 90 and 365 days so 365 days has a score
962 // of 10.
963 unnormalized_recency_score =
964 10 + (365 - days_ago) * (20 - 10) / (365 - 90);
965 } else {
966 // greater than a year.
967 unnormalized_recency_score = 10;
968 }
969 days_ago_to_recency_score_[days_ago] = unnormalized_recency_score / 100.0;
970 if (days_ago > 0) {
971 DCHECK_LE(days_ago_to_recency_score_[days_ago],
972 days_ago_to_recency_score_[days_ago - 1]);
973 }
974 }
975 }
976
977 // static
978 float URLIndexPrivateData::GetPopularityScore(int typed_count,
979 int visit_count) {
980 // The max()s are to guard against database corruption.
981 return (std::max(typed_count, 0) * 5.0 + std::max(visit_count, 0) * 3.0) /
982 (5.0 + 3.0);
983 }
984
985 void URLIndexPrivateData::ResetSearchTermCache() { 545 void URLIndexPrivateData::ResetSearchTermCache() {
986 for (SearchTermCacheMap::iterator iter = search_term_cache_.begin(); 546 for (SearchTermCacheMap::iterator iter = search_term_cache_.begin();
987 iter != search_term_cache_.end(); ++iter) 547 iter != search_term_cache_.end(); ++iter)
988 iter->second.used_ = false; 548 iter->second.used_ = false;
989 } 549 }
990 550
991 HistoryIDSet URLIndexPrivateData::HistoryIDSetFromWords( 551 HistoryIDSet URLIndexPrivateData::HistoryIDSetFromWords(
992 const String16Vector& unsorted_words) { 552 const String16Vector& unsorted_words) {
993 // Break the terms down into individual terms (words), get the candidate 553 // Break the terms down into individual terms (words), get the candidate
994 // set for each term, and intersect each to get a final candidate list. 554 // set for each term, and intersect each to get a final candidate list.
(...skipping 588 matching lines...) Expand 10 before | Expand all | Expand 10 after
1583 } 1143 }
1584 1144
1585 // static 1145 // static
1586 bool URLIndexPrivateData::URLSchemeIsWhitelisted( 1146 bool URLIndexPrivateData::URLSchemeIsWhitelisted(
1587 const GURL& gurl, 1147 const GURL& gurl,
1588 const std::set<std::string>& whitelist) { 1148 const std::set<std::string>& whitelist) {
1589 return whitelist.find(gurl.scheme()) != whitelist.end(); 1149 return whitelist.find(gurl.scheme()) != whitelist.end();
1590 } 1150 }
1591 1151
1592 } // namespace history 1152 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/url_index_private_data.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698