]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22875 Index project measures using the measures table (#11734)
authorClaire Villard <60586848+claire-villard-sonarsource@users.noreply.github.com>
Wed, 18 Sep 2024 12:35:09 +0000 (14:35 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 9 Oct 2024 20:02:46 +0000 (20:02 +0000)
Co-authored-by: Eric Giffon <eric.giffon@sonarsource.com>
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ProjectNclocComputationStep.java
server/sonar-db-dao/src/it/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java
server/sonar-server-common/src/it/java/org/sonar/server/measure/index/ProjectMeasuresIndexerIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java

index 031208c1c2a4900e054099fd153c28bbb42c41f9..2d2c4baae758f11377631b38c47937b78f107ca3 100644 (file)
  */
 package org.sonar.ce.task.projectanalysis.step;
 
-import java.util.List;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.ce.task.step.ComputationStep;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.measure.MeasureDto;
-
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 
 public class ProjectNclocComputationStep implements ComputationStep {
 
index 93646ce68bf20d93ce45ea485c9029c19c0f7c83..930ec27023b260c7313309fe428395ffa9c03b02 100644 (file)
@@ -64,8 +64,8 @@ class ProjectMeasuresIndexerIteratorIT {
     SnapshotDto analysis = dbTester.components().insertSnapshot(project);
     MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
     MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage"));
-    dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d));
-    dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(20d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric1.getKey(), 10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric2.getKey(), 20d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -89,8 +89,8 @@ class ProjectMeasuresIndexerIteratorIT {
     SnapshotDto analysis = dbTester.components().insertSnapshot(project);
     MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
     MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage"));
-    dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d));
-    dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(20d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric1.getKey(), 10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric2.getKey(), 20d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -111,7 +111,7 @@ class ProjectMeasuresIndexerIteratorIT {
       p -> p.setTagsString("platform,java"));
     ComponentDto project = projectData.getMainBranchComponent();
     MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("new_lines"));
-    dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric.getKey(), 10d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -120,14 +120,13 @@ class ProjectMeasuresIndexerIteratorIT {
 
   @Test
   void return_quality_gate_status_measure() {
-    ComponentDto project1 = dbTester.components().insertPrivateProject().getMainBranchComponent();
     ProjectData projectData2 = dbTester.components().insertPrivateProject();
     ComponentDto project2 = projectData2.getMainBranchComponent();
     ProjectData projectData3 = dbTester.components().insertPrivateProject();
     ComponentDto project3 = projectData3.getMainBranchComponent();
     MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status"));
-    dbTester.measures().insertLiveMeasure(project2, metric, m -> m.setValue(null).setData(OK.name()));
-    dbTester.measures().insertLiveMeasure(project3, metric, m -> m.setValue(null).setData(ERROR.name()));
+    dbTester.measures().insertMeasure(project2, m -> m.addValue(metric.getKey(), OK.name()));
+    dbTester.measures().insertMeasure(project3, m -> m.addValue(metric.getKey(), ERROR.name()));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -140,7 +139,7 @@ class ProjectMeasuresIndexerIteratorIT {
     ProjectData projectData = dbTester.components().insertPrivateProject();
     ComponentDto project = projectData.getMainBranchComponent();
     MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status"));
-    dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setData((String) null));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric.getKey(), null));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -150,17 +149,18 @@ class ProjectMeasuresIndexerIteratorIT {
   @Test
   void return_language_distribution_measure_from_biggest_branch() {
     ProjectData projectData = dbTester.components().insertPrivateProject();
+    dbClient.projectDao().updateNcloc(dbSession, projectData.projectUuid(), 52L);
     ComponentDto project = projectData.getMainBranchComponent();
     MetricDto languagesDistributionMetric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey(
       "ncloc_language_distribution"));
     MetricDto nclocMetric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
 
-    dbTester.measures().insertLiveMeasure(project, languagesDistributionMetric, m -> m.setValue(null).setData("<null>=2;java=6;xoo=18"));
-    dbTester.measures().insertLiveMeasure(project, nclocMetric, m -> m.setValue(26d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(languagesDistributionMetric.getKey(), "<null>=2;java=6;xoo=18"));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(nclocMetric.getKey(), 26d));
 
     ComponentDto branch = dbTester.components().insertProjectBranch(project);
-    dbTester.measures().insertLiveMeasure(branch, languagesDistributionMetric, m -> m.setValue(null).setData("<null>=4;java=12;xoo=36"));
-    dbTester.measures().insertLiveMeasure(branch, nclocMetric, m -> m.setValue(52d));
+    dbTester.measures().insertMeasure(branch, m -> m.addValue(languagesDistributionMetric.getKey(), "<null>=4;java=12;xoo=36"));
+    dbTester.measures().insertMeasure(branch, m -> m.addValue(nclocMetric.getKey(), 52d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -175,9 +175,9 @@ class ProjectMeasuresIndexerIteratorIT {
     MetricDto dataMetric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey("data"));
     MetricDto distribMetric = dbTester.measures().insertMetric(m -> m.setValueType(DISTRIB.name()).setKey("distrib"));
     MetricDto stringMetric = dbTester.measures().insertMetric(m -> m.setValueType(STRING.name()).setKey("string"));
-    dbTester.measures().insertLiveMeasure(project, dataMetric, m -> m.setData("dat"));
-    dbTester.measures().insertLiveMeasure(project, distribMetric, m -> m.setData("dis"));
-    dbTester.measures().insertLiveMeasure(project, stringMetric, m -> m.setData("str"));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(dataMetric.getKey(), "dat"));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(distribMetric.getKey(), "dis"));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(stringMetric.getKey(), "str"));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -190,7 +190,7 @@ class ProjectMeasuresIndexerIteratorIT {
     ComponentDto project = projectData.getMainBranchComponent();
     MetricDto disabledMetric =
       dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setEnabled(false).setHidden(false).setKey("disabled"));
-    dbTester.measures().insertLiveMeasure(project, disabledMetric, m -> m.setValue(10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(disabledMetric.getKey(), 10d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
@@ -205,9 +205,9 @@ class ProjectMeasuresIndexerIteratorIT {
     ProjectData projectData = dbTester.components().insertPrivateProject();
     ComponentDto project = projectData.getMainBranchComponent();
 
-    dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d));
-    dbTester.measures().insertLiveMeasure(project, leakMetric, m -> m.setValue(20d));
-    dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric1.getKey(), 10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(leakMetric.getKey(), 20d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric2.getKey(), null));
 
     Map<String, Double> numericMeasures =
       createResultSetAndReturnDocsById().get(projectData.projectUuid()).getMeasures().getNumericMeasures();
@@ -220,8 +220,8 @@ class ProjectMeasuresIndexerIteratorIT {
     MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
     ProjectData projectData = dbTester.components().insertPrivateProject();
     ComponentDto project = projectData.getMainBranchComponent();
-    dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d).setData((String) null));
-    dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null).setData("foo"));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric1.getKey(), 10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric2.getKey(), "foo"));
 
     Map<String, Double> numericMeasures =
       createResultSetAndReturnDocsById().get(projectData.projectUuid()).getMeasures().getNumericMeasures();
