ranking_cluster_finalizer.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2021 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.
  4. #include "components/history_clusters/core/ranking_cluster_finalizer.h"
  5. #include "base/containers/adapters.h"
  6. #include "components/history_clusters/core/on_device_clustering_util.h"
  7. namespace history_clusters {
  8. namespace {
  9. // See https://en.wikipedia.org/wiki/Smoothstep.
  10. float clamp(float x, float lowerlimit, float upperlimit) {
  11. if (x < lowerlimit)
  12. x = lowerlimit;
  13. if (x > upperlimit)
  14. x = upperlimit;
  15. return x;
  16. }
  17. // Maps |values| from the range specified by |low| and
  18. // |high| into [0, 1].
  19. // See https://en.wikipedia.org/wiki/Smoothstep.
  20. float Smoothstep(float low, float high, float value) {
  21. DCHECK_NE(low, high);
  22. const float x = clamp((value - low) / (high - low), 0.0, 1.0);
  23. return x * x * (3 - 2 * x);
  24. }
  25. } // namespace
  26. RankingClusterFinalizer::RankingClusterFinalizer() = default;
  27. RankingClusterFinalizer::~RankingClusterFinalizer() = default;
  28. void RankingClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
  29. base::flat_map<history::VisitID, VisitScores> url_visit_scores;
  30. CalculateVisitDurationScores(cluster, url_visit_scores);
  31. CalculateVisitAttributeScoring(cluster, url_visit_scores);
  32. ComputeFinalVisitScores(cluster, url_visit_scores);
  33. }
  34. void RankingClusterFinalizer::CalculateVisitAttributeScoring(
  35. history::Cluster& cluster,
  36. base::flat_map<history::VisitID, VisitScores>& url_visit_scores) {
  37. for (const history::ClusterVisit& visit : base::Reversed(cluster.visits)) {
  38. auto it = url_visit_scores.find(visit.annotated_visit.visit_row.visit_id);
  39. if (it == url_visit_scores.end()) {
  40. auto visit_score = VisitScores();
  41. url_visit_scores.insert(
  42. {visit.annotated_visit.visit_row.visit_id, visit_score});
  43. }
  44. it = url_visit_scores.find(visit.annotated_visit.visit_row.visit_id);
  45. // Check if the visit is bookmarked.
  46. if (visit.annotated_visit.context_annotations.is_existing_bookmark ||
  47. visit.annotated_visit.context_annotations.is_new_bookmark) {
  48. it->second.set_bookmarked();
  49. }
  50. // Check if the visit contained a search query.
  51. if (!visit.annotated_visit.content_annotations.search_terms.empty()) {
  52. it->second.set_is_srp();
  53. }
  54. if (!visit.annotated_visit.url_row.title().empty()) {
  55. it->second.set_has_page_title();
  56. }
  57. // Additional/future attribute checks go here.
  58. }
  59. }
  60. void RankingClusterFinalizer::CalculateVisitDurationScores(
  61. history::Cluster& cluster,
  62. base::flat_map<history::VisitID, VisitScores>& url_visit_scores) {
  63. // |max_visit_duration| and |max_foreground_duration| must be > 0 for
  64. // reshaping between 0 and 1.
  65. base::TimeDelta max_visit_duration = base::Seconds(1);
  66. base::TimeDelta max_foreground_duration = base::Seconds(1);
  67. for (const auto& visit : cluster.visits) {
  68. // We don't care about checking for duplicate visits since the duration
  69. // should have been rolled up already.
  70. if (visit.annotated_visit.visit_row.visit_duration > max_visit_duration) {
  71. max_visit_duration = visit.annotated_visit.visit_row.visit_duration;
  72. }
  73. if (visit.annotated_visit.context_annotations.total_foreground_duration >
  74. max_foreground_duration) {
  75. max_foreground_duration =
  76. visit.annotated_visit.context_annotations.total_foreground_duration;
  77. }
  78. }
  79. for (const history::ClusterVisit& visit : base::Reversed(cluster.visits)) {
  80. float visit_duration_score =
  81. Smoothstep(0.0f, max_visit_duration.InSecondsF(),
  82. visit.annotated_visit.visit_row.visit_duration.InSecondsF());
  83. float foreground_duration_score =
  84. Smoothstep(0.0f, max_foreground_duration.InSecondsF(),
  85. std::max(0.0, visit.annotated_visit.context_annotations
  86. .total_foreground_duration.InSecondsF()));
  87. auto visit_scores_it =
  88. url_visit_scores.find(visit.annotated_visit.visit_row.visit_id);
  89. if (visit_scores_it == url_visit_scores.end()) {
  90. VisitScores visit_scores;
  91. visit_scores.set_visit_duration_score(visit_duration_score);
  92. visit_scores.set_foreground_duration_score(foreground_duration_score);
  93. url_visit_scores.insert(
  94. {visit.annotated_visit.visit_row.visit_id, std::move(visit_scores)});
  95. } else {
  96. visit_scores_it->second.set_visit_duration_score(visit_duration_score);
  97. visit_scores_it->second.set_foreground_duration_score(
  98. foreground_duration_score);
  99. }
  100. }
  101. }
  102. void RankingClusterFinalizer::ComputeFinalVisitScores(
  103. history::Cluster& cluster,
  104. base::flat_map<history::VisitID, VisitScores>& url_visit_scores) {
  105. float max_score = -1.0;
  106. for (history::ClusterVisit& visit : base::Reversed(cluster.visits)) {
  107. // Only canonical visits should have scores > 0.0.
  108. for (auto& duplicate_visit : visit.duplicate_visits) {
  109. // Check that no individual scores have been given a visit that is not
  110. // canonical a score.
  111. DCHECK(url_visit_scores.find(
  112. duplicate_visit.annotated_visit.visit_row.visit_id) ==
  113. url_visit_scores.end());
  114. duplicate_visit.score = 0.0;
  115. }
  116. // Determine the max score to use for normalizing all the scores.
  117. auto visit_scores_it =
  118. url_visit_scores.find(visit.annotated_visit.visit_row.visit_id);
  119. if (visit_scores_it != url_visit_scores.end()) {
  120. visit.score = visit_scores_it->second.GetTotalScore();
  121. if (visit.score > max_score) {
  122. max_score = visit.score;
  123. }
  124. }
  125. }
  126. if (max_score <= 0.0)
  127. return;
  128. // Now normalize the score by `max_score` so they values are all between 0
  129. // and 1.
  130. for (history::ClusterVisit& visit : base::Reversed(cluster.visits)) {
  131. visit.score = visit.score / max_score;
  132. }
  133. }
  134. } // namespace history_clusters