]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20021, SONAR-20198 Fix CCT filtering and facets when both attributes of nested...
authorJacek <jacek.poreda@sonarsource.com>
Fri, 25 Aug 2023 12:55:45 +0000 (14:55 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 25 Aug 2023 20:02:41 +0000 (20:02 +0000)
server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java
server/sonar-server-common/src/main/java/org/sonar/server/es/StickyFacetBuilder.java
server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java

index 32f58812bbcbef0c7e2a91dbb1015dd890406991..85bab8dbf45583db13ae2c8b2781b475a18916cf 100644 (file)
@@ -519,7 +519,7 @@ public class RuleIndexIT {
 
     List<RuleDto> rules = new ArrayList<>();
 
-    //Creation of one rule for each standard security category defined (except other)
+    // Creation of one rule for each standard security category defined (except other)
     for (Map.Entry<SecurityStandards.SQCategory, Set<String>> sqCategorySetEntry : SecurityStandards.CWES_BY_SQ_CATEGORY.entrySet()) {
       rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())), r -> r.setType(SECURITY_HOTSPOT)));
     }
@@ -797,7 +797,6 @@ public class RuleIndexIT {
     SearchIdResult result1 = underTest.search(query, new SearchOptions());
     assertThat(result1.getUuids()).isEmpty();
 
-
     query = new RuleQuery();
     query.setCleanCodeAttributesCategories(List.of(CleanCodeAttribute.FOCUSED.getAttributeCategory().name()));
 
@@ -816,7 +815,6 @@ public class RuleIndexIT {
     SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), new SearchOptions());
     assertThat(result1.getUuids()).isEmpty();
 
-
     query = new RuleQuery();
     SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())), new SearchOptions());
     assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
@@ -832,7 +830,6 @@ public class RuleIndexIT {
     SearchIdResult result1 = underTest.search(query.setImpactSeverities(List.of(Severity.MEDIUM.name())), new SearchOptions());
     assertThat(result1.getUuids()).isEmpty();
 
-
     query = new RuleQuery();
     SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.HIGH.name())), new SearchOptions());
     assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
@@ -908,6 +905,35 @@ public class RuleIndexIT {
         entry("RELIABILITY", 0L));
   }
 