@@ -262,8 +262,9 @@ class ProjectMeasuresIndexerIteratorIT {
     ComponentDto project2 = dbTester.components().insertPrivateProject().getMainBranchComponent();
     ComponentDto project3 = dbTester.components().insertPrivateProject().getMainBranchComponent();
     SnapshotDto analysis1 = dbTester.components().insertSnapshot(project1);
-    SnapshotDto analysis2 = dbTester.components().insertSnapshot(project2);
-    SnapshotDto analysis3 = dbTester.components().insertSnapshot(project3);
+    // analyses on projects 2 and 3
+    dbTester.components().insertSnapshot(project2);
+    dbTester.components().insertSnapshot(project3);
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById(projectData1.projectUuid());
 
@@ -291,10 +292,10 @@ class ProjectMeasuresIndexerIteratorIT {
     ProjectData projectData = dbTester.components().insertPrivateProject();
     ComponentDto project = projectData.getMainBranchComponent();
     MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
-    dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(10d));
+    dbTester.measures().insertMeasure(project, m -> m.addValue(metric.getKey(), 10d));
 
     ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
-    dbTester.measures().insertLiveMeasure(branch, metric, m -> m.setValue(20d));
+    dbTester.measures().insertMeasure(branch, m -> m.addValue(metric.getKey(), 20d));
 
     Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();
 
index eb256bd630951cd8eaec56138da9b8437f308126..fdff5b8d0271e320e57bf7f129c4445d2291feab 100644 (file)
@@ -21,6 +21,8 @@ package org.sonar.db.measure;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -32,12 +34,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.apache.commons.lang3.StringUtils;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.core.metric.SoftwareQualitiesMetrics;
@@ -46,6 +44,7 @@ import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 
 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
 import static org.sonar.api.utils.KeyValueFormat.parseStringInt;
 import static org.sonar.db.component.DbTagsReader.readDbTags;
@@ -53,7 +52,7 @@ import static org.sonar.db.component.DbTagsReader.readDbTags;
 public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMeasuresIndexerIterator.ProjectMeasures> {
 
   public static final Set<String> METRIC_KEYS = ImmutableSortedSet.of(
-    CoreMetrics.NCLOC_KEY,
+    NCLOC_KEY,
     CoreMetrics.LINES_KEY,
     CoreMetrics.DUPLICATED_LINES_DENSITY_KEY,
     CoreMetrics.COVERAGE_KEY,
@@ -80,73 +79,91 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY,
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY
-    );
+  );
 
-  private static final String SQL_PROJECTS = "SELECT p.uuid, p.kee, p.name, p.created_at, s.created_at, p.tags, p.qualifier " +
-    "FROM projects p " +
-    "INNER JOIN project_branches pb ON pb.project_uuid = p.uuid AND pb.is_main = ? " +
-    "LEFT OUTER JOIN snapshots s ON s.root_component_uuid=pb.uuid AND s.islast=? " +
-    "WHERE p.qualifier in (?, ?)";
+  private static final Gson GSON = new Gson();
+
+  private static final String SQL_PROJECTS = """
+    SELECT p.uuid, p.kee, p.name, p.created_at, s.created_at, p.tags, p.qualifier, p.ncloc
+    FROM projects p
+    INNER JOIN project_branches pb ON pb.project_uuid = p.uuid AND pb.is_main = ?
+    LEFT OUTER JOIN snapshots s ON s.root_component_uuid=pb.uuid AND s.islast=?
+    WHERE p.qualifier in (?, ?)""";
 
   private static final String PROJECT_FILTER = " AND pb.project_uuid=?";
 
+  private static final String SQL_GENERIC_METRICS = """
+    SELECT m.name
+    FROM metrics m
+    WHERE m.enabled = ?""";
+
   private static final String SQL_MEASURES = """
-    SELECT m.name, pm.value, pm.text_value FROM live_measures pm
-    INNER JOIN metrics m ON m.uuid = pm.metric_uuid
-    INNER JOIN project_branches pb ON pb.uuid = pm.component_uuid
+    SELECT m.json_value
+    FROM measures m
+    INNER JOIN project_branches pb ON pb.uuid = m.component_uuid
     WHERE pb.project_uuid = ?
-    AND pb.is_main = ?
-    AND m.name IN ({metricNames})
-    AND (pm.value IS NOT NULL OR pm.text_value IS NOT NULL)
-    AND m.enabled = ?""";
+    AND pb.is_main = ?""";
 
   private static final String SQL_NCLOC_LANGUAGE_DISTRIBUTION = """
-    SELECT m.name, pm.value, pm.text_value FROM live_measures pm
-    INNER JOIN metrics m ON m.uuid = pm.metric_uuid
-    WHERE pm.component_uuid = ?
-    AND m.name = ?
-    AND (pm.value IS NOT NULL OR pm.text_value IS NOT NULL)
-    AND m.enabled = ?""";
-
-  private static final String SQL_BIGGEST_NCLOC_VALUE = """
-    SELECT max(lm.value) FROM metrics m
-    INNER JOIN live_measures lm ON m.uuid = lm.metric_uuid
-    INNER JOIN project_branches pb ON lm.component_uuid = pb.uuid
-    WHERE pb.project_uuid = ?
-    AND m.name = ? AND lm.value IS NOT NULL AND m.enabled = ? """;
+    SELECT m.component_uuid, m.branch_uuid, m.json_value
+    FROM measures m
+    WHERE m.branch_uuid = ?""";
 
   private static final String SQL_BRANCH_BY_NCLOC = """
-    SELECT lm.component_uuid FROM metrics m
-    INNER JOIN live_measures lm ON m.uuid = lm.metric_uuid
-    INNER JOIN project_branches pb ON lm.component_uuid = pb.uuid
-    WHERE pb.project_uuid = ?
-    AND m.name = ? AND lm.value = ? AND m.enabled = ?""";
+    SELECT m.component_uuid, m.json_value
+    FROM measures m
+    INNER JOIN project_branches pb ON m.component_uuid = pb.uuid
+    WHERE pb.project_uuid = ?""";
 
   private static final boolean ENABLED = true;
-  private static final int FIELD_METRIC_NAME = 1;
-  private static final int FIELD_MEASURE_VALUE = 2;
-  private static final int FIELD_MEASURE_TEXT_VALUE = 3;
+  public static final int JSON_VALUE_FIELD = 2;
 
   private final DbSession dbSession;
   private final PreparedStatement measuresStatement;
   private final Iterator<Project> projects;
+  private final List<String> metrics;
 
-  private ProjectMeasuresIndexerIterator(DbSession dbSession, PreparedStatement measuresStatement, List<Project> projects) {
+  private ProjectMeasuresIndexerIterator(DbSession dbSession, PreparedStatement measuresStatement, List<Project> projects, List<String> metrics) {
     this.dbSession = dbSession;
     this.measuresStatement = measuresStatement;
     this.projects = projects.iterator();
+    this.metrics = metrics;
   }
 
   public static ProjectMeasuresIndexerIterator create(DbSession session, @Nullable String projectUuid) {
     List<Project> projects = selectProjects(session, projectUuid);
     PreparedStatement projectsStatement = createMeasuresStatement(session);
-    return new ProjectMeasuresIndexerIterator(session, projectsStatement, projects);
+    return new ProjectMeasuresIndexerIterator(session, projectsStatement, projects, selectMetrics(session));
+  }
+
+  private static List<String> selectMetrics(DbSession session) {
+    List<String> metrics = new ArrayList<>();
+    try (PreparedStatement stmt = createMetricsStatement(session);
+         ResultSet rs = stmt.executeQuery()) {
+      while (rs.next()) {
+        String key = rs.getString(1);
+        metrics.add(key);
+      }
+      return metrics;
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to execute request to select all metrics", e);
+    }
+  }
+
+  private static PreparedStatement createMetricsStatement(DbSession session) {
+    try {
+      PreparedStatement stmt = session.getConnection().prepareStatement(SQL_GENERIC_METRICS);
+      stmt.setBoolean(1, ENABLED);
+      return stmt;
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to prepare SQL request to select all metrics", e);
+    }
   }
 
   private static List<Project> selectProjects(DbSession session, @Nullable String projectUuid) {
     List<Project> projects = new ArrayList<>();
     try (PreparedStatement stmt = createProjectsStatement(session, projectUuid);
-      ResultSet rs = stmt.executeQuery()) {
+         ResultSet rs = stmt.executeQuery()) {
       while (rs.next()) {
         String uuid = rs.getString(1);
         String key = rs.getString(2);
@@ -155,7 +172,17 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
         Long analysisDate = DatabaseUtils.getLong(rs, 5);
         List<String> tags = readDbTags(DatabaseUtils.getString(rs, 6));
         String qualifier = rs.getString(7);
-        Project project = new Project(uuid, key, name, qualifier, tags, creationDate, analysisDate);
+        Long ncloc = DatabaseUtils.getLong(rs, 8);
+        Project project = new Project.Builder()
+          .setUuid(uuid)
+          .setKey(key)
+          .setName(name)
+          .setQualifier(qualifier)
+          .setTags(tags)
+          .setCreationDate(creationDate)
+          .setAnalysisDate(analysisDate)
+          .setNcloc(ncloc)
+          .build();
         projects.add(project);
       }
       return projects;
@@ -186,11 +213,7 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
 
   private static PreparedStatement createMeasuresStatement(DbSession session) {
     try {
-      String metricNameQuestionMarks = METRIC_KEYS.stream()
-        .filter(m -> !m.equals(NCLOC_LANGUAGE_DISTRIBUTION_KEY))
-        .map(x -> "?").collect(Collectors.joining(","));
-      String sql = StringUtils.replace(SQL_MEASURES, "{metricNames}", metricNameQuestionMarks);
-      return session.getConnection().prepareStatement(sql);
+      return session.getConnection().prepareStatement(SQL_MEASURES);
     } catch (SQLException e) {
       throw new IllegalStateException("Fail to prepare SQL request to select measures", e);
     }
@@ -203,30 +226,20 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
       return null;
     }
     Project project = projects.next();
-    Measures measures = selectMeasures(project.getUuid());
+    Measures measures = selectMeasures(project);
     return new ProjectMeasures(project, measures);
   }
 
-  private Measures selectMeasures(String projectUuid) {
+  private Measures selectMeasures(Project project) {
+    String projectUuid = project.getUuid();
     try {
-      Measures measures = new Measures();
-      prepareMeasuresStatement(projectUuid);
-      try (ResultSet rs = measuresStatement.executeQuery()) {
-        while (rs.next()) {
-          readMeasure(rs, measures);
-        }
-      }
+      Measures measures = getMeasures(projectUuid);
 
-      String biggestBranch = selectProjectBiggestNcloc(dbSession, projectUuid)
-        .flatMap(ncloc -> selectProjectBranchForNcloc(dbSession, projectUuid, ncloc))
-        .orElse("");
+      Optional<String> biggestBranch = project.getNcloc()
+        .flatMap(ncloc -> selectProjectBranchForNcloc(dbSession, projectUuid, ncloc));
 
-      try (PreparedStatement prepareNclocByLanguageStatement = prepareNclocByLanguageStatement(dbSession, biggestBranch)) {
-        try (ResultSet rs = prepareNclocByLanguageStatement.executeQuery()) {
-          if (rs.next()) {
-            readMeasure(rs, measures);
-          }
-        }
+      if (biggestBranch.isPresent()) {
+        readNclocDistributionFromBiggestBranch(biggestBranch.get(), measures);
       }
 
       return measures;
@@ -235,88 +248,100 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     }
   }
 
-  private void prepareMeasuresStatement(String projectUuid) throws SQLException {
-    AtomicInteger index = new AtomicInteger(1);
-    measuresStatement.setString(index.getAndIncrement(), projectUuid);
-    measuresStatement.setBoolean(index.getAndIncrement(), true);
-    METRIC_KEYS
-      .stream()
-      .filter(m -> !m.equals(NCLOC_LANGUAGE_DISTRIBUTION_KEY))
-      .forEach(DatabaseUtils.setStrings(measuresStatement, index::getAndIncrement));
-    measuresStatement.setBoolean(index.getAndIncrement(), ENABLED);
+  private Measures getMeasures(String projectUuid) throws SQLException {
+    Measures measures = new Measures();
+    measuresStatement.setString(1, projectUuid);
+    measuresStatement.setBoolean(2, true);
+
+    try (ResultSet rs = measuresStatement.executeQuery()) {
+      while (rs.next()) {
+        readMeasureLine(rs, measures);
+      }
+    }
+    return measures;
+  }
+
+  private void readMeasureLine(ResultSet rs, Measures measures) throws SQLException {
+    String jsonValue = rs.getString(1);
+    Map<String, Object> metricValues = GSON.fromJson(jsonValue, new TypeToken<Map<String, Object>>() {
+    }.getType());
+
+    metricValues.forEach((metricKey, value) -> readMeasure(measures, metricKey, value));
+  }
+
+  private void readMeasure(Measures measures, String metricKey, Object value) {
+    if (METRIC_KEYS.contains(metricKey) && metrics.contains(metricKey)) {
+      if (ALERT_STATUS_KEY.equals(metricKey) && value instanceof String qualityGate) {
+        measures.setQualityGateStatus(qualityGate);
+        return;
+      }
+      if (NCLOC_LANGUAGE_DISTRIBUTION_KEY.equals(metricKey) && value instanceof String distribution) {
+        measures.setNclocByLanguages(distribution);
+        return;
+      }
+      if (value instanceof Double doubleValue) {
+        measures.addNumericMeasure(metricKey, doubleValue);
+      }
+    }
+  }
+
+  private void readNclocDistributionFromBiggestBranch(String biggestBranch, Measures measures) throws SQLException {
+    try (PreparedStatement prepareNclocByLanguageStatement = prepareNclocByLanguageStatement(dbSession, biggestBranch);
+         ResultSet rs = prepareNclocByLanguageStatement.executeQuery()) {
+      if (rs.next()) {
+        readNclocDistributionKey(rs, measures);
+      }
+    }
   }
 
   private static PreparedStatement prepareNclocByLanguageStatement(DbSession session, String branchUuid) {
     try {
       PreparedStatement stmt = session.getConnection().prepareStatement(SQL_NCLOC_LANGUAGE_DISTRIBUTION);
-      AtomicInteger index = new AtomicInteger(1);
-      stmt.setString(index.getAndIncrement(), branchUuid);
-      stmt.setString(index.getAndIncrement(), NCLOC_LANGUAGE_DISTRIBUTION_KEY);
-      stmt.setBoolean(index.getAndIncrement(), ENABLED);
+      stmt.setString(1, branchUuid);
       return stmt;
     } catch (SQLException e) {
       throw new IllegalStateException("Fail to execute request to select ncloc_language_distribution measure", e);
     }
   }
 
-  private static Optional<Long> selectProjectBiggestNcloc(DbSession session, String projectUuid) {
-    try (PreparedStatement nclocStatement = session.getConnection().prepareStatement(SQL_BIGGEST_NCLOC_VALUE)) {
-      AtomicInteger index = new AtomicInteger(1);
-      nclocStatement.setString(index.getAndIncrement(), projectUuid);
-      nclocStatement.setString(index.getAndIncrement(), CoreMetrics.NCLOC_KEY);
-      nclocStatement.setBoolean(index.getAndIncrement(), ENABLED);
+  private static Optional<String> selectProjectBranchForNcloc(DbSession session, String projectUuid, long ncloc) {
+    try (PreparedStatement nclocStatement = session.getConnection().prepareStatement(SQL_BRANCH_BY_NCLOC)) {
+      nclocStatement.setString(1, projectUuid);
 
       try (ResultSet rs = nclocStatement.executeQuery()) {
-        if (rs.next()) {
-          return Optional.of(rs.getLong(1));
-        }
-        return Optional.empty();
+        return readBranchMeasures(rs, ncloc);
       }
     } catch (SQLException e) {
-      throw new IllegalStateException("Fail to execute request to select the project biggest ncloc", e);
+      throw new IllegalStateException("Fail to execute request to select the project biggest branch", e);
     }
   }
 
-  private static Optional<String> selectProjectBranchForNcloc(DbSession session, String projectUuid, long ncloc) {
-    try (PreparedStatement nclocStatement = session.getConnection().prepareStatement(SQL_BRANCH_BY_NCLOC)) {
-      AtomicInteger index = new AtomicInteger(1);
-
-      nclocStatement.setString(index.getAndIncrement(), projectUuid);
-      nclocStatement.setString(index.getAndIncrement(), CoreMetrics.NCLOC_KEY);
-      nclocStatement.setLong(index.getAndIncrement(), ncloc);
-      nclocStatement.setBoolean(index.getAndIncrement(), ENABLED);
+  private static Optional<String> readBranchMeasures(ResultSet rs, long ncloc) throws SQLException {
+    while (rs.next()) {
+      String jsonValue = rs.getString(JSON_VALUE_FIELD);
+      Map<String, Object> metricValues = GSON.fromJson(jsonValue, new TypeToken<Map<String, Object>>() {
+      }.getType());
 
-      try (ResultSet rs = nclocStatement.executeQuery()) {
-        if (rs.next()) {
+      if (metricValues.containsKey(NCLOC_KEY)) {
+        Object nclocValue = metricValues.get(NCLOC_KEY);
+        if (nclocValue instanceof Double branchNcloc && branchNcloc == ncloc) {
           return Optional.of(rs.getString(1));
         }
       }
-      return Optional.empty();
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to execute request to select the project biggest branch", e);
     }
+    return Optional.empty();
   }
 
-  private static void readMeasure(ResultSet rs, Measures measures) throws SQLException {
-    String metricKey = rs.getString(FIELD_METRIC_NAME);
-    Optional<Double> value = getDouble(rs, FIELD_MEASURE_VALUE);
-    if (value.isPresent()) {
-      measures.addNumericMeasure(metricKey, value.get());
-      return;
-    }
-    if (ALERT_STATUS_KEY.equals(metricKey)) {
-      readTextValue(rs, measures::setQualityGateStatus);
-      return;
-    }
-    if (NCLOC_LANGUAGE_DISTRIBUTION_KEY.equals(metricKey)) {
-      readTextValue(rs, measures::setNclocByLanguages);
-    }
-  }
+  private static void readNclocDistributionKey(ResultSet rs, Measures measures) throws SQLException {
+    String jsonValue = rs.getString(3);
+    Map<String, Object> metricValues = GSON.fromJson(jsonValue, new TypeToken<Map<String, Object>>() {
+    }.getType());
 
-  private static void readTextValue(ResultSet rs, Consumer<String> action) throws SQLException {
-    String textValue = rs.getString(FIELD_MEASURE_TEXT_VALUE);
-    if (!rs.wasNull()) {
-      action.accept(textValue);
+    if (metricValues.containsKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY)) {
+      Object distribution = metricValues.get(NCLOC_LANGUAGE_DISTRIBUTION_KEY);
+      if (distribution instanceof String stringDistribution) {
+        measures.setNclocByLanguages(stringDistribution);
+      }
     }
   }
 
@@ -325,18 +350,6 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     measuresStatement.close();
   }
 
-  private static Optional<Double> getDouble(ResultSet rs, int index) {
-    try {
-      Double value = rs.getDouble(index);
-      if (!rs.wasNull()) {
-        return Optional.of(value);
-      }
-      return Optional.empty();
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to get double value", e);
-    }
-  }
-
   public static class Project {
     private final String uuid;
     private final String key;
@@ -345,15 +358,17 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     private final Long creationDate;
     private final Long analysisDate;
     private final List<String> tags;
-
-    public Project(String uuid, String key, String name, String qualifier, List<String> tags, Long creationDate, @Nullable Long analysisDate) {
-      this.uuid = uuid;
-      this.key = key;
-      this.name = name;
-      this.qualifier = qualifier;
-      this.tags = tags;
-      this.analysisDate = analysisDate;
-      this.creationDate = creationDate;
+    private final Long ncloc;
+
+    private Project(Builder builder) {
+      this.uuid = builder.uuid;
+      this.key = builder.key;
+      this.name = builder.name;
+      this.qualifier = builder.qualifier;
+      this.tags = builder.tags;
+      this.analysisDate = builder.analysisDate;
+      this.creationDate = builder.creationDate;
+      this.ncloc = builder.ncloc;
     }
 
     public String getUuid() {
@@ -384,6 +399,65 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     public Long getCreationDate() {
       return creationDate;
     }
+
+    public Optional<Long> getNcloc() {
+      return Optional.ofNullable(ncloc);
+    }
+
+    private static class Builder {
+      private String uuid;
+      private String key;
+      private String name;
+      private String qualifier;
+      private Long creationDate;
+      private Long analysisDate;
+      private List<String> tags;
+      private Long ncloc;
+
+      public Builder setUuid(String uuid) {
+        this.uuid = uuid;
+        return this;
+      }
+
+      public Builder setKey(String key) {
+        this.key = key;
+        return this;
+      }
+
+      public Builder setName(String name) {
+        this.name = name;
+        return this;
+      }
+
+      public Builder setQualifier(String qualifier) {
+        this.qualifier = qualifier;
+        return this;
+      }
+
+      public Builder setCreationDate(Long creationDate) {
+        this.creationDate = creationDate;
+        return this;
+      }
+
+      public Builder setAnalysisDate(@Nullable Long analysisDate) {
+        this.analysisDate = analysisDate;
+        return this;
+      }
+
+      public Builder setTags(List<String> tags) {
+        this.tags = tags;
+        return this;
+      }
+
+      public Builder setNcloc(@Nullable Long ncloc) {
+        this.ncloc = ncloc;
+        return this;
+      }
+
+      public Project build() {
+        return new Project(this);
+      }
+    }
   }
 
   public static class Measures {
index e462b9d611b61e605e066573c0b9190a89f7a6e7..862a0cae918ac383ff2f15f1b2fc2356551e7ff8 100644 (file)
@@ -209,7 +209,7 @@ public class ProjectMeasuresIndexerIT {
     BranchDto branchDto = db.components().insertProjectBranch(project1.getProjectDto());
     underTest.indexOnAnalysis(branchDto.getUuid());
 
-    assertThatIndexContainsOnly(new ProjectDto[] {});
+    assertThatIndexContainsOnly(new ProjectDto[]{});
   }
 
   @Test
@@ -266,8 +266,8 @@ public class ProjectMeasuresIndexerIT {
     BranchDto oldMainBranchDto = projectData.getMainBranchDto();
     BranchDto newMainBranchDto = db.components().insertProjectBranch(project);
     MetricDto nloc = db.measures().insertMetric(m -> m.setKey(CoreMetrics.NCLOC_KEY));
-    db.measures().insertLiveMeasure(oldMainBranchDto, nloc, e -> e.setValue(1d));
-    db.measures().insertLiveMeasure(newMainBranchDto, nloc, e -> e.setValue(2d));
+    db.measures().insertMeasure(oldMainBranchDto, e -> e.addValue(nloc.getKey(), 1d));
+    db.measures().insertMeasure(newMainBranchDto, e -> e.addValue(nloc.getKey(), 2d));
     indexProject(project, CREATION);
     assertThatProjectHasMeasure(project, CoreMetrics.NCLOC_KEY, 1d);
 
@@ -394,9 +394,9 @@ public class ProjectMeasuresIndexerIT {
     List<Map<String, Object>> documents = es.getDocuments(TYPE_PROJECT_MEASURES).stream().map(SearchHit::getSourceAsMap).toList();
 
     List<Tuple> expected = Arrays.stream(projectDatas).map(
-      projectData -> tuple(
-        projectData.getProjectDto().getKey(),
-        projectData.getProjectDto().getCreatedAt()))
+        projectData -> tuple(
+          projectData.getProjectDto().getKey(),
+          projectData.getProjectDto().getCreatedAt()))
       .toList();
     assertThat(documents)
       .extracting(hit -> hit.get("key"), hit -> stringDateToMilliseconds((String) hit.get("createdAt")))
index c73a37947f8de87f4a4d0db6dee22b3a4e35ffc5..ddfe67b49198cf4d86702e142dff620ded161ce6 100644 (file)
@@ -33,7 +33,6 @@ import java.util.stream.IntStream;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
 import org.apache.ibatis.session.ResultHandler;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,7 +50,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
 import org.sonar.db.entity.EntityDto;
-import org.sonar.db.measure.LiveMeasureDto;
+import org.sonar.db.measure.MeasureDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.db.property.PropertyDto;
@@ -284,16 +283,14 @@ public class SearchProjectsActionIT {
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
     ComponentDto project1 = insertProject(
       c -> c.setKey(KEY_PROJECT_EXAMPLE_001).setName("My Project 1"),
-      p -> p.setTagsString("finance, java"),
-      new Measure(coverage, c -> c.setValue(80d)));
+      p -> p.setTagsString("finance, java"), c -> c.addValue(coverage.getKey(), 80d));
 
     ComponentDto project2 = insertProject(
       c -> c.setKey(KEY_PROJECT_EXAMPLE_002).setName("My Project 2"),
-      new Measure(coverage, c -> c.setValue(90d)));
+      c -> c.addValue(coverage.getKey(), 90d));
     ComponentDto project3 = insertProject(
       c -> c.setKey(KEY_PROJECT_EXAMPLE_003).setName("My Project 3"),
-      p -> p.setTagsString("sales, offshore, java"),
-      new Measure(coverage, c -> c.setValue(20d)));
+      p -> p.setTagsString("sales, offshore, java"), c -> c.addValue(coverage.getKey(), 20d));
     addFavourite(db.components().getProjectDtoByMainBranch(project1));
     index();
 
@@ -316,9 +313,9 @@ public class SearchProjectsActionIT {
   @Test
   public void order_by_name_case_insensitive() {
     userSession.logIn();
-    insertProject(c -> c.setName("Maven"));
-    insertProject(c -> c.setName("Apache"));
-    insertProject(c -> c.setName("guava"));
+    insertProject(c -> c.setName("Maven"), null);
+    insertProject(c -> c.setName("Apache"), null);
+    insertProject(c -> c.setName("guava"), null);
     index();
 
     SearchProjectsWsResponse result = call(request);
@@ -330,7 +327,7 @@ public class SearchProjectsActionIT {
   @Test
   public void paginate_result() {
     userSession.logIn();
-    IntStream.rangeClosed(1, 9).forEach(i -> insertProject(c -> c.setName("PROJECT-" + i)));
+    IntStream.rangeClosed(1, 9).forEach(i -> insertProject(c -> c.setName("PROJECT-" + i), null));
     index();
 
     SearchProjectsWsResponse result = call(request.setPage(2).setPageSize(3));
@@ -362,15 +359,9 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType(INT.name()));
     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(
-      new Measure(coverage, c -> c.setValue(81d)),
-      new Measure(ncloc, c -> c.setValue(10_000d)));
-    ComponentDto project2 = insertProject(
-      new Measure(coverage, c -> c.setValue(80d)),
-      new Measure(ncloc, c -> c.setValue(10_000d)));
-    ComponentDto project3 = insertProject(
-      new Measure(coverage, c -> c.setValue(80d)),
-      new Measure(ncloc, c -> c.setValue(10_001d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 81d).addValue(ncloc.getKey(), 10_000d));
+    ComponentDto project2 = insertProject(c -> c.addValue(coverage.getKey(), 80d).addValue(ncloc.getKey(), 10_000d));
+    insertProject(c -> c.addValue(coverage.getKey(), 81d).addValue(ncloc.getKey(), 10_001d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("coverage <= 80 and ncloc <= 10000"));
@@ -382,9 +373,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_quality_gate() {
     userSession.logIn();
     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
-    ComponentDto project1 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
-    ComponentDto project2 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
-    ComponentDto project3 = insertProject(new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
+    ComponentDto project1 = insertProject(c -> c.addValue(qualityGateStatus.getKey(), "OK"));
+    ComponentDto project2 = insertProject(c -> c.addValue(qualityGateStatus.getKey(), "OK"));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), "ERROR"));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("alert_status = OK"));
@@ -399,16 +390,14 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
-    ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric,
-      c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
-    db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d));
-    ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=3;xoo=9")));
-    db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(12d));
-    ComponentDto project3 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("xoo=1")));
-    db.measures().insertLiveMeasure(project3, nclocMetric, m -> m.setValue(1d));
-    ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric,
-      c -> c.setValue(null).setData("<null>=1;java=5;xoo=13")));
-    db.measures().insertLiveMeasure(project4, nclocMetric, m -> m.setValue(19d));
+    ComponentDto project1 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "<null>=2;java=6;xoo=18"));
+    db.measures().insertMeasure(project1, m -> m.addValue(nclocMetric.getKey(), 26d));
+    ComponentDto project2 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "java=3;xoo=9"));
+    db.measures().insertMeasure(project2, m -> m.addValue(nclocMetric.getKey(), 12d));
+    ComponentDto project3 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "xoo=1"));
+    db.measures().insertMeasure(project3, m -> m.addValue(nclocMetric.getKey(), 1d));
+    ComponentDto project4 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "<null>=1;java=5;xoo=13"));
+    db.measures().insertMeasure(project4, m -> m.addValue(nclocMetric.getKey(), 19d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("languages IN (java, js, <null>)"));
@@ -422,9 +411,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_rating(String metricKey) {
     userSession.logIn();
     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(metricKey).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    ComponentDto project2 = insertProject(new Measure(ratingMetric, c -> c.setValue(2d)));
-    ComponentDto project3 = insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    ComponentDto project2 = insertProject(c -> c.addValue(ratingMetric.getKey(), 2d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 3d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter(metricKey + " = 2"));
@@ -437,9 +426,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_new_rating(String newMetricKey) {
     userSession.logIn();
     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(newMetricKey).setValueType(INT.name()));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    ComponentDto project2 = insertProject(new Measure(ratingMetric, c -> c.setValue(2d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    ComponentDto project2 = insertProject(c -> c.addValue(ratingMetric.getKey(), 2d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 3d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter(newMetricKey + " = 2"));
@@ -450,9 +439,9 @@ public class SearchProjectsActionIT {
   @Test
   public void filter_projects_by_tags() {
     userSession.logIn();
-    ComponentDto project1 = insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
-    insertProject(defaults(), p -> p.setTags(singletonList("marketing")));
-    ComponentDto project3 = insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
+    ComponentDto project1 = insertProject(defaults(), p -> p.setTags(asList("finance", "platform")), null);
+    insertProject(defaults(), p -> p.setTags(singletonList("marketing")), null);
+    ComponentDto project3 = insertProject(defaults(), p -> p.setTags(singletonList("offshore")), null);
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("tags in (finance, offshore)"));
@@ -464,9 +453,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_coverage() {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
-    ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(coverage, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(coverage.getKey(), 80d));
+    insertProject(c -> c.addValue(coverage.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(coverage.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("coverage <= 80"));
@@ -478,9 +467,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_new_coverage() {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_COVERAGE).setValueType("PERCENT"));
-    ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(coverage, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(coverage.getKey(), 80d));
+    insertProject(c -> c.addValue(coverage.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(coverage.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("new_coverage <= 80"));
@@ -492,9 +481,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_duplications() {
     userSession.logIn();
     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
-    ComponentDto project1 = insertProject(new Measure(duplications, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(duplications, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(duplications, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(duplications.getKey(), 80d));
+    insertProject(c -> c.addValue(duplications.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(duplications.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density <= 80"));
@@ -507,9 +496,9 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
-    ComponentDto project1 = insertProject(new Measure(coverage, c -> c.setValue(10d)));
-    ComponentDto project2 = insertProject(new Measure(duplications, c -> c.setValue(0d)));
-    ComponentDto project3 = insertProject(new Measure(duplications, c -> c.setValue(79d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(coverage.getKey(), 10d));
+    insertProject(c -> c.addValue(duplications.getKey(), 0d));
+    insertProject(c -> c.addValue(duplications.getKey(), 79d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA"));
@@ -522,7 +511,7 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
     MetricDto duplications = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
-    insertProject(new Measure(duplications, c -> c.setValue(10d)), new Measure(coverage, c -> c.setValue(50d)));
+    insertProject(c -> c.addValue(duplications.getKey(), 10d).addValue(coverage.getKey(), 50d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA"));
@@ -534,9 +523,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_new_duplications() {
     userSession.logIn();
     MetricDto newDuplications = db.measures().insertMetric(c -> c.setKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
-    ComponentDto project1 = insertProject(new Measure(newDuplications, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(newDuplications, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(newDuplications, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(newDuplications.getKey(), 80d));
+    insertProject(c -> c.addValue(newDuplications.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(newDuplications.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("new_duplicated_lines_density <= 80"));
@@ -548,9 +537,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_ncloc() {
     userSession.logIn();
     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(new Measure(ncloc, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(ncloc, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(ncloc, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(ncloc.getKey(), 80d));
+    insertProject(c -> c.addValue(ncloc.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(ncloc.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("ncloc <= 80"));
@@ -562,9 +551,9 @@ public class SearchProjectsActionIT {
   public void filter_projects_by_new_lines() {
     userSession.logIn();
     MetricDto newLines = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(new Measure(newLines, c -> c.setValue(80d)));
-    ComponentDto project2 = insertProject(new Measure(newLines, c -> c.setValue(85d)));
-    ComponentDto project3 = insertProject(new Measure(newLines, c -> c.setValue(10d)));
+    ComponentDto project1 = insertProject(c -> c.addValue(newLines.getKey(), 80d));
+    insertProject(c -> c.addValue(newLines.getKey(), 85d));
+    ComponentDto project3 = insertProject(c -> c.addValue(newLines.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("new_lines <= 80"));
@@ -575,10 +564,10 @@ public class SearchProjectsActionIT {
   @Test
   public void filter_projects_by_text_query() {
     userSession.logIn();
-    insertProject(c -> c.setKey("sonar-java").setName("Sonar Java"));
-    insertProject(c -> c.setKey("sonar-groovy").setName("Sonar Groovy"));
-    insertProject(c -> c.setKey("sonar-markdown").setName("Sonar Markdown"));
-    insertProject(c -> c.setKey("sonarqube").setName("Sonar Qube"));
+    insertProject(c -> c.setKey("sonar-java").setName("Sonar Java"), null);
+    insertProject(c -> c.setKey("sonar-groovy").setName("Sonar Groovy"), null);
+    insertProject(c -> c.setKey("sonar-markdown").setName("Sonar Markdown"), null);
+    insertProject(c -> c.setKey("sonarqube").setName("Sonar Qube"), null);
     index();
 
     assertThat(call(request.setFilter("query = \"Groovy\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar " +
@@ -595,7 +584,7 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     ComponentDto javaProject = insertProject();
     ComponentDto markDownProject = insertProject();
-    ComponentDto sonarQubeProject = insertProject();
+    insertProject();
     Stream.of(javaProject, markDownProject).forEach(c -> addFavourite(db.components().getProjectDtoByMainBranch(c)));
     index();
 
@@ -610,7 +599,7 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     ComponentDto javaProject = insertProject();
     ComponentDto markDownProject = insertProject();
-    ComponentDto sonarQubeProject = insertProject();
+    insertProject();
     Stream.of(javaProject, markDownProject).forEach(c -> addFavourite(db.components().getProjectDtoByMainBranch(c)));
     index();
 
@@ -627,7 +616,7 @@ public class SearchProjectsActionIT {
     userSession.anonymous();
     ComponentDto javaProject = insertProject();
     ComponentDto markDownProject = insertProject();
-    ComponentDto sonarQubeProject = insertProject();
+    insertProject();
     Stream.of(javaProject, markDownProject).forEach(c -> addFavourite(db.components().getProjectDtoByMainBranch(c)));
     index();
 
@@ -641,8 +630,8 @@ public class SearchProjectsActionIT {
   public void default_filter_projects_and_apps_by_editions(String[] qualifiers, Edition edition) {
     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
     userSession.logIn();
-    ComponentDto portfolio1 = insertPortfolio();
-    ComponentDto portfolio2 = insertPortfolio();
+    insertPortfolio();
+    insertPortfolio();
 
     ComponentDto application1 = insertApplication();
     ComponentDto application2 = insertApplication();
@@ -673,8 +662,8 @@ public class SearchProjectsActionIT {
     when(editionProviderMock.get()).thenReturn(Optional.empty());
     userSession.logIn();
 
-    ComponentDto portfolio1 = insertPortfolio();
-    ComponentDto portfolio2 = insertPortfolio();
+    insertPortfolio();
+    insertPortfolio();
 
     insertApplication();
     insertApplication();
@@ -751,8 +740,9 @@ public class SearchProjectsActionIT {
   public void fail_when_qualifier_filter_by_APP_set_when_ce_or_de(Edition edition) {
     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
     userSession.logIn();
+    RequestBuilder requestBuilder = request.setFilter("qualifiers = APP");
 
-    assertThatThrownBy(() -> call(request.setFilter("qualifiers = APP")))
+    assertThatThrownBy(() -> call(requestBuilder))
       .isInstanceOf(IllegalArgumentException.class);
   }
 
@@ -761,8 +751,9 @@ public class SearchProjectsActionIT {
   public void fail_when_qualifier_filter_invalid_when_ee_or_dc(Edition edition) {
     when(editionProviderMock.get()).thenReturn(Optional.of(edition));
     userSession.logIn();
+    RequestBuilder requestBuilder = request.setFilter("qualifiers = BLA");
 
-    assertThatThrownBy(() -> call(request.setFilter("qualifiers = BLA")))
+    assertThatThrownBy(() -> call(requestBuilder))
       .isInstanceOf(IllegalArgumentException.class);
   }
 
@@ -781,10 +772,10 @@ public class SearchProjectsActionIT {
   public void return_nloc_facet() {
     userSession.logIn();
     MetricDto ncloc = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
-    insertProject(new Measure(ncloc, c -> c.setValue(5d)));
-    insertProject(new Measure(ncloc, c -> c.setValue(5d)));
-    insertProject(new Measure(ncloc, c -> c.setValue(10_000d)));
-    insertProject(new Measure(ncloc, c -> c.setValue(500_001d)));
+    insertProject(c -> c.addValue(ncloc.getKey(), 5d));
+    insertProject(c -> c.addValue(ncloc.getKey(), 5d));
+    insertProject(c -> c.addValue(ncloc.getKey(), 10_000d));
+    insertProject(c -> c.addValue(ncloc.getKey(), 500_001d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NCLOC)));
@@ -806,9 +797,9 @@ public class SearchProjectsActionIT {
   public void return_new_lines_facet() {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name()));
-    insertProject(new Measure(coverage, c -> c.setValue(100d)));
-    insertProject(new Measure(coverage, c -> c.setValue(15_000d)));
-    insertProject(new Measure(coverage, c -> c.setValue(50_000d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 100d));
+    insertProject(c -> c.addValue(coverage.getKey(), 15_000d));
+    insertProject(c -> c.addValue(coverage.getKey(), 50_000d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_LINES_KEY)));
@@ -831,15 +822,14 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
-    ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric,
-      c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
-    db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d));
-    ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=5;xoo=19")));
-    db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(24d));
-    ComponentDto project3 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("xoo=1")));
-    db.measures().insertLiveMeasure(project3, nclocMetric, m -> m.setValue(1d));
-    ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=1;java=3;xoo=8")));
-    db.measures().insertLiveMeasure(project4, nclocMetric, m -> m.setValue(12d));
+    ComponentDto project1 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "<null>=2;java=6;xoo=18"));
+    db.measures().insertMeasure(project1, m -> m.addValue(nclocMetric.getKey(), 26d));
+    ComponentDto project2 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "java=5;xoo=19"));
+    db.measures().insertMeasure(project2, m -> m.addValue(nclocMetric.getKey(), 24d));
+    ComponentDto project3 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "xoo=1"));
+    db.measures().insertMeasure(project3, m -> m.addValue(nclocMetric.getKey(), 1d));
+    ComponentDto project4 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "<null>=1;java=3;xoo=8"));
+    db.measures().insertMeasure(project4, m -> m.addValue(nclocMetric.getKey(), 12d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_LANGUAGES)));
@@ -860,10 +850,10 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA"));
     MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6")));
-    db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(8d));
-    ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=5")));
-    db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(5d));
+    ComponentDto project1 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "<null>=2;java=6"));
+    db.measures().insertMeasure(project1, m -> m.addValue(nclocMetric.getKey(), 8d));
+    ComponentDto project2 = insertProject(c -> c.addValue(languagesDistributionMetric.getKey(), "java=5"));
+    db.measures().insertMeasure(project2, m -> m.addValue(nclocMetric.getKey(), 5d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("languages = xoo").setFacets(singletonList(FILTER_LANGUAGES)));
@@ -882,9 +872,9 @@ public class SearchProjectsActionIT {
   @Test
   public void return_tags_facet() {
     userSession.logIn();
-    insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
-    insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
-    insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
+    insertProject(defaults(), p -> p.setTags(asList("finance", "platform")), null);
+    insertProject(defaults(), p -> p.setTags(singletonList("offshore")), null);
+    insertProject(defaults(), p -> p.setTags(singletonList("offshore")), null);
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_TAGS)));
@@ -903,9 +893,9 @@ public class SearchProjectsActionIT {
   @Test
   public void return_tags_facet_with_tags_having_no_project_if_tags_is_in_filter() {
     userSession.logIn();
-    insertProject(defaults(), p -> p.setTags(asList("finance", "platform")));
-    insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
-    insertProject(defaults(), p -> p.setTags(singletonList("offshore")));
+    insertProject(defaults(), p -> p.setTags(asList("finance", "platform")), null);
+    insertProject(defaults(), p -> p.setTags(singletonList("offshore")), null);
+    insertProject(defaults(), p -> p.setTags(singletonList("offshore")), null);
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("tags = marketing").setFacets(singletonList(FILTER_TAGS)));
@@ -926,14 +916,14 @@ public class SearchProjectsActionIT {
   public void return_qualifiers_facet() {
     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
     userSession.logIn();
-    ComponentDto application1 = insertApplication();
-    ComponentDto application2 = insertApplication();
-    ComponentDto application3 = insertApplication();
-    ComponentDto application4 = insertApplication();
+    insertApplication();
+    insertApplication();
+    insertApplication();
+    insertApplication();
 
-    ComponentDto project1 = insertProject();
-    ComponentDto project2 = insertProject();
-    ComponentDto project3 = insertProject();
+    insertProject();
+    insertProject();
+    insertProject();
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_QUALIFIER)));
@@ -952,10 +942,10 @@ public class SearchProjectsActionIT {
   public void return_qualifiers_facet_with_qualifiers_having_no_project_if_qualifiers_is_in_filter() {
     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
     userSession.logIn();
-    ComponentDto application1 = insertApplication();
-    ComponentDto application2 = insertApplication();
-    ComponentDto application3 = insertApplication();
-    ComponentDto application4 = insertApplication();
+    insertApplication();
+    insertApplication();
+    insertApplication();
+    insertApplication();
     index();
 
     SearchProjectsWsResponse result = call(request.setFilter("qualifier = APP").setFacets(singletonList(FILTER_QUALIFIER)));
@@ -975,10 +965,10 @@ public class SearchProjectsActionIT {
   public void return_rating_facet(String ratingMetricKey) {
     userSession.logIn();
     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(ratingMetricKey).setValueType("RATING"));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 3d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 5d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ratingMetricKey)));
@@ -1001,11 +991,11 @@ public class SearchProjectsActionIT {
   public void return_software_quality_rating_facet(String ratingMetricKey) {
     userSession.logIn();
     MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(ratingMetricKey).setValueType("RATING"));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
-    insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 3d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 5d));
+    insertProject(c -> c.addValue(ratingMetric.getKey(), 5d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ratingMetricKey)));
@@ -1028,10 +1018,10 @@ public class SearchProjectsActionIT {
   public void return_new_rating_facet(String newRatingMetricKey) {
     userSession.logIn();
     MetricDto newRatingMetric = db.measures().insertMetric(c -> c.setKey(newRatingMetricKey).setValueType("RATING"));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(3d)));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(5d)));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 3d));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 5d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(newRatingMetricKey)));
@@ -1054,9 +1044,9 @@ public class SearchProjectsActionIT {
   public void return_new_software_quality_rating_facet(String newRatingMetricKey) {
     userSession.logIn();
     MetricDto newRatingMetric = db.measures().insertMetric(c -> c.setKey(newRatingMetricKey).setValueType("RATING"));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(1d)));
-    insertProject(new Measure(newRatingMetric, c -> c.setValue(3d)));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 1d));
+    insertProject(c -> c.addValue(newRatingMetric.getKey(), 3d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(newRatingMetricKey)));
@@ -1079,9 +1069,9 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT"));
     insertProject();
-    insertProject(new Measure(coverage, c -> c.setValue(80d)));
-    insertProject(new Measure(coverage, c -> c.setValue(85d)));
-    insertProject(new Measure(coverage, c -> c.setValue(10d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 80d));
+    insertProject(c -> c.addValue(coverage.getKey(), 85d));
+    insertProject(c -> c.addValue(coverage.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(COVERAGE)));
@@ -1105,9 +1095,9 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_COVERAGE).setValueType("PERCENT"));
     insertProject();
-    insertProject(new Measure(coverage, c -> c.setValue(80d)));
-    insertProject(new Measure(coverage, c -> c.setValue(85d)));
-    insertProject(new Measure(coverage, c -> c.setValue(10d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 80d));
+    insertProject(c -> c.addValue(coverage.getKey(), 85d));
+    insertProject(c -> c.addValue(coverage.getKey(), 10d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_COVERAGE)));
@@ -1130,9 +1120,9 @@ public class SearchProjectsActionIT {
   public void return_duplications_facet() {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
-    insertProject(new Measure(coverage, c -> c.setValue(10d)));
-    insertProject(new Measure(coverage, c -> c.setValue(15d)));
-    insertProject(new Measure(coverage, c -> c.setValue(5d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 10d));
+    insertProject(c -> c.addValue(coverage.getKey(), 15d));
+    insertProject(c -> c.addValue(coverage.getKey(), 5d));
     insertProject();
     index();
 
@@ -1157,9 +1147,9 @@ public class SearchProjectsActionIT {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setValueType("PERCENT"));
     insertProject();
-    insertProject(new Measure(coverage, c -> c.setValue(10d)));
-    insertProject(new Measure(coverage, c -> c.setValue(15d)));
-    insertProject(new Measure(coverage, c -> c.setValue(5d)));
+    insertProject(c -> c.addValue(coverage.getKey(), 10d));
+    insertProject(c -> c.addValue(coverage.getKey(), 15d));
+    insertProject(c -> c.addValue(coverage.getKey(), 5d));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_DUPLICATED_LINES_DENSITY_KEY)));
@@ -1182,9 +1172,9 @@ public class SearchProjectsActionIT {
   public void return_quality_gate_facet() {
     userSession.logIn();
     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name()));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null)));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.ERROR.name()));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.ERROR.name()));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.OK.name()));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY)));
@@ -1203,9 +1193,9 @@ public class SearchProjectsActionIT {
   public void return_quality_gate_facet_without_warning_when_no_projects_in_warning() {
     userSession.logIn();
     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name()));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null)));
-    insertProject(new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null)));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.ERROR.name()));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.ERROR.name()));
+    insertProject(c -> c.addValue(qualityGateStatus.getKey(), Metric.Level.OK.name()));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY)));
@@ -1223,10 +1213,10 @@ public class SearchProjectsActionIT {
   @Test
   public void default_sort_is_by_ascending_name() {
     userSession.logIn();
-    insertProject(c -> c.setName("Sonar Java"));
-    insertProject(c -> c.setName("Sonar Groovy"));
-    insertProject(c -> c.setName("Sonar Markdown"));
-    insertProject(c -> c.setName("Sonar Qube"));
+    insertProject(c -> c.setName("Sonar Java"), null);
+    insertProject(c -> c.setName("Sonar Groovy"), null);
+    insertProject(c -> c.setName("Sonar Markdown"), null);
+    insertProject(c -> c.setName("Sonar Qube"), null);
     index();
 
     SearchProjectsWsResponse result = call(request);
@@ -1238,10 +1228,10 @@ public class SearchProjectsActionIT {
   @Test
   public void sort_by_name() {
     userSession.logIn();
-    insertProject(c -> c.setName("Sonar Java"));
-    insertProject(c -> c.setName("Sonar Groovy"));
-    insertProject(c -> c.setName("Sonar Markdown"));
-    insertProject(c -> c.setName("Sonar Qube"));
+    insertProject(c -> c.setName("Sonar Java"), null);
+    insertProject(c -> c.setName("Sonar Groovy"), null);
+    insertProject(c -> c.setName("Sonar Markdown"), null);
+    insertProject(c -> c.setName("Sonar Qube"), null);
     index();
 
     assertThat(call(request.setSort("name").setAsc(true)).getComponentsList()).extracting(Component::getName)
@@ -1254,10 +1244,10 @@ public class SearchProjectsActionIT {
   public void sort_by_coverage_then_by_name() {
     userSession.logIn();
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType(INT.name()));
-    ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(coverage, c -> c.setValue(81d)));
-    ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(coverage, c -> c.setValue(81d)));
-    ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(coverage, c -> c.setValue(80d)));
-    ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(coverage, c -> c.setValue(80d)));
+    ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), c -> c.addValue(coverage.getKey(), 81d));
+    ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), c -> c.addValue(coverage.getKey(), 81d));
+    ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), c -> c.addValue(coverage.getKey(), 80d));
+    ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), c -> c.addValue(coverage.getKey(), 80d));
     index();
 
     assertThat(call(request.setSort(COVERAGE).setAsc(true)).getComponentsList()).extracting(Component::getKey)
@@ -1270,14 +1260,10 @@ public class SearchProjectsActionIT {
   public void sort_by_quality_gate_then_by_name() {
     userSession.logIn();
     MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
-    ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData(
-      "ERROR")));
-    ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData(
-      "ERROR")));
-    ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData(
-      "OK")));
-    ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus,
-      c -> c.setValue(null).setData("OK")));
+    ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), c -> c.addValue(qualityGateStatus.getKey(), "ERROR"));
+    ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), c -> c.addValue(qualityGateStatus.getKey(), "ERROR"));
+    ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), c -> c.addValue(qualityGateStatus.getKey(), "OK"));
+    ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), c -> c.addValue(qualityGateStatus.getKey(), "OK"));
     index();
 
     assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getKey)
@@ -1338,7 +1324,6 @@ public class SearchProjectsActionIT {
         tuple(mainBranch3.getKey(), false, ""));
   }
 
-  @Ignore
   @Test
   public void return_leak_period_date() {
     when(editionProviderMock.get()).thenReturn(Optional.of(Edition.ENTERPRISE));
@@ -1355,9 +1340,8 @@ public class SearchProjectsActionIT {
     authorizationIndexerTester.allowOnlyAnyone(db.components().getProjectDtoByMainBranch(project3));
 
     MetricDto leakProjects = db.measures().insertMetric(c -> c.setKey(LEAK_PROJECTS_KEY).setValueType(DATA.name()));
-    ComponentDto application1 = insertApplication(
-      new Measure(leakProjects, c -> c.setData("{\"leakProjects\":[{\"id\": 1, \"leak\":20000000000}, {\"id\": 2, " +
-        "\"leak\":10000000000}]}")));
+    ComponentDto application1 = insertApplication(m -> m.addValue(leakProjects.getKey(),
+      ("{\"leakProjects\":[{\"id\": 1, \"leak\":20000000000}, {\"id\": 2, \"leak\":10000000000}]}")));
     db.components().insertSnapshot(application1);
 
     authorizationIndexerTester.allowOnlyAnyone(db.components().getProjectDtoByMainBranch(application1));
@@ -1478,29 +1462,41 @@ public class SearchProjectsActionIT {
 
   private int projectCount = 0;
 
-  private ComponentDto insertProject(Measure... measures) {
-    return insertProject(defaults(), projectDto -> projectDto.setName("project_" + projectCount++), measures);
+  private ComponentDto insertProject() {
+    return insertProject(defaults(), projectDto -> projectDto.setName("project_" + projectCount++), null);
+  }
+
+  private ComponentDto insertProject(Consumer<MeasureDto> measureConsumer) {
+    return insertProject(defaults(), projectDto -> projectDto.setName("project_" + projectCount++), measureConsumer);
   }
 
-  private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Measure... measures) {
-    return insertProject(componentConsumer, defaults(), measures);
+  private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, @Nullable Consumer<MeasureDto> measureConsumer) {
+    return insertProject(componentConsumer, defaults(), measureConsumer);
   }
 
-  private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Consumer<ProjectDto> projectConsumer, Measure... measures) {
+  private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Consumer<ProjectDto> projectConsumer, @Nullable Consumer<MeasureDto> measureConsumer) {
     ComponentDto project = db.components().insertPublicProject(componentConsumer, projectConsumer).getMainBranchComponent();
-    Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(project, m.metric, m.consumer));
+    if (measureConsumer != null) {
+      db.measures().insertMeasure(project, measureConsumer);
+    }
     return project;
   }
 
   private int applicationCount = 0;
 
-  private ComponentDto insertApplication(Measure... measures) {
-    return insertApplication(componentDto -> componentDto.setName("app_" + applicationCount++), measures);
+  private ComponentDto insertApplication() {
+    return insertApplication(componentDto -> componentDto.setName("app_" + applicationCount++), null);
   }
 
-  private ComponentDto insertApplication(Consumer<ComponentDto> componentConsumer, Measure... measures) {
+  private ComponentDto insertApplication(Consumer<MeasureDto> measureConsumer) {
+    return insertApplication(componentDto -> componentDto.setName("app_" + applicationCount++), measureConsumer);
+  }
+
+  private ComponentDto insertApplication(Consumer<ComponentDto> componentConsumer, @Nullable Consumer<MeasureDto> measureConsumer) {
     ComponentDto application = db.components().insertPublicApplication(componentConsumer).getMainBranchComponent();
-    Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(application, m.metric, m.consumer));
+    if (measureConsumer != null) {
+      db.measures().insertMeasure(application, measureConsumer);
+    }
     return application;
   }
 
@@ -1518,16 +1514,6 @@ public class SearchProjectsActionIT {
     return db.components().insertPublicPortfolio();
   }
 
-  private static class Measure {
-    private final MetricDto metric;
-    private final Consumer<LiveMeasureDto> consumer;
-
-    public Measure(MetricDto metric, Consumer<LiveMeasureDto> consumer) {
-      this.metric = metric;
-      this.consumer = consumer;
-    }
-  }
-
   private static <T> Consumer<T> defaults() {
     return t -> {
     };