Add a min score for new front tiles

If the first tile is not frequently clicked, other tiles may be displayed
before it. And those tiles, even if they are not clicked for many days,
will stay before the first.
This may cause new front tiles, such as the trending tiles, to have a
lower score than those not clicked tiles. And will not show up in the
front. This CL fixes that issue

BUG=1136306

(cherry picked from commit 6821ef76966a60fea75905e01e3b7e5214bae663)

Change-Id: I2f5aa7d3a64450b23375b2df2647265ed9c70c22
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2459941
Commit-Queue: Min Qin <[email protected]>
Reviewed-by: Shakti Sahu <[email protected]>
Reviewed-by: Xing Liu <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#815277}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2463921
Reviewed-by: Ben Mason <[email protected]>
Reviewed-by: Min Qin <[email protected]>
Commit-Queue: Ben Mason <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#277}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/components/query_tiles/internal/tile_config.cc b/components/query_tiles/internal/tile_config.cc
index 68b90b4..9fe5cc64 100644
--- a/components/query_tiles/internal/tile_config.cc
+++ b/components/query_tiles/internal/tile_config.cc
@@ -50,6 +50,9 @@
 
 constexpr char kTileScoreDecayLambdaKey[] = "tile_score_decay_lambda";
 
+constexpr char kMinimumScoreForNewFrontTilesKey[] =
+    "min_score_for_new_front_tiles";
+
 // Default expire duration.
 constexpr int kDefaultExpireDurationInSeconds = 48 * 60 * 60;  // 2 days.
 
@@ -72,6 +75,11 @@
 // Default lambda value used for calculating tile score decay over time.
 constexpr double kDefaultTileScoreDecayLambda = -0.099;
 
+// Default minimum score for new tiles in front of others. 0.9 is chosen so
+// that new tiles will have a higher score than tiles that have not been
+// clicked for 2 days.
+constexpr double kDefaultMinimumTileScoreForNewFrontTiles = 0.9;
+
 namespace {
 
 // For testing. Json string for single tier experiment tag.
@@ -183,4 +191,11 @@
       kDefaultTileScoreDecayLambda);
 }
 
+// static
+double TileConfig::GetMinimumScoreForNewFrontTiles() {
+  return base::GetFieldTrialParamByFeatureAsDouble(
+      features::kQueryTiles, kMinimumScoreForNewFrontTilesKey,
+      kDefaultMinimumTileScoreForNewFrontTiles);
+}
+
 }  // namespace query_tiles
diff --git a/components/query_tiles/internal/tile_config.h b/components/query_tiles/internal/tile_config.h
index 33836b17..5dd1816 100644
--- a/components/query_tiles/internal/tile_config.h
+++ b/components/query_tiles/internal/tile_config.h
@@ -50,6 +50,10 @@
 // Finch parameter key for lambda in tile score decay calculation.
 extern const char kTileScoreDecayLambdaKey[];
 
+// Finch parameter key representing the minimum scores for new tiles that are in
+// front of others.
+extern const char kMinimumScoreForNewFrontTilesKey[];
+
 class TileConfig {
  public:
   // Gets the URL for the Query Tiles server.
@@ -90,6 +94,9 @@
 
   // Get the lambda value used for calculating the tile score decay over time.
   static double GetTileScoreDecayLambda();
+
+  // Get the minimum scrore for newly showing tiles that are in front of others.
+  static double GetMinimumScoreForNewFrontTiles();
 };
 
 }  // namespace query_tiles
diff --git a/components/query_tiles/internal/tile_utils.cc b/components/query_tiles/internal/tile_utils.cc
index b6d7c57..1fcf655 100644
--- a/components/query_tiles/internal/tile_utils.cc
+++ b/components/query_tiles/internal/tile_utils.cc
@@ -32,18 +32,6 @@
 
   // Some tiles do not have scores, so the first step is to calculate scores
   // for them.
-  // To calculate scores for new tiles, ordering from the server response will
-  // be taken into consideration. As the server has already ordered tiles
-  // according to their importance.
-  // For example, if the first tile returned by server never appeared before, we
-  // should set its score to at least the 2nd tile. so that it can show up in
-  // the first place if no other tiles in the back have a higher score. For
-  // a new tile at position x, its score should be the minimum of its neighbors
-  // at position x-1 and x+1. For new tiles showing up at the end, their score
-  // will be set to 0.
-  // For example, if the tile scores are (new_tile, 0.5, 0.7), then the adjusted
-  // score will be (0.5, 0.5, 0.7). Simularly, (0.5, new_tile1, 0.7, new_tile2)
-  // will result in (0.5, 0.5, 0.7, 0).
   double last_score = std::numeric_limits<double>::max();
   base::Time now_time = base::Time::Now();
   TileStats last_tile_stats(now_time, last_score);