+  @Test
+  public void search_whenFilteringOnSeverityAndSoftwareQuality_shouldReturnFacet() {
+    ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH).setUuid("uuid");
+    ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW).setUuid("uuid2");
+    ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.LOW).setUuid("uuid3");
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3)));
+    index();
+
+    RuleQuery query = new RuleQuery();
+
+    SearchIdResult result = underTest.search(
+      query.setImpactSeverities(Set.of(Severity.LOW.name())).setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())),
+      new SearchOptions().addFacets(List.of("impactSoftwareQualities", "impactSeverities")));
+
+    assertThat(result.getFacets().getAll()).hasSize(2);
+    assertThat(result.getFacets().getAll().get("impactSoftwareQualities"))
+      .containsOnly(
+        entry("SECURITY", 0L),
+        entry("MAINTAINABILITY", 1L),
+        entry("RELIABILITY", 1L));
+
+    assertThat(result.getFacets().getAll().get("impactSeverities"))
+      .containsOnly(
+        entry("HIGH", 1L),
+        entry("MEDIUM", 0L),
+        entry("LOW", 1L));
+  }
+
   @Test
   public void search_should_support_severity_facet() {
     ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
index 798de162480219b5f641d0cede22383f4b5a0906..d84c469860d2cb0ba4c343b31202fa93dec26eee 100644 (file)
@@ -100,8 +100,8 @@ public class StickyFacetBuilder {
       .subAggregation(facetTopAggregation);
   }
 
-  public AggregationBuilder buildTopAggregationStickyFacet(String fieldName, String facetName, AggregationBuilder additionalAggregationFilter) {
-    BoolQueryBuilder facetFilter = getStickyFacetFilter(fieldName);
+  public AggregationBuilder buildNestedAggregationStickyFacet(String parentFieldName, String childFieldName, String facetName, AggregationBuilder additionalAggregationFilter) {
+    BoolQueryBuilder facetFilter = getStickyFacetFilter(parentFieldName + "." + childFieldName, parentFieldName);
     return AggregationBuilders
       .global(facetName)
       .subAggregation(AggregationBuilders
index fab8e5dfd4c25806517300756d4d45412da21a29..61b3207172e252ffdc1260632f065f0d3b969762 100644 (file)
@@ -92,6 +92,7 @@ import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.ENGLISH_H
 import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SEARCH_GRAMS_ANALYZER;
 import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SEARCH_WORDS_ANALYZER;
 import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SORTABLE_ANALYZER;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACTS;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_UUID;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY;
@@ -120,6 +121,8 @@ import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_TAGS;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_TEMPLATE_KEY;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_TYPE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_UPDATED_AT;
+import static org.sonar.server.rule.index.RuleIndexDefinition.SUB_FIELD_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.SUB_FIELD_SOFTWARE_QUALITY;
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
 
@@ -236,9 +239,9 @@ public class RuleIndex {
     BoolQueryBuilder textQuery = boolQuery();
     JavaTokenizer.split(queryText)
       .stream().map(token -> boolQuery().should(
-          matchQuery(
-            SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME),
-            StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F))
+        matchQuery(
+          SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME),
+          StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F))
         .should(
           matchPhraseQuery(
             ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION),
@@ -256,7 +259,7 @@ public class RuleIndex {
 
   private static QueryBuilder termQuery(String field, String query, float boost) {
     return QueryBuilders.multiMatchQuery(query,
-        field, SEARCH_WORDS_ANALYZER.subField(field))
+      field, SEARCH_WORDS_ANALYZER.subField(field))
       .operator(Operator.AND)
       .boost(boost);
   }
@@ -375,23 +378,32 @@ public class RuleIndex {
   }
 
   private static void addImpactFilters(RuleQuery query, Map<String, QueryBuilder> allFilters) {
-    if (isNotEmpty(query.getImpactSoftwareQualities())) {
+    if (isEmpty(query.getImpactSoftwareQualities()) && isEmpty(query.getImpactSeverities())) {
+      return;
+    }
+    if (isNotEmpty(query.getImpactSoftwareQualities()) && isEmpty(query.getImpactSeverities())) {
       allFilters.put(
         FIELD_RULE_IMPACT_SOFTWARE_QUALITY,
         nestedQuery(
           FIELD_RULE_IMPACTS,
           termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, query.getImpactSoftwareQualities()),
           ScoreMode.Avg));
+      return;
     }
 
-    if (isNotEmpty(query.getImpactSeverities())) {
+    if (isNotEmpty(query.getImpactSeverities()) && isEmpty(query.getImpactSoftwareQualities())) {
       allFilters.put(
         FIELD_RULE_IMPACT_SEVERITY,
         nestedQuery(
           FIELD_RULE_IMPACTS,
           termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities()),
           ScoreMode.Avg));
+      return;
     }
+    BoolQueryBuilder impactsFilter = boolQuery()
+      .filter(termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, query.getImpactSoftwareQualities()))
+      .filter(termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities()));
+    allFilters.put(FIELD_RULE_IMPACTS, nestedQuery(FIELD_ISSUE_IMPACTS, impactsFilter, ScoreMode.Avg));
   }
 
   private static void addSecurityStandardFilter(Map<String, QueryBuilder> filters, String key, Collection<String> values) {
@@ -524,52 +536,67 @@ public class RuleIndex {
       return;
     }
 
-    Function<SoftwareQuality, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery()
-      .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, softwareQuality.name()));
+    Function<String, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery()
+      .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, softwareQuality));
 
     FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(SoftwareQuality.values())
       .map(softwareQuality -> new FiltersAggregator.KeyedFilter(softwareQuality.name(),
-        isNotEmpty(query.getImpactSeverities()) ? mainQuery.apply(softwareQuality)
-          .filter(termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities())) : mainQuery.apply(softwareQuality)))
+        buildSoftwareQualityFacetFilter(query, mainQuery, softwareQuality.name())))
       .toArray(FiltersAggregator.KeyedFilter[]::new);
 
     NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nested_" + FACET_IMPACT_SOFTWARE_QUALITY, FIELD_RULE_IMPACTS)
       .subAggregation(filters(FACET_IMPACT_SOFTWARE_QUALITY, keyedFilters));
 
