summaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-es
diff options
context:
space:
mode:
authorJacek <jacek.poreda@sonarsource.com>2020-03-19 13:08:00 +0100
committersonartech <sonartech@sonarsource.com>2020-03-30 20:03:43 +0000
commit2a1f7ca4e6db457852042e96d1379d8ebd547792 (patch)
treec0a8de2513b8669eaf9ee0a3b33df4fbcb6f0651 /server/sonar-webserver-es
parent8abccea1b9f0fc662bd11ac5f101c0b942bd0eac (diff)
downloadsonarqube-2a1f7ca4e6db457852042e96d1379d8ebd547792.tar.gz
sonarqube-2a1f7ca4e6db457852042e96d1379d8ebd547792.zip
SONAR-13104 drop global aggregation from issues ES searches
Diffstat (limited to 'server/sonar-webserver-es')
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java604
1 files changed, 336 insertions, 268 deletions
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index ef60ff462ba..3713c2a3642 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -20,13 +20,11 @@
package org.sonar.server.issue.index;
import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -34,6 +32,7 @@ import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
@@ -72,18 +71,24 @@ import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.es.BaseDoc;
import org.sonar.server.es.EsClient;
import org.sonar.server.es.EsUtils;
import org.sonar.server.es.IndexType;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.Sorting;
-import org.sonar.server.es.StickyFacetBuilder;
+import org.sonar.server.es.searchrequest.RequestFiltersComputer;
+import org.sonar.server.es.searchrequest.RequestFiltersComputer.AllFilters;
+import org.sonar.server.es.searchrequest.SubAggregationHelper;
+import org.sonar.server.es.searchrequest.TermTopAggregationDef;
+import org.sonar.server.es.searchrequest.TopAggregationDef;
+import org.sonar.server.es.searchrequest.TopAggregationDefinition;
+import org.sonar.server.es.searchrequest.TopAggregationHelper;
import org.sonar.server.issue.index.IssueQuery.PeriodStart;
import org.sonar.server.permission.index.AuthorizationDoc;
import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.security.SecurityStandards;
-import org.sonar.server.security.SecurityStandards.SQCategory;
import org.sonar.server.user.UserSession;
import org.sonar.server.view.index.ViewIndexDefinition;
@@ -103,6 +108,8 @@ import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.server.es.BaseDoc.epochMillisToEpochSeconds;
import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
import static org.sonar.server.es.IndexType.FIELD_INDEX_TYPE;
+import static org.sonar.server.es.searchrequest.TopAggregationHelper.NO_EXTRA_FILTER;
+import static org.sonar.server.es.searchrequest.TopAggregationHelper.NO_OTHER_SUBAGGREGATION;
import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNED_TO_ME;
import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNEES;
import static org.sonar.server.issue.index.IssueIndex.Facet.AUTHOR;
@@ -211,37 +218,45 @@ public class IssueIndex {
.filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_REVIEWED))
.filter(termQuery(FIELD_ISSUE_RESOLUTION, Issue.RESOLUTION_FIXED));
+ private static final boolean STICKY = true;
+ private static final boolean NON_STICKY = false;
+ private static final Object[] NO_SELECTED_VALUES = {0};
+ private static final TopAggregationDefinition EFFORT_TOP_AGGREGATION = new TopAggregationDef(FIELD_ISSUE_EFFORT, NON_STICKY);
+
public enum Facet {
- SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, Severity.ALL.size()),
- STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, Issue.STATUSES.size()),
+ SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()),
+ 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
- RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, Issue.RESOLUTIONS.size() + 1),
- TYPES(PARAM_TYPES, FIELD_ISSUE_TYPE, RuleType.values().length),
- LANGUAGES(PARAM_LANGUAGES, FIELD_ISSUE_LANGUAGE, MAX_FACET_SIZE),
- RULES(PARAM_RULES, FIELD_ISSUE_RULE_ID, MAX_FACET_SIZE),
- TAGS(PARAM_TAGS, FIELD_ISSUE_TAGS, MAX_FACET_SIZE),
- AUTHORS(DEPRECATED_PARAM_AUTHORS, FIELD_ISSUE_AUTHOR_LOGIN, MAX_FACET_SIZE),
- AUTHOR(PARAM_AUTHOR, FIELD_ISSUE_AUTHOR_LOGIN, MAX_FACET_SIZE),
- PROJECT_UUIDS(FACET_PROJECTS, FIELD_ISSUE_PROJECT_UUID, MAX_FACET_SIZE),
- MODULE_UUIDS(PARAM_MODULE_UUIDS, FIELD_ISSUE_MODULE_UUID, MAX_FACET_SIZE),
- FILE_UUIDS(PARAM_FILE_UUIDS, FIELD_ISSUE_COMPONENT_UUID, MAX_FACET_SIZE),
- DIRECTORIES(PARAM_DIRECTORIES, FIELD_ISSUE_DIRECTORY_PATH, MAX_FACET_SIZE),
- ASSIGNEES(PARAM_ASSIGNEES, FIELD_ISSUE_ASSIGNEE_UUID, MAX_FACET_SIZE),
- ASSIGNED_TO_ME(FACET_ASSIGNED_TO_ME, FIELD_ISSUE_ASSIGNEE_UUID, 1),
- OWASP_TOP_10(PARAM_OWASP_TOP_10, FIELD_ISSUE_OWASP_TOP_10, DEFAULT_FACET_SIZE),
- SANS_TOP_25(PARAM_SANS_TOP_25, FIELD_ISSUE_SANS_TOP_25, DEFAULT_FACET_SIZE),
- CWE(PARAM_CWE, FIELD_ISSUE_CWE, DEFAULT_FACET_SIZE),
- CREATED_AT(PARAM_CREATED_AT, FIELD_ISSUE_FUNC_CREATED_AT, DEFAULT_FACET_SIZE),
- SONARSOURCE_SECURITY(PARAM_SONARSOURCE_SECURITY, FIELD_ISSUE_SQ_SECURITY_CATEGORY, DEFAULT_FACET_SIZE);
+ RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, STICKY, Issue.RESOLUTIONS.size() + 1),
+ TYPES(PARAM_TYPES, FIELD_ISSUE_TYPE, STICKY, RuleType.values().length),
+ LANGUAGES(PARAM_LANGUAGES, FIELD_ISSUE_LANGUAGE, STICKY, MAX_FACET_SIZE),
+ RULES(PARAM_RULES, FIELD_ISSUE_RULE_ID, STICKY, MAX_FACET_SIZE),
+ TAGS(PARAM_TAGS, FIELD_ISSUE_TAGS, STICKY, MAX_FACET_SIZE),
+ AUTHORS(DEPRECATED_PARAM_AUTHORS, FIELD_ISSUE_AUTHOR_LOGIN, STICKY, MAX_FACET_SIZE),
+ AUTHOR(PARAM_AUTHOR, FIELD_ISSUE_AUTHOR_LOGIN, STICKY, MAX_FACET_SIZE),
+ PROJECT_UUIDS(FACET_PROJECTS, FIELD_ISSUE_PROJECT_UUID, STICKY, MAX_FACET_SIZE),
+ MODULE_UUIDS(PARAM_MODULE_UUIDS, FIELD_ISSUE_MODULE_UUID, STICKY, MAX_FACET_SIZE),
+ FILE_UUIDS(PARAM_FILE_UUIDS, FIELD_ISSUE_COMPONENT_UUID, STICKY, MAX_FACET_SIZE),
+ DIRECTORIES(PARAM_DIRECTORIES, FIELD_ISSUE_DIRECTORY_PATH, STICKY, MAX_FACET_SIZE),
+ ASSIGNEES(PARAM_ASSIGNEES, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, MAX_FACET_SIZE),
+ ASSIGNED_TO_ME(FACET_ASSIGNED_TO_ME, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, 1),
+ OWASP_TOP_10(PARAM_OWASP_TOP_10, FIELD_ISSUE_OWASP_TOP_10, STICKY, DEFAULT_FACET_SIZE),
+ SANS_TOP_25(PARAM_SANS_TOP_25, FIELD_ISSUE_SANS_TOP_25, STICKY, DEFAULT_FACET_SIZE),
+ CWE(PARAM_CWE, FIELD_ISSUE_CWE, STICKY, DEFAULT_FACET_SIZE),
+ CREATED_AT(PARAM_CREATED_AT, FIELD_ISSUE_FUNC_CREATED_AT, NON_STICKY),
+ SONARSOURCE_SECURITY(PARAM_SONARSOURCE_SECURITY, FIELD_ISSUE_SQ_SECURITY_CATEGORY, STICKY, DEFAULT_FACET_SIZE);
private final String name;
- private final String fieldName;
- private final int size;
+ private final TopAggregationDefinition topAggregation;
+
+ Facet(String name, String fieldName, boolean sticky, int size) {
+ this.name = name;
+ this.topAggregation = new TermTopAggregationDef(fieldName, sticky, size);
+ }
- Facet(String name, String fieldName, int size) {
+ Facet(String name, String fieldName, boolean sticky) {
this.name = name;
- this.fieldName = fieldName;
- this.size = size;
+ this.topAggregation = new TopAggregationDef(fieldName, sticky);
}
public String getName() {
@@ -249,14 +264,21 @@ public class IssueIndex {
}
public String getFieldName() {
- return fieldName;
+ return topAggregation.getFieldName();
+ }
+
+ public TopAggregationDefinition getTopAggregationDef() {
+ return topAggregation;
}
- public int getSize() {
- return size;
+ public TermTopAggregationDef getTermTopAggregationDef() {
+ return (TermTopAggregationDef) topAggregation;
}
}
+ private static final Map<String, Facet> FACETS_BY_NAME = Arrays.stream(Facet.values())
+ .collect(uniqueIndex(Facet::getName));
+
private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*";
// TODO to be documented
// TODO move to Facets ?
@@ -308,30 +330,39 @@ public class IssueIndex {
}
public SearchResponse search(IssueQuery query, SearchOptions options) {
- SearchRequestBuilder requestBuilder = client.prepareSearch(TYPE_ISSUE.getMainType());
+ SearchRequestBuilder esRequest = client.prepareSearch(TYPE_ISSUE.getMainType());
- configureSorting(query, requestBuilder);
- configurePagination(options, requestBuilder);
- configureRouting(query, options, requestBuilder);
+ configureSorting(query, esRequest);
+ configurePagination(options, esRequest);
+ configureRouting(query, options, esRequest);
- QueryBuilder esQuery = matchAllQuery();
- BoolQueryBuilder esFilter = boolQuery();
- Map<String, QueryBuilder> filters = createFilters(query);
- for (QueryBuilder filter : filters.values()) {
- if (filter != null) {
- esFilter.must(filter);
- }
- }
- if (esFilter.hasClauses()) {
- requestBuilder.setQuery(boolQuery().must(esQuery).filter(esFilter));
- } else {
- requestBuilder.setQuery(esQuery);
- }
+ AllFilters allFilters = createAllFilters(query);
+ RequestFiltersComputer filterComputer = newFilterComputer(options, allFilters);
+
+ configureTopAggregations(query, options, esRequest, allFilters, filterComputer);
+ configureQuery(esRequest, filterComputer);
+ configureTopFilters(esRequest, filterComputer);
+
+ esRequest.setFetchSource(false);
+
+ return esRequest.get();
+ }
+
+ private void configureTopAggregations(IssueQuery query, SearchOptions options, SearchRequestBuilder esRequest, AllFilters allFilters, RequestFiltersComputer filterComputer) {
+ TopAggregationHelper aggregationHelper = newAggregationHelper(filterComputer, query);
- configureStickyFacets(query, options, filters, esQuery, requestBuilder);
- requestBuilder.addAggregation(EFFORT_AGGREGATION);
- requestBuilder.setFetchSource(false);
- return requestBuilder.get();
+ configureTopAggregations(aggregationHelper, query, options, allFilters, esRequest);
+ }
+
+ private static void configureQuery(SearchRequestBuilder esRequest, RequestFiltersComputer filterComputer) {
+ QueryBuilder esQuery = filterComputer.getQueryFilters()
+ .map(t -> (QueryBuilder) boolQuery().must(matchAllQuery()).filter(t))
+ .orElse(matchAllQuery());
+ esRequest.setQuery(esQuery);
+ }
+
+ private static void configureTopFilters(SearchRequestBuilder esRequest, RequestFiltersComputer filterComputer) {
+ filterComputer.getPostFilters().ifPresent(esRequest::setPostFilter);
}
/**
@@ -352,44 +383,43 @@ public class IssueIndex {
esSearch.setFrom(options.getOffset()).setSize(options.getLimit());
}
- private Map<String, QueryBuilder> createFilters(IssueQuery query) {
- Map<String, QueryBuilder> filters = new HashMap<>();
- filters.put("__indexType", termQuery(FIELD_INDEX_TYPE, TYPE_ISSUE.getName()));
- filters.put("__authorization", createAuthorizationFilter());
+ private AllFilters createAllFilters(IssueQuery query) {
+ AllFilters filters = RequestFiltersComputer.newAllFilters();
+ filters.addFilter("__indexType", FIELD_INDEX_TYPE, termQuery(FIELD_INDEX_TYPE, TYPE_ISSUE.getName()));
+ filters.addFilter("__authorization", "parent", createAuthorizationFilter());
// Issue is assigned Filter
if (BooleanUtils.isTrue(query.assigned())) {
- filters.put(IS_ASSIGNED_FILTER, existsQuery(FIELD_ISSUE_ASSIGNEE_UUID));
+ filters.addFilter(IS_ASSIGNED_FILTER, FIELD_ISSUE_ASSIGNEE_UUID, existsQuery(FIELD_ISSUE_ASSIGNEE_UUID));
} else if (BooleanUtils.isFalse(query.assigned())) {
- filters.put(IS_ASSIGNED_FILTER, boolQuery().mustNot(existsQuery(FIELD_ISSUE_ASSIGNEE_UUID)));
+ filters.addFilter(IS_ASSIGNED_FILTER, FIELD_ISSUE_ASSIGNEE_UUID, boolQuery().mustNot(existsQuery(FIELD_ISSUE_ASSIGNEE_UUID)));
}
// Issue is Resolved Filter
- String isResolved = "__isResolved";
if (BooleanUtils.isTrue(query.resolved())) {
- filters.put(isResolved, existsQuery(FIELD_ISSUE_RESOLUTION));
+ filters.addFilter("__isResolved", FIELD_ISSUE_RESOLUTION, existsQuery(FIELD_ISSUE_RESOLUTION));
} else if (BooleanUtils.isFalse(query.resolved())) {
- filters.put(isResolved, boolQuery().mustNot(existsQuery(FIELD_ISSUE_RESOLUTION)));
+ filters.addFilter("__isResolved", FIELD_ISSUE_RESOLUTION, boolQuery().mustNot(existsQuery(FIELD_ISSUE_RESOLUTION)));
}
// Field Filters
- filters.put(FIELD_ISSUE_KEY, createTermsFilter(FIELD_ISSUE_KEY, query.issueKeys()));
- filters.put(FIELD_ISSUE_ASSIGNEE_UUID, createTermsFilter(FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
- filters.put(FIELD_ISSUE_LANGUAGE, createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages()));
- filters.put(FIELD_ISSUE_TAGS, createTermsFilter(FIELD_ISSUE_TAGS, query.tags()));
- filters.put(FIELD_ISSUE_TYPE, createTermsFilter(FIELD_ISSUE_TYPE, query.types()));
- filters.put(FIELD_ISSUE_RESOLUTION, createTermsFilter(FIELD_ISSUE_RESOLUTION, query.resolutions()));
- filters.put(FIELD_ISSUE_AUTHOR_LOGIN, createTermsFilter(FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
- filters.put(FIELD_ISSUE_RULE_ID, createTermsFilter(
+ filters.addFilter(FIELD_ISSUE_KEY, createTermsFilter(FIELD_ISSUE_KEY, query.issueKeys()));
+ filters.addFilter(FIELD_ISSUE_ASSIGNEE_UUID, createTermsFilter(FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
+ filters.addFilter(FIELD_ISSUE_LANGUAGE, createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages()));
+ filters.addFilter(FIELD_ISSUE_TAGS, createTermsFilter(FIELD_ISSUE_TAGS, query.tags()));
+ filters.addFilter(FIELD_ISSUE_TYPE, createTermsFilter(FIELD_ISSUE_TYPE, query.types()));
+ filters.addFilter(FIELD_ISSUE_RESOLUTION, createTermsFilter(FIELD_ISSUE_RESOLUTION, query.resolutions()));
+ filters.addFilter(FIELD_ISSUE_AUTHOR_LOGIN, createTermsFilter(FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
+ filters.addFilter(FIELD_ISSUE_RULE_ID, createTermsFilter(
FIELD_ISSUE_RULE_ID,
query.rules().stream().map(IssueDoc::formatRuleId).collect(toList())));
- filters.put(FIELD_ISSUE_STATUS, createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
- filters.put(FIELD_ISSUE_ORGANIZATION_UUID, createTermFilter(FIELD_ISSUE_ORGANIZATION_UUID, query.organizationUuid()));
- filters.put(FIELD_ISSUE_OWASP_TOP_10, createTermsFilter(FIELD_ISSUE_OWASP_TOP_10, query.owaspTop10()));
- filters.put(FIELD_ISSUE_SANS_TOP_25, createTermsFilter(FIELD_ISSUE_SANS_TOP_25, query.sansTop25()));
- filters.put(FIELD_ISSUE_CWE, createTermsFilter(FIELD_ISSUE_CWE, query.cwe()));
+ filters.addFilter(FIELD_ISSUE_STATUS, createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
+ filters.addFilter(FIELD_ISSUE_ORGANIZATION_UUID, createTermFilter(FIELD_ISSUE_ORGANIZATION_UUID, query.organizationUuid()));
+ filters.addFilter(FIELD_ISSUE_OWASP_TOP_10, createTermsFilter(FIELD_ISSUE_OWASP_TOP_10, query.owaspTop10()));
+ filters.addFilter(FIELD_ISSUE_SANS_TOP_25, createTermsFilter(FIELD_ISSUE_SANS_TOP_25, query.sansTop25()));
+ filters.addFilter(FIELD_ISSUE_CWE, createTermsFilter(FIELD_ISSUE_CWE, query.cwe()));
addSeverityFilter(query, filters);
- filters.put(FIELD_ISSUE_SQ_SECURITY_CATEGORY, createTermsFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, query.sonarsourceSecurity()));
+ filters.addFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, createTermsFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, query.sonarsourceSecurity()));
addComponentRelatedFilters(query, filters);
addDatesFilter(filters, query);
@@ -397,17 +427,17 @@ public class IssueIndex {
return filters;
}
- private static void addSeverityFilter(IssueQuery query, Map<String, QueryBuilder> filters) {
+ private static void addSeverityFilter(IssueQuery query, AllFilters allFilters) {
QueryBuilder severityFieldFilter = createTermsFilter(FIELD_ISSUE_SEVERITY, query.severities());
if (severityFieldFilter != null) {
- filters.put(FIELD_ISSUE_SEVERITY, boolQuery()
+ allFilters.addFilter(FIELD_ISSUE_SEVERITY, boolQuery()
.must(severityFieldFilter)
// Ignore severity of Security HotSpots
.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())));
}
}
- private static void addComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ private static void addComponentRelatedFilters(IssueQuery query, AllFilters filters) {
addCommonComponentRelatedFilters(query, filters);
if (query.viewUuids().isEmpty()) {
addBranchComponentRelatedFilters(query, filters);
@@ -416,35 +446,46 @@ public class IssueIndex {
}
}
- private static void addCommonComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ private static void addCommonComponentRelatedFilters(IssueQuery query, AllFilters filters) {
QueryBuilder componentFilter = createTermsFilter(FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
- QueryBuilder projectFilter = createTermsFilter(FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
- QueryBuilder moduleRootFilter = createTermsFilter(FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids());
- QueryBuilder moduleFilter = createTermsFilter(FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
- QueryBuilder directoryFilter = createTermsFilter(FIELD_ISSUE_DIRECTORY_PATH, query.directories());
QueryBuilder fileFilter = createTermsFilter(FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
if (BooleanUtils.isTrue(query.onComponentOnly())) {
- filters.put(FIELD_ISSUE_COMPONENT_UUID, componentFilter);
+ filters.addFilter(FIELD_ISSUE_COMPONENT_UUID, componentFilter);
} else {
- filters.put(FIELD_ISSUE_PROJECT_UUID, projectFilter);
- filters.put("__module", moduleRootFilter);
- filters.put(FIELD_ISSUE_MODULE_UUID, moduleFilter);
- filters.put(FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
- filters.put(FIELD_ISSUE_COMPONENT_UUID, fileFilter != null ? fileFilter : componentFilter);
+ filters.addFilter(
+ FIELD_ISSUE_PROJECT_UUID,
+ createTermsFilter(FIELD_ISSUE_PROJECT_UUID, query.projectUuids()));
+ filters.addFilter(
+ "__module",
+ FIELD_ISSUE_MODULE_PATH,
+ createTermsFilter(FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids()));
+ filters.addFilter(
+ FIELD_ISSUE_MODULE_UUID,
+ createTermsFilter(FIELD_ISSUE_MODULE_UUID, query.moduleUuids()));
+ filters.addFilter(
+ FIELD_ISSUE_DIRECTORY_PATH,
+ createTermsFilter(FIELD_ISSUE_DIRECTORY_PATH, query.directories()));
+ filters.addFilter(
+ FIELD_ISSUE_COMPONENT_UUID,
+ fileFilter == null ? componentFilter : fileFilter);
}
}
- private static void addBranchComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ private static void addBranchComponentRelatedFilters(IssueQuery query, AllFilters allFilters) {
if (BooleanUtils.isTrue(query.onComponentOnly())) {
return;
}
- QueryBuilder branchFilter = createTermFilter(FIELD_ISSUE_BRANCH_UUID, query.branchUuid());
- filters.put("__is_main_branch", createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
- filters.put(FIELD_ISSUE_BRANCH_UUID, branchFilter);
+ allFilters.addFilter(
+ "__is_main_branch",
+ FIELD_ISSUE_IS_MAIN_BRANCH,
+ createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
+ allFilters.addFilter(
+ FIELD_ISSUE_BRANCH_UUID,
+ createTermFilter(FIELD_ISSUE_BRANCH_UUID, query.branchUuid()));
}
- private static void addViewRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ private static void addViewRelatedFilters(IssueQuery query, AllFilters allFilters) {
if (BooleanUtils.isTrue(query.onComponentOnly())) {
return;
}
@@ -452,10 +493,10 @@ public class IssueIndex {
String branchUuid = query.branchUuid();
boolean onApplicationBranch = branchUuid != null && !viewUuids.isEmpty();
if (onApplicationBranch) {
- filters.put("__view", createViewFilter(singletonList(query.branchUuid())));
+ allFilters.addFilter("__view", createViewFilter(singletonList(query.branchUuid())));
} else {
- filters.put("__is_main_branch", createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(true)));
- filters.put("__view", createViewFilter(viewUuids));
+ allFilters.addFilter("__is_main_branch", createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(true)));
+ allFilters.addFilter("__view", createViewFilter(viewUuids));
}
}
@@ -478,6 +519,26 @@ public class IssueIndex {
return viewsFilter;
}
+ private static RequestFiltersComputer newFilterComputer(SearchOptions options, AllFilters allFilters) {
+ Collection<String> facetNames = options.getFacets();
+ Set<TopAggregationDefinition> facets = Stream.concat(
+ Stream.of(EFFORT_TOP_AGGREGATION),
+ facetNames.stream()
+ .map(FACETS_BY_NAME::get)
+ .filter(Objects::nonNull)
+ .map(Facet::getTopAggregationDef))
+ .collect(MoreCollectors.toSet(facetNames.size()));
+
+ return new RequestFiltersComputer(allFilters, facets);
+ }
+
+ private static TopAggregationHelper newAggregationHelper(RequestFiltersComputer filterComputer, IssueQuery query) {
+ if (hasQueryEffortFacet(query)) {
+ return new TopAggregationHelper(filterComputer, new SubAggregationHelper(EFFORT_AGGREGATION, EFFORT_AGGREGATION_ORDER));
+ }
+ return new TopAggregationHelper(filterComputer, new SubAggregationHelper());
+ }
+
private static AggregationBuilder addEffortAggregationIfNeeded(IssueQuery query, AggregationBuilder aggregation) {
if (hasQueryEffortFacet(query)) {
aggregation.subAggregation(EFFORT_AGGREGATION);
@@ -489,69 +550,6 @@ public class IssueIndex {
return FACET_MODE_EFFORT.equals(query.facetMode());
}
- private static AggregationBuilder createSeverityFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
- String fieldName = SEVERITIES.getFieldName();
- String facetName = SEVERITIES.getName();
- StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, filters, queryBuilder);
- BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName)
- // Ignore severity of Security HotSpots
- .mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()));
- FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, SEVERITIES.getSize());
- return AggregationBuilders
- .global(facetName)
- .subAggregation(facetTopAggregation);
- }
-
- private static AggregationBuilder createAssigneesFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
- String fieldName = ASSIGNEES.getFieldName();
- String facetName = ASSIGNEES.getName();
-
- // Same as in super.stickyFacetBuilder
- Map<String, QueryBuilder> assigneeFilters = new HashMap<>(filters);
- assigneeFilters.remove(IS_ASSIGNED_FILTER);
- assigneeFilters.remove(fieldName);
- StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, assigneeFilters, queryBuilder);
- BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName);
- FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, ASSIGNEES.getSize());
- if (!query.assignees().isEmpty()) {
- facetTopAggregation = stickyFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t, query.assignees().toArray());
- }
-
- // Add missing facet for unassigned issues
- facetTopAggregation.subAggregation(
- addEffortAggregationIfNeeded(query, AggregationBuilders
- .missing(facetName + FACET_SUFFIX_MISSING)
- .field(fieldName)));
-
- return AggregationBuilders
- .global(facetName)
- .subAggregation(facetTopAggregation);
- }
-
- private static AggregationBuilder createResolutionFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
- String fieldName = RESOLUTIONS.getFieldName();
- String facetName = RESOLUTIONS.getName();
-
- // Same as in super.stickyFacetBuilder
- Map<String, QueryBuilder> resolutionFilters = Maps.newHashMap(filters);
- resolutionFilters.remove("__isResolved");
- resolutionFilters.remove(fieldName);
- StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, resolutionFilters, esQuery);
- BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName);
- FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, RESOLUTIONS.getSize());
- facetTopAggregation = stickyFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t);
-
- // Add missing facet for unresolved issues
- facetTopAggregation.subAggregation(
- addEffortAggregationIfNeeded(query, AggregationBuilders
- .missing(facetName + FACET_SUFFIX_MISSING)
- .field(fieldName)));
-
- return AggregationBuilders
- .global(facetName)
- .subAggregation(facetTopAggregation);
- }
-
@CheckForNull
private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
return values.isEmpty() ? null : termsQuery(field, values);
@@ -579,35 +577,44 @@ public class IssueIndex {
return authorizationTypeSupport.createQueryFilter();
}
- private void addDatesFilter(Map<String, QueryBuilder> filters, IssueQuery query) {
+ private void addDatesFilter(AllFilters filters, IssueQuery query) {
PeriodStart createdAfter = query.createdAfter();
Date createdBefore = query.createdBefore();
validateCreationDateBounds(createdBefore, createdAfter != null ? createdAfter.date() : null);
if (createdAfter != null) {
- filters.put("__createdAfter", QueryBuilders
- .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
- .from(BaseDoc.dateToEpochSeconds(createdAfter.date()), createdAfter.inclusive()));
+ filters.addFilter(
+ "__createdAfter",
+ FIELD_ISSUE_FUNC_CREATED_AT,
+ QueryBuilders
+ .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
+ .from(BaseDoc.dateToEpochSeconds(createdAfter.date()), createdAfter.inclusive()));
}
if (createdBefore != null) {
- filters.put("__createdBefore", QueryBuilders
- .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
- .lt(BaseDoc.dateToEpochSeconds(createdBefore)));
+ filters.addFilter(
+ "__createdBefore",
+ FIELD_ISSUE_FUNC_CREATED_AT,
+ QueryBuilders
+ .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
+ .lt(BaseDoc.dateToEpochSeconds(createdBefore)));
}
Date createdAt = query.createdAt();
if (createdAt != null) {
- filters.put("__createdAt", termQuery(FIELD_ISSUE_FUNC_CREATED_AT, BaseDoc.dateToEpochSeconds(createdAt)));
+ filters.addFilter(
+ "__createdAt",
+ FIELD_ISSUE_FUNC_CREATED_AT,
+ termQuery(FIELD_ISSUE_FUNC_CREATED_AT, BaseDoc.dateToEpochSeconds(createdAt)));
}
}
- private static void addCreatedAfterByProjectsFilter(Map<String, QueryBuilder> filters, IssueQuery query) {
+ private static void addCreatedAfterByProjectsFilter(AllFilters allFilters, IssueQuery query) {
Map<String, PeriodStart> createdAfterByProjectUuids = query.createdAfterByProjectUuids();
BoolQueryBuilder boolQueryBuilder = boolQuery();
createdAfterByProjectUuids.forEach((projectUuid, createdAfterDate) -> boolQueryBuilder.should(boolQuery()
.filter(termQuery(FIELD_ISSUE_PROJECT_UUID, projectUuid))
.filter(rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT).from(BaseDoc.dateToEpochSeconds(createdAfterDate.date()), createdAfterDate.inclusive()))));
- filters.put("createdAfterByProjectUuids", boolQueryBuilder);
+ allFilters.addFilter("createdAfterByProjectUuids", boolQueryBuilder);
}
private void validateCreationDateBounds(@Nullable Date createdBefore, @Nullable Date createdAfter) {
@@ -617,46 +624,111 @@ public class IssueIndex {
"Start bound cannot be larger or equal to end bound");
}
- private void configureStickyFacets(IssueQuery query, SearchOptions options, Map<String, QueryBuilder> filters, QueryBuilder esQuery, SearchRequestBuilder esSearch) {
- if (!options.getFacets().isEmpty()) {
- StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, filters, esQuery);
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, STATUSES);
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, PROJECT_UUIDS, query.projectUuids().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, MODULE_UUIDS, query.moduleUuids().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, DIRECTORIES, query.directories().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, FILE_UUIDS, query.fileUuids().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, LANGUAGES, query.languages().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, RULES, query.rules().stream().map(IssueDoc::formatRuleId).toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, AUTHORS, query.authors().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, AUTHOR, query.authors().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, TAGS, query.tags().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, TYPES, query.types().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, OWASP_TOP_10, query.owaspTop10().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, SANS_TOP_25, query.sansTop25().toArray());
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, CWE, query.cwe().toArray());
- if (options.getFacets().contains(PARAM_SEVERITIES)) {
- esSearch.addAggregation(createSeverityFacet(query, filters, esQuery));
- }
- addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, SONARSOURCE_SECURITY, query.sonarsourceSecurity().toArray());
- if (options.getFacets().contains(PARAM_RESOLUTIONS)) {
- esSearch.addAggregation(createResolutionFacet(query, filters, esQuery));
- }
- if (options.getFacets().contains(PARAM_ASSIGNEES)) {
- esSearch.addAggregation(createAssigneesFacet(query, filters, esQuery));
- }
- if (options.getFacets().contains(PARAM_CREATED_AT)) {
- getCreatedAtFacet(query, filters, esQuery).ifPresent(esSearch::addAggregation);
- }
- addAssignedToMeFacetIfNeeded(esSearch, options, query, filters, esQuery);
+ private void configureTopAggregations(TopAggregationHelper aggregationHelper, IssueQuery query, SearchOptions options,
+ AllFilters queryFilters, SearchRequestBuilder esRequest) {
+ addFacetIfNeeded(options, aggregationHelper, esRequest, STATUSES, NO_SELECTED_VALUES);
+ addFacetIfNeeded(options, aggregationHelper, esRequest, PROJECT_UUIDS, query.projectUuids().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, MODULE_UUIDS, query.moduleUuids().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, DIRECTORIES, query.directories().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, FILE_UUIDS, query.fileUuids().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, LANGUAGES, query.languages().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, RULES, query.rules().stream().map(RuleDefinitionDto::getId).toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, AUTHORS, query.authors().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, AUTHOR, query.authors().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, OWASP_TOP_10, query.owaspTop10().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, SANS_TOP_25, query.sansTop25().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, CWE, query.cwe().toArray());
+ addFacetIfNeeded(options, aggregationHelper, esRequest, SONARSOURCE_SECURITY, query.sonarsourceSecurity().toArray());
+ addSeverityFacetIfNeeded(options, aggregationHelper, esRequest);
+ addResolutionFacetIfNeeded(options, query, aggregationHelper, esRequest);
+ addAssigneesFacetIfNeeded(options, query, aggregationHelper, esRequest);
+ addCreatedAtFacetIfNeeded(options, query, aggregationHelper, queryFilters, esRequest);
+ addAssignedToMeFacetIfNeeded(options, aggregationHelper, esRequest);
+ addEffortTopAggregation(aggregationHelper, esRequest);
+ }
+
+ private static void addFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper,
+ SearchRequestBuilder esRequest, Facet facet, Object[] selectedValues) {
+ if (!options.getFacets().contains(facet.getName())) {
+ return;
+ }
+
+ FilterAggregationBuilder topAggregation = aggregationHelper.buildTermTopAggregation(
+ facet.getName(), facet.getTermTopAggregationDef(),
+ NO_EXTRA_FILTER,
+ t -> aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(facet.getName(), facet.getTermTopAggregationDef(), selectedValues)
+ .ifPresent(t::subAggregation));
+ esRequest.addAggregation(topAggregation);
+ }
+
+ private static void addSeverityFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper, SearchRequestBuilder esRequest) {
+ if (!options.getFacets().contains(PARAM_SEVERITIES)) {
+ return;
}
+
+ AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
+ SEVERITIES.getName(), SEVERITIES.getTermTopAggregationDef(),
+ // Ignore severity of Security HotSpots
+ filter -> filter.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())),
+ NO_OTHER_SUBAGGREGATION);
+ esRequest.addAggregation(aggregation);
}
- private Optional<AggregationBuilder> getCreatedAtFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
+ private static void addResolutionFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchRequestBuilder esRequest) {
+ if (!options.getFacets().contains(PARAM_RESOLUTIONS)) {
+ return;
+ }
+
+ AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
+ RESOLUTIONS.getName(), RESOLUTIONS.getTermTopAggregationDef(),
+ NO_EXTRA_FILTER,
+ t -> {
+ // add aggregation of type "missing" to return count of unresolved issues in the facet
+ t.subAggregation(
+ addEffortAggregationIfNeeded(query, AggregationBuilders
+ .missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING)
+ .field(RESOLUTIONS.getFieldName())));
+ });
+ esRequest.addAggregation(aggregation);
+ }
+
+ private static void addAssigneesFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchRequestBuilder esRequest) {
+ if (!options.getFacets().contains(PARAM_ASSIGNEES)) {
+ return;
+ }
+
+ Consumer<FilterAggregationBuilder> assigneeAggregations = t -> {
+ // optional second aggregation to return the issue count for selected assignees (if any)
+ Object[] assignees = query.assignees().toArray();
+ aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(ASSIGNEES.getName(), ASSIGNEES.getTopAggregationDef(), assignees)
+ .ifPresent(t::subAggregation);
+
+ // third aggregation to always return the count of unassigned in the assignee facet
+ t.subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders
+ .missing(ASSIGNEES.getName() + FACET_SUFFIX_MISSING)
+ .field(ASSIGNEES.getFieldName())));
+ };
+
+ AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
+ ASSIGNEES.getName(), ASSIGNEES.getTermTopAggregationDef(), NO_EXTRA_FILTER, assigneeAggregations);
+ esRequest.addAggregation(aggregation);
+ }
+
+ private void addCreatedAtFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, AllFilters allFilters,
+ SearchRequestBuilder esRequest) {
+ if (options.getFacets().contains(PARAM_CREATED_AT)) {
+ getCreatedAtFacet(query, aggregationHelper, allFilters).ifPresent(esRequest::addAggregation);
+ }
+ }
+
+ private Optional<AggregationBuilder> getCreatedAtFacet(IssueQuery query, TopAggregationHelper aggregationHelper, AllFilters allFilters) {
long startTime;
boolean startInclusive;
PeriodStart createdAfter = query.createdAfter();
if (createdAfter == null) {
- OptionalLong minDate = getMinCreatedAt(filters, esQuery);
+ OptionalLong minDate = getMinCreatedAt(allFilters);
if (!minDate.isPresent()) {
return Optional.empty();
}
@@ -670,38 +742,50 @@ public class IssueIndex {
long endTime = createdBefore == null ? system.now() : createdBefore.getTime();
Duration timeSpan = new Duration(startTime, endTime);
- DateHistogramInterval bucketSize = DateHistogramInterval.YEAR;
+ DateHistogramInterval bucketSize = computeDateHistogramBucketSize(timeSpan);
+
+ FilterAggregationBuilder topAggregation = aggregationHelper.buildTopAggregation(
+ CREATED_AT.getName(),
+ CREATED_AT.getTopAggregationDef(),
+ NO_EXTRA_FILTER,
+ t -> {
+ AggregationBuilder dateHistogram = AggregationBuilders.dateHistogram(CREATED_AT.getName())
+ .field(CREATED_AT.getFieldName())
+ .dateHistogramInterval(bucketSize)
+ .minDocCount(0L)
+ .format(DateUtils.DATETIME_FORMAT)
+ .timeZone(DateTimeZone.forOffsetMillis(system.getDefaultTimeZone().getRawOffset()))
+ // ES dateHistogram bounds are inclusive while createdBefore parameter is exclusive
+ .extendedBounds(new ExtendedBounds(startInclusive ? startTime : (startTime + 1), endTime - 1L));
+ addEffortAggregationIfNeeded(query, dateHistogram);
+ t.subAggregation(dateHistogram);
+ });
+
+ return Optional.of(topAggregation);
+ }
+
+ private static DateHistogramInterval computeDateHistogramBucketSize(Duration timeSpan) {
if (timeSpan.isShorterThan(TWENTY_DAYS)) {
- bucketSize = DateHistogramInterval.DAY;
- } else if (timeSpan.isShorterThan(TWENTY_WEEKS)) {
- bucketSize = DateHistogramInterval.WEEK;
- } else if (timeSpan.isShorterThan(TWENTY_MONTHS)) {
- bucketSize = DateHistogramInterval.MONTH;
- }
-
- AggregationBuilder dateHistogram = AggregationBuilders.dateHistogram(CREATED_AT.getName())
- .field(CREATED_AT.getFieldName())
- .dateHistogramInterval(bucketSize)
- .minDocCount(0L)
- .format(DateUtils.DATETIME_FORMAT)
- .timeZone(DateTimeZone.forOffsetMillis(system.getDefaultTimeZone().getRawOffset()))
- // ES dateHistogram bounds are inclusive while createdBefore parameter is exclusive
- .extendedBounds(new ExtendedBounds(startInclusive ? startTime : (startTime + 1), endTime - 1L));
- addEffortAggregationIfNeeded(query, dateHistogram);
- return Optional.of(dateHistogram);
- }
-
- private OptionalLong getMinCreatedAt(Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
+ return DateHistogramInterval.DAY;
+ }
+ if (timeSpan.isShorterThan(TWENTY_WEEKS)) {
+ return DateHistogramInterval.WEEK;
+ }
+ if (timeSpan.isShorterThan(TWENTY_MONTHS)) {
+ return DateHistogramInterval.MONTH;
+ }
+ return DateHistogramInterval.YEAR;
+ }
+
+ private OptionalLong getMinCreatedAt(AllFilters filters) {
String facetNameAndField = CREATED_AT.getFieldName();
SearchRequestBuilder esRequest = client
.prepareSearch(TYPE_ISSUE.getMainType())
.setSize(0);
BoolQueryBuilder esFilter = boolQuery();
- filters.values().stream().filter(Objects::nonNull).forEach(esFilter::must);
+ filters.stream().filter(Objects::nonNull).forEach(esFilter::must);
if (esFilter.hasClauses()) {
- esRequest.setQuery(QueryBuilders.boolQuery().must(esQuery).filter(esFilter));
- } else {
- esRequest.setQuery(esQuery);
+ esRequest.setQuery(QueryBuilders.boolQuery().filter(esFilter));
}
esRequest.addAggregation(AggregationBuilders.min(facetNameAndField).field(facetNameAndField));
Min minValue = esRequest.get().getAggregations().get(facetNameAndField);
@@ -713,43 +797,29 @@ public class IssueIndex {
return OptionalLong.of((long) actualValue);
}
- private void addAssignedToMeFacetIfNeeded(SearchRequestBuilder builder, SearchOptions options, IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
+ private void addAssignedToMeFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper, SearchRequestBuilder esRequest) {
String uuid = userSession.getUuid();
-
- if (!options.getFacets().contains(ASSIGNED_TO_ME.getName()) || StringUtils.isEmpty(uuid)) {
- return;
- }
-
- String fieldName = ASSIGNED_TO_ME.getFieldName();
- String facetName = ASSIGNED_TO_ME.getName();
-
- // Same as in super.stickyFacetBuilder
- StickyFacetBuilder assignedToMeFacetBuilder = newStickyFacetBuilder(query, filters, queryBuilder);
- BoolQueryBuilder facetFilter = assignedToMeFacetBuilder.getStickyFacetFilter(IS_ASSIGNED_FILTER, fieldName);
-
- FilterAggregationBuilder facetTopAggregation = AggregationBuilders
- .filter(facetName + "__filter", facetFilter)
- .subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders.terms(facetName + "__terms")
- .field(fieldName)
- .includeExclude(new IncludeExclude(escapeSpecialRegexChars(uuid), null))));
-
- builder.addAggregation(
- AggregationBuilders.global(facetName)
- .subAggregation(facetTopAggregation));
- }
-
- private static StickyFacetBuilder newStickyFacetBuilder(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
- if (hasQueryEffortFacet(query)) {
- return new StickyFacetBuilder(esQuery, filters, EFFORT_AGGREGATION, EFFORT_AGGREGATION_ORDER);
+ if (options.getFacets().contains(ASSIGNED_TO_ME.getName()) && !StringUtils.isEmpty(uuid)) {
+ AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
+ ASSIGNED_TO_ME.getName(),
+ ASSIGNED_TO_ME.getTermTopAggregationDef(),
+ NO_EXTRA_FILTER,
+ t -> {
+ // add sub-aggregation to return issue count for current user
+ aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTermTopAggregationDef(), new String[] {uuid})
+ .ifPresent(t::subAggregation);
+ });
+ esRequest.addAggregation(aggregation);
}
- return new StickyFacetBuilder(esQuery, filters);
}
- private static void addSimpleStickyFacetIfNeeded(SearchOptions options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch,
- Facet facet, Object... selectedValues) {
- if (options.getFacets().contains(facet.getName())) {
- esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(facet.getFieldName(), facet.getName(), facet.getSize(), selectedValues));
- }
+ private static void addEffortTopAggregation(TopAggregationHelper aggregationHelper, SearchRequestBuilder esRequest) {
+ AggregationBuilder topAggregation = aggregationHelper.buildTopAggregation(
+ FACET_MODE_EFFORT,
+ EFFORT_TOP_AGGREGATION,
+ NO_EXTRA_FILTER,
+ t -> t.subAggregation(EFFORT_AGGREGATION));
+ esRequest.addAggregation(topAggregation);
}
public List<String> searchTags(IssueQuery query, @Nullable String textQuery, int size) {
@@ -790,11 +860,9 @@ public class IssueIndex {
private BoolQueryBuilder createBoolFilter(IssueQuery query) {
BoolQueryBuilder boolQuery = boolQuery();
- for (QueryBuilder filter : createFilters(query).values()) {
- if (filter != null) {
- boolQuery.must(filter);
- }
- }
+ createAllFilters(query).stream()
+ .filter(Objects::nonNull)
+ .forEach(boolQuery::must);
return boolQuery;
}
@@ -880,7 +948,7 @@ public class IssueIndex {
public List<SecurityStandardCategoryStatistics> getSonarSourceReport(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
- Arrays.stream(SQCategory.values())
+ Arrays.stream(SecurityStandards.SQCategory.values())
.forEach(sonarsourceCategory -> request.addAggregation(
newSecurityReportSubAggregations(
AggregationBuilders.filter(sonarsourceCategory.getKey(), boolQuery().filter(termQuery(FIELD_ISSUE_SQ_SECURITY_CATEGORY, sonarsourceCategory.getKey()))),