@@ -65,6 +53,17 @@
       double min_score = std::min(new_score, last_score);
       TileStats new_stats =
           new_score > last_score ? last_tile_stats : iter->second;
+      // For new tiles at the beginning, give them a score higher than the
+      // minimum score, so that they have a chance to show if the top ranked
+      // tiles have not been clicked recently.
+      if (new_tile_index == 0) {
+        double min_score_for_new_front_tiles =
+            TileConfig::GetMinimumScoreForNewFrontTiles();
+        if (min_score < min_score_for_new_front_tiles) {
+          min_score = min_score_for_new_front_tiles;
+          new_stats = TileStats(now_time, min_score);
+        }
+      }
       for (size_t j = new_tile_index; j < i; ++j) {
         tile_stats->emplace((*tiles)[j]->id, new_stats);
         score_map.emplace((*tiles)[j]->id, min_score);
diff --git a/components/query_tiles/internal/tile_utils.h b/components/query_tiles/internal/tile_utils.h
index ba5b53f..35b2efb 100644
--- a/components/query_tiles/internal/tile_utils.h
+++ b/components/query_tiles/internal/tile_utils.h
@@ -16,6 +16,20 @@
 // Function to sort a vector of tiles based on their score in |tile_stats|. If
 // a tile ID doesn't exists in |tile_stats|, a new entry will be created and
 // a score will be calculated.
+// To calculate scores for new tiles, ordering from the server response will
+// be taken into consideration. As the server has already ordered tiles
+// according to their importance.
+// For example, if the first tile returned by server never appeared before, we
+// should set its score to at least the 2nd tile. so that it can show up in
+// the first place if no other tiles in the back have a higher score. For
+// a new tile at position x, its score should be the minimum of its neighbors
+// at position x-1 and x+1. For new tiles showing up at the end, their score
+// will be set to 0.
+// For example, if the tile scores are (new_tile, 0.5, 0.7), then the adjusted
+// score will be (0.5, 0.5, 0.7). Simularly, (0.5, new_tile1, 0.7, new_tile2)
+// will result in (0.5, 0.5, 0.7, 0). And for new tiles at the front, they are
+// guaranteed a minimum score. So that if all the other tiles haven't been
+// clicked for a while, it will have a chance to be placed at the front.
 void SortTiles(std::vector<std::unique_ptr<Tile>>* tiles,
                std::map<std::string, TileStats>* tile_stats);
 
diff --git a/components/query_tiles/internal/tile_utils_unittest.cc b/components/query_tiles/internal/tile_utils_unittest.cc
index 18ba3f5..000503c 100644
--- a/components/query_tiles/internal/tile_utils_unittest.cc
+++ b/components/query_tiles/internal/tile_utils_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/query_tiles/internal/tile_utils.h"
 
+#include "components/query_tiles/internal/tile_config.h"
 #include "components/query_tiles/internal/tile_group.h"
 #include "components/query_tiles/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -56,9 +57,13 @@
   EXPECT_EQ(group.tiles[2]->id, "guid-1-3");
   EXPECT_EQ(group.tiles[0]->sub_tiles[0]->id, "guid-2-1");
   EXPECT_EQ(group.tiles[0]->sub_tiles[1]->id, "guid-2-2");
-  EXPECT_EQ(tile_stats["guid-1-1"].score, 0.7);
-  EXPECT_EQ(tile_stats["guid-1-2"].score, 0.7);
-  EXPECT_EQ(tile_stats["guid-2-1"].score, 0.6);
+  // Front tiles should have the minimum score.
+  EXPECT_EQ(tile_stats["guid-1-1"].score,
+            TileConfig::GetMinimumScoreForNewFrontTiles());
+  EXPECT_EQ(tile_stats["guid-1-2"].score,
+            TileConfig::GetMinimumScoreForNewFrontTiles());
+  EXPECT_EQ(tile_stats["guid-2-1"].score,
+            TileConfig::GetMinimumScoreForNewFrontTiles());
 }
 
 // If new tiles are at the end, tile ordering should be kept after