-    AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildTopAggregationStickyFacet(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, FACET_IMPACT_SOFTWARE_QUALITY,
-      nestedAggregationBuilder);
+    AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildNestedAggregationStickyFacet(FIELD_RULE_IMPACTS, SUB_FIELD_SOFTWARE_QUALITY,
+      FACET_IMPACT_SOFTWARE_QUALITY, nestedAggregationBuilder);
 
     aggregations.put(FACET_IMPACT_SOFTWARE_QUALITY, aggregationBuilder);
   }
 
+  private static BoolQueryBuilder buildSoftwareQualityFacetFilter(RuleQuery query, Function<String, BoolQueryBuilder> mainQuery, String value) {
+    BoolQueryBuilder boolQueryBuilder = mainQuery.apply(value);
+    if (isNotEmpty(query.getImpactSeverities())) {
+      return boolQueryBuilder.filter(termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities()));
+    }
+    return boolQueryBuilder;
+  }
+
   private static void addImpactSeverityFacetIfNeeded(SearchOptions options, RuleQuery query, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) {
     if (!options.getFacets().contains(FACET_IMPACT_SEVERITY)) {
       return;
     }
 
-    Function<org.sonar.api.issue.impact.Severity, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery()
-      .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SEVERITY, softwareQuality.name()));
+    Function<String, BoolQueryBuilder> mainQuery = severity -> boolQuery()
+      .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SEVERITY, severity));
 
     FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(org.sonar.api.issue.impact.Severity.values())
       .map(severity -> new FiltersAggregator.KeyedFilter(severity.name(),
-        isNotEmpty(query.getImpactSoftwareQualities()) ? mainQuery.apply(severity).filter(termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, query.getImpactSoftwareQualities()))
-          : mainQuery.apply(severity)))
+        buildSeverityFacetFilter(query, mainQuery, severity.name())))
       .toArray(FiltersAggregator.KeyedFilter[]::new);
 
     NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nested_" + FACET_IMPACT_SEVERITY, FIELD_RULE_IMPACTS)
       .subAggregation(filters(FACET_IMPACT_SEVERITY, keyedFilters).subAggregation(reverseNested("reverse_nested_" + FIELD_RULE_IMPACT_SEVERITY)));
 
-    AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildTopAggregationStickyFacet(FIELD_RULE_IMPACT_SEVERITY, FACET_IMPACT_SEVERITY, nestedAggregationBuilder);
+    AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildNestedAggregationStickyFacet(FIELD_RULE_IMPACTS, SUB_FIELD_SEVERITY,
+      FACET_IMPACT_SEVERITY, nestedAggregationBuilder);
 
     aggregations.put(FACET_IMPACT_SEVERITY, aggregationBuilder);
   }
 
+  private static BoolQueryBuilder buildSeverityFacetFilter(RuleQuery query, Function<String, BoolQueryBuilder> mainQuery, String value) {
+    BoolQueryBuilder boolQueryBuilder = mainQuery.apply(value);
+    if (isNotEmpty(query.getImpactSoftwareQualities())) {
+      return boolQueryBuilder.filter(termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, query.getImpactSoftwareQualities()));
+    }
+    return boolQueryBuilder;
+  }
+
   private static Function<TermsAggregationBuilder, AggregationBuilder> filterSecurityCategories() {
     return termsAggregation -> AggregationBuilders.filter(
-        "filter_by_rule_types_" + termsAggregation.getName(),
-        termsQuery(FIELD_RULE_TYPE,
-          VULNERABILITY.name(),
-          SECURITY_HOTSPOT.name()))
+      "filter_by_rule_types_" + termsAggregation.getName(),
+      termsQuery(FIELD_RULE_TYPE,
+        VULNERABILITY.name(),
+        SECURITY_HOTSPOT.name()))
       .subAggregation(termsAggregation);
   }
 
@@ -685,9 +712,9 @@ public class RuleIndex {
 
   private static String appendSortSuffixIfNeeded(String field) {
     return field +
-           ((field.equals(FIELD_RULE_NAME) || field.equals(FIELD_RULE_KEY))
-             ? ("." + SORTABLE_ANALYZER.getSubFieldSuffix())
-             : "");
+      ((field.equals(FIELD_RULE_NAME) || field.equals(FIELD_RULE_KEY))
+        ? ("." + SORTABLE_ANALYZER.getSubFieldSuffix())
+        : "");
   }
 
   private static void setPagination(SearchOptions options, SearchSourceBuilder esSearch) {
@@ -731,4 +758,8 @@ public class RuleIndex {
   private static boolean isNotEmpty(@Nullable Collection<?> list) {
     return list != null && !list.isEmpty();
   }
+
+  private static boolean isEmpty(@Nullable Collection<?> list) {
+    return list == null || list.isEmpty();
+  }
 }
index d9d19df159871e11209d7342f3749abb423b0bc2..08cf93e12a41c3fdfe4d8caad5c297d580779f0e 100644 (file)
@@ -86,6 +86,7 @@ import org.sonar.server.es.searchrequest.RequestFiltersComputer.AllFilters;
 import org.sonar.server.es.searchrequest.SimpleFieldTopAggregationDefinition;
 import org.sonar.server.es.searchrequest.SubAggregationHelper;
 import org.sonar.server.es.searchrequest.TopAggregationDefinition;
+import org.sonar.server.es.searchrequest.TopAggregationDefinition.FilterScope;
 import org.sonar.server.es.searchrequest.TopAggregationDefinition.SimpleFieldFilterScope;
 import org.sonar.server.es.searchrequest.TopAggregationHelper;
 import org.sonar.server.issue.index.IssueQuery.PeriodStart;
@@ -256,9 +257,8 @@ public class IssueIndex {
 
   public enum Facet {
     SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()),
-    IMPACT_SOFTWARE_QUALITY(PARAM_IMPACT_SOFTWARE_QUALITIES, FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, STICKY, SoftwareQuality.values().length),
-    IMPACT_SEVERITY(PARAM_IMPACT_SEVERITIES, FIELD_ISSUE_IMPACT_SEVERITY, STICKY,
-      org.sonar.api.issue.impact.Severity.values().length),
+    IMPACT_SOFTWARE_QUALITY(PARAM_IMPACT_SOFTWARE_QUALITIES, FIELD_ISSUE_IMPACTS, STICKY),
+    IMPACT_SEVERITY(PARAM_IMPACT_SEVERITIES, FIELD_ISSUE_IMPACTS, STICKY),
     CLEAN_CODE_ATTRIBUTE_CATEGORY(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES, FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, STICKY, CleanCodeAttributeCategory.values().length),
     STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, STICKY, Issue.STATUSES.size()),
     // Resolutions facet returns one more element than the number of resolutions to take into account unresolved issues
@@ -286,7 +286,7 @@ public class IssueIndex {
     CODE_VARIANTS(PARAM_CODE_VARIANTS, FIELD_ISSUE_CODE_VARIANTS, STICKY, MAX_FACET_SIZE);
 
     private final String name;
-    private final SimpleFieldTopAggregationDefinition topAggregation;
+    private final TopAggregationDefinition<FilterScope> topAggregation;
     private final Integer numberOfTerms;
 
     Facet(String name, String fieldName, boolean sticky, int numberOfTerms) {
@@ -309,11 +309,11 @@ public class IssueIndex {
       return topAggregation.getFilterScope().getFieldName();
     }
 
-    public TopAggregationDefinition.FilterScope getFilterScope() {
+    public FilterScope getFilterScope() {
       return topAggregation.getFilterScope();
     }
 
-    public SimpleFieldTopAggregationDefinition getTopAggregationDef() {
+    public TopAggregationDefinition<FilterScope> getTopAggregationDef() {
       return topAggregation;
     }
 
@@ -602,7 +602,8 @@ public class IssueIndex {
     if (query.impactSoftwareQualities().isEmpty() && query.impactSeverities().isEmpty()) {
       return;
     }
-    if (!query.impactSoftwareQualities().isEmpty()) {
+
+    if (!query.impactSoftwareQualities().isEmpty() && query.impactSeverities().isEmpty()) {
       allFilters.addFilter(
         FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY,
         IMPACT_SOFTWARE_QUALITY.getFilterScope(),
@@ -610,9 +611,10 @@ public class IssueIndex {
           FIELD_ISSUE_IMPACTS,
           termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities()),
           ScoreMode.Avg));
+      return;
     }
 
-    if (!query.impactSeverities().isEmpty()) {
+    if (!query.impactSeverities().isEmpty() && query.impactSoftwareQualities().isEmpty()) {
       allFilters.addFilter(
         FIELD_ISSUE_IMPACT_SEVERITY,
         IMPACT_SEVERITY.getFilterScope(),
@@ -620,7 +622,15 @@ public class IssueIndex {
           FIELD_ISSUE_IMPACTS,
           termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities()),
           ScoreMode.Avg));
+      return;
     }
+
+    BoolQueryBuilder impactsFilter = boolQuery()
+      .filter(termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities()))
+      .filter(termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities()));
+
+    allFilters.addFilter(FIELD_ISSUE_IMPACTS, new SimpleFieldFilterScope(FIELD_ISSUE_IMPACTS),
+      nestedQuery(FIELD_ISSUE_IMPACTS, impactsFilter, ScoreMode.Avg));
   }
 
   private static void addComponentRelatedFilters(IssueQuery query, AllFilters filters) {
@@ -914,7 +924,7 @@ public class IssueIndex {
       .map(softwareQuality -> new FiltersAggregator.KeyedFilter(softwareQuality.name(),
         query.impactSeverities().isEmpty() ? mainQuery.apply(softwareQuality)
           : mainQuery.apply(softwareQuality)
-            .filter(termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities()))))
+          .filter(termsQuery(FIELD_ISSUE_IMPACT_SEVERITY, query.impactSeverities()))))
       .toArray(FiltersAggregator.KeyedFilter[]::new);
 
     AggregationBuilder aggregation = aggregationHelper.buildTopAggregation(
@@ -938,7 +948,7 @@ public class IssueIndex {
       .map(severity -> new FiltersAggregator.KeyedFilter(severity.name(),
         query.impactSoftwareQualities().isEmpty() ? mainQuery.apply(severity)
           : mainQuery.apply(severity)
-            .filter(termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities()))))
+          .filter(termsQuery(FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, query.impactSoftwareQualities()))))
       .toArray(FiltersAggregator.KeyedFilter[]::new);
 
     AggregationBuilder aggregation = aggregationHelper.buildTopAggregation(
index 1171923de9757ad3096d0abfef89a4bd6d617e13..2f15ebbde6350c4380a8afb5221296e1cf02b430 100644 (file)
@@ -715,6 +715,11 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
       entry("RELIABILITY", 0L),
       entry("SECURITY", 0L));
 
+    assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), "impactSoftwareQualities",
+      entry("MAINTAINABILITY", 3L),
+      entry("RELIABILITY", 2L),
+      entry("SECURITY", 0L));
+
     assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(Severity.MEDIUM.name())), "impactSoftwareQualities",
       entry("MAINTAINABILITY", 0L),
       entry("RELIABILITY", 1L),
@@ -733,6 +738,33 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
       entry("SECURITY", 0L));
   }
 
+  @Test
+  public void search_whenFilteredOnSeverityAndSoftwareQuality_shouldReturnImpactFacets() {
+    ComponentDto project = newPrivateProjectDto();
+    ComponentDto file = newFileDto(project);
+
+    indexIssues(
+      newDoc("I1", project.uuid(), file).setImpacts(Map.of(
+        MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW,
+        RELIABILITY, org.sonar.api.issue.impact.Severity.LOW)));
+
+    assertThatFacetHasOnly(IssueQuery.builder()
+      .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
+      .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
+      "impactSoftwareQualities",
+      entry("MAINTAINABILITY", 1L),
+      entry("RELIABILITY", 1L),
+      entry("SECURITY", 0L));
+
+    assertThatFacetHasOnly(IssueQuery.builder()
+      .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
+      .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
+      "impactSeverities",
+      entry("HIGH", 0L),
+      entry("MEDIUM", 0L),
+      entry("LOW", 1L));
+  }
+
   @Test
   public void search_shouldReturnImpactSeverityFacet() {
     ComponentDto project = newPrivateProjectDto();