on_device_clustering_util.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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/on_device_clustering_util.h"
  5. #include "base/containers/contains.h"
  6. #include "base/time/time.h"
  7. #include "components/history_clusters/core/config.h"
  8. #include "components/history_clusters/core/history_clusters_util.h"
  9. #include "components/history_clusters/core/on_device_clustering_features.h"
  10. namespace history_clusters {
  11. void MergeDuplicateVisitIntoCanonicalVisit(
  12. history::ClusterVisit&& duplicate_visit,
  13. history::ClusterVisit& canonical_visit) {
  14. // Upgrade the canonical visit's annotations (i.e. is-bookmarked) with
  15. // those of the duplicate visits.
  16. auto& context_annotations =
  17. canonical_visit.annotated_visit.context_annotations;
  18. const auto& duplicate_annotations =
  19. duplicate_visit.annotated_visit.context_annotations;
  20. context_annotations.is_existing_bookmark |=
  21. duplicate_annotations.is_existing_bookmark;
  22. context_annotations.is_existing_part_of_tab_group |=
  23. duplicate_annotations.is_existing_part_of_tab_group;
  24. context_annotations.is_new_bookmark |= duplicate_annotations.is_new_bookmark;
  25. context_annotations.is_placed_in_tab_group |=
  26. duplicate_annotations.is_placed_in_tab_group;
  27. context_annotations.is_ntp_custom_link |=
  28. duplicate_annotations.is_ntp_custom_link;
  29. context_annotations.omnibox_url_copied |=
  30. duplicate_annotations.omnibox_url_copied;
  31. auto& canonical_searches =
  32. canonical_visit.annotated_visit.content_annotations.related_searches;
  33. const auto& duplicate_searches =
  34. duplicate_visit.annotated_visit.content_annotations.related_searches;
  35. for (const auto& query : duplicate_searches) {
  36. // This is an n^2 algorithm, but in practice the list of related
  37. // searches should be on the order of 10 elements long at maximum.
  38. // If that's not true we should replace this with a set structure.
  39. if (!base::Contains(canonical_searches, query)) {
  40. canonical_searches.push_back(query);
  41. }
  42. }
  43. // Merge over the model annotations (categories and entities) too.
  44. canonical_visit.annotated_visit.content_annotations.model_annotations
  45. .MergeFrom(duplicate_visit.annotated_visit.content_annotations
  46. .model_annotations);
  47. // Roll up the visit duration from the duplicate visit into the canonical
  48. // visit.
  49. canonical_visit.annotated_visit.visit_row.visit_duration +=
  50. duplicate_visit.annotated_visit.visit_row.visit_duration;
  51. // Only add the foreground duration if it is populated and roll it up.
  52. base::TimeDelta duplicate_foreground_duration =
  53. duplicate_visit.annotated_visit.context_annotations
  54. .total_foreground_duration;
  55. // Check for > 0 since the default for total_foreground_duration is -1.
  56. if (duplicate_foreground_duration > base::Seconds(0)) {
  57. base::TimeDelta canonical_foreground_duration =
  58. canonical_visit.annotated_visit.context_annotations
  59. .total_foreground_duration;
  60. canonical_visit.annotated_visit.context_annotations
  61. .total_foreground_duration =
  62. canonical_foreground_duration > base::Seconds(0)
  63. ? canonical_foreground_duration + duplicate_foreground_duration
  64. : duplicate_foreground_duration;
  65. }
  66. // Update the canonical_visit with the more recent timestamp.
  67. canonical_visit.annotated_visit.visit_row.visit_time =
  68. std::max(canonical_visit.annotated_visit.visit_row.visit_time,
  69. duplicate_visit.annotated_visit.visit_row.visit_time);
  70. canonical_visit.duplicate_visits.push_back(std::move(duplicate_visit));
  71. }
  72. void SortClusters(std::vector<history::Cluster>* clusters) {
  73. DCHECK(clusters);
  74. // Within each cluster, sort visits.
  75. for (auto& cluster : *clusters) {
  76. StableSortVisits(cluster.visits);
  77. }
  78. // After that, sort clusters reverse-chronologically based on their highest
  79. // scored visit.
  80. base::ranges::stable_sort(*clusters, [&](auto& c1, auto& c2) {
  81. DCHECK(!c1.visits.empty());
  82. base::Time c1_time = c1.visits.front().annotated_visit.visit_row.visit_time;
  83. DCHECK(!c2.visits.empty());
  84. base::Time c2_time = c2.visits.front().annotated_visit.visit_row.visit_time;
  85. // Use c1 > c2 to get more recent clusters BEFORE older clusters.
  86. return c1_time > c2_time;
  87. });
  88. }
  89. bool IsNoisyVisit(const history::ClusterVisit& visit) {
  90. return visit.engagement_score >
  91. GetConfig().noisy_cluster_visits_engagement_threshold &&
  92. visit.annotated_visit.content_annotations.search_terms.empty();
  93. }
  94. } // namespace history_clusters