aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2014-09-23 14:44:49 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2014-09-23 14:53:48 +0200
commit3c8726bcaeb9898d995452938d702d9b3543a39e (patch)
tree0ecf8bdc3ec36240d6a1d0acce5a350be0f2ccad
parent322d6155f57a92ca914b356c2c7bfa1a66ec007a (diff)
downloadsonarqube-3c8726bcaeb9898d995452938d702d9b3543a39e.tar.gz
sonarqube-3c8726bcaeb9898d995452938d702d9b3543a39e.zip
SONAR-5531 Fix sort and search by dates
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java96
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java20
5 files changed, 135 insertions, 35 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index 152f6440213..a2f2489fd73 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -30,6 +30,9 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.web.UserRole;
@@ -39,10 +42,7 @@ import org.sonar.server.search.*;
import javax.annotation.Nullable;
import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import static com.google.common.collect.Lists.newArrayList;
@@ -131,15 +131,33 @@ public class IssueIndex extends BaseIndex<Issue, IssueDto, String> {
.setTypes(this.getIndexType())
.setIndices(this.getIndexName());
- // Integrate Pagination
- esSearch.setFrom(options.getOffset());
- esSearch.setSize(options.getLimit());
-
if (options.isScroll()) {
esSearch.setSearchType(SearchType.SCAN);
esSearch.setScroll(TimeValue.timeValueMinutes(3));
}
+ setFacets(options, esSearch);
+ setSorting(query, esSearch);
+ setPagination(options, esSearch);
+
+ QueryBuilder esQuery = QueryBuilders.matchAllQuery();
+ BoolFilterBuilder esFilter = getFilter(query, options);
+ if (esFilter.hasClauses()) {
+ esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter));
+ } else {
+ esSearch.setQuery(esQuery);
+ }
+
+ // Sample Functional aggregation
+ // esSearch.addAggregation(AggregationBuilders.sum("totalDuration")
+ // .field(IssueNormalizer.IssueField.DEBT.field()));
+
+ SearchResponse response = getClient().execute(esSearch);
+ return new Result<Issue>(this, response);
+ }
+
+ /* Build main filter (match based) */
+ protected BoolFilterBuilder getFilter(IssueQuery query, QueryContext options) {
BoolFilterBuilder esFilter = FilterBuilders.boolFilter();
// Authorization
@@ -158,7 +176,7 @@ public class IssueIndex extends BaseIndex<Issue, IssueDto, String> {
FilterBuilders.boolFilter()
.must(FilterBuilders.termFilter(IssueAuthorizationNormalizer.IssueAuthorizationField.PERMISSION.field(), UserRole.USER), groupsAndUser)
.cache(true))
- ));
+ ));
// Issue is assigned Filter
if (BooleanUtils.isTrue(query.assigned())) {
@@ -189,27 +207,27 @@ public class IssueIndex extends BaseIndex<Issue, IssueDto, String> {
matchFilter(esFilter, IssueNormalizer.IssueField.STATUS, query.statuses());
// Date filters
- if (query.createdAfter() != null) {
+ Date createdAfter = query.createdAfter();
+ if (createdAfter != null) {
esFilter.must(FilterBuilders
.rangeFilter(IssueNormalizer.IssueField.ISSUE_CREATED_AT.field())
- .gte(query.createdAfter()));
+ .gte(createdAfter));
}
- if (query.createdBefore() != null) {
+ Date createdBefore = query.createdBefore();
+ if (createdBefore != null) {
esFilter.must(FilterBuilders
.rangeFilter(IssueNormalizer.IssueField.ISSUE_CREATED_AT.field())
- .lte(query.createdBefore()));
+ .lte(createdBefore));
}
- // TODO match day bracket for day on createdAt
- // query.createdAt();
-
- QueryBuilder esQuery = QueryBuilders.matchAllQuery();
-
- if (esFilter.hasClauses()) {
- esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter));
- } else {
- esSearch.setQuery(esQuery);
+ Date createdAt = query.createdAt();
+ if (createdAt != null) {
+ esFilter.must(FilterBuilders.termFilter(IssueNormalizer.IssueField.ISSUE_CREATED_AT.field(), createdAt));
}
+ return esFilter;
+ }
+
+ private void setFacets(QueryContext options, SearchRequestBuilder esSearch) {
if (options.isFacet()) {
// Execute Term aggregations
esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.SEVERITY.field())
@@ -221,13 +239,37 @@ public class IssueIndex extends BaseIndex<Issue, IssueDto, String> {
esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.ACTION_PLAN.field())
.field(IssueNormalizer.IssueField.ACTION_PLAN.field()));
}
+ }
- // Sample Functional aggregation
- // esSearch.addAggregation(AggregationBuilders.sum("totalDuration")
- // .field(IssueNormalizer.IssueField.DEBT.field()));
+ private void setSorting(IssueQuery query, SearchRequestBuilder esSearch) {
+ /* integrate Query Sort */
+ String sortField = query.sort();
+ Boolean asc = query.asc();
+ if (sortField != null) {
+ FieldSortBuilder sort = SortBuilders.fieldSort(toIndexField(sortField).sortField());
+ if (asc != null && asc) {
+ sort.order(SortOrder.ASC);
+ } else {
+ sort.order(SortOrder.DESC);
+ }
+ esSearch.addSort(sort);
+ } else {
+ esSearch.addSort(IssueNormalizer.IssueField.ISSUE_UPDATED_AT.sortField(), SortOrder.DESC);
+ // deterministic sort when exactly the same updated_at (same millisecond)
+ esSearch.addSort(IssueNormalizer.IssueField.KEY.sortField(), SortOrder.ASC);
+ }
+ }
- SearchResponse response = getClient().execute(esSearch);
- return new Result<Issue>(this, response);
+ private IndexField toIndexField(String sort){
+ if (IssueQuery.SORT_BY_ASSIGNEE.equals(sort)) {
+ return IssueNormalizer.IssueField.ASSIGNEE;
+ }
+ throw new IllegalStateException("Unknown sort field : " + sort);
+ }
+
+ protected void setPagination(QueryContext options, SearchRequestBuilder esSearch) {
+ esSearch.setFrom(options.getOffset());
+ esSearch.setSize(options.getLimit());
}
private void matchFilter(BoolFilterBuilder filter, IndexField field, @Nullable Collection<?> values) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java
index ba91903c0e9..f8c6cb2048a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java
@@ -44,21 +44,21 @@ public class IssueNormalizer extends BaseNormalizer<IssueDto, String> {
public static final IndexField UPDATED_AT = add(IndexField.Type.DATE, "updatedAt");
public static final IndexField ACTION_PLAN = add(IndexField.Type.STRING, "actionPlan");
- public static final IndexField ASSIGNEE = add(IndexField.Type.STRING, "assignee");
+ public static final IndexField ASSIGNEE = addSortable(IndexField.Type.STRING, "assignee");
public static final IndexField ATTRIBUTE = add(IndexField.Type.OBJECT, "attributes");
public static final IndexField AUTHOR_LOGIN = add(IndexField.Type.STRING, "authorLogin");
public static final IndexField COMPONENT = add(IndexField.Type.STRING, "component");
public static final IndexField DEBT = add(IndexField.Type.NUMERIC, "debt");
public static final IndexField EFFORT = add(IndexField.Type.NUMERIC, "effort");
- public static final IndexField ISSUE_CREATED_AT = add(IndexField.Type.DATE, "issueCreatedAt");
- public static final IndexField ISSUE_UPDATED_AT = add(IndexField.Type.DATE, "issueUpdatedAt");
- public static final IndexField ISSUE_CLOSE_DATE = add(IndexField.Type.DATE, "issueClosedAt");
+ public static final IndexField ISSUE_CREATED_AT = addSortable(IndexField.Type.DATE, "issueCreatedAt");
+ public static final IndexField ISSUE_UPDATED_AT = addSortable(IndexField.Type.DATE, "issueUpdatedAt");
+ public static final IndexField ISSUE_CLOSE_DATE = addSortable(IndexField.Type.DATE, "issueClosedAt");
public static final IndexField LINE = add(IndexField.Type.NUMERIC, "line");
public static final IndexField MESSAGE = add(IndexField.Type.STRING, "message");
public static final IndexField PROJECT = add(IndexField.Type.STRING, "project");
public static final IndexField RESOLUTION = add(IndexField.Type.STRING, "resolution");
public static final IndexField REPORTER = add(IndexField.Type.STRING, "reporter");
- public static final IndexField STATUS = add(IndexField.Type.STRING, "status");
+ public static final IndexField STATUS = addSortable(IndexField.Type.STRING, "status");
public static final IndexField SEVERITY = add(IndexField.Type.STRING, "severity");
public static final IndexField LANGUAGE = add(IndexField.Type.STRING, "language");
public static final IndexField RULE_KEY = addSearchable(IndexField.Type.STRING, "ruleKey");
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java b/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java
index 4ccc0d56aca..e4104142fd0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/search/ws/SearchRequestHandler.java
@@ -124,7 +124,7 @@ public abstract class SearchRequestHandler<QUERY, DOMAIN> implements RequestHand
json.endObject().close();
}
- private final QueryContext getQueryContext(Request request) {
+ private QueryContext getQueryContext(Request request) {
return new QueryContext().addFieldsToReturn(request.paramAsStrings(PARAM_FIELDS))
.setFacet(request.mandatoryParamAsBoolean(PARAM_FACETS))
.setPage(request.mandatoryParamAsInt(PARAM_PAGE),
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java
index af9779d1a27..6b9d6a3ef79 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java
@@ -20,7 +20,10 @@
package org.sonar.server.issue.index;
import com.google.common.collect.ImmutableList;
-import org.junit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.rule.RuleKey;
@@ -212,6 +215,42 @@ public class IssueIndexMediumTest {
}
@Test
+ public void filter_created_after() throws Exception {
+ IssueDto issue1 = createIssue().setIssueCreationDate(DateUtils.parseDate("2014-09-20"));
+ IssueDto issue2 = createIssue().setIssueCreationDate(DateUtils.parseDate("2014-09-23"));
+ db.issueDao().insert(session, issue1, issue2);
+ session.commit();
+
+ assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-19")).build(), new QueryContext()).getHits()).hasSize(2);
+ assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(2);
+ assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).hasSize(1);
+ assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-25")).build(), new QueryContext()).getHits()).isEmpty();
+ }
+
+ @Test
+ public void filter_created_before() throws Exception {
+ IssueDto issue1 = createIssue().setIssueCreationDate(DateUtils.parseDate("2014-09-20"));
+ IssueDto issue2 = createIssue().setIssueCreationDate(DateUtils.parseDate("2014-09-23"));
+ db.issueDao().insert(session, issue1, issue2);
+ session.commit();
+
+ assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-19")).build(), new QueryContext()).getHits()).isEmpty();
+ assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(1);
+ assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).hasSize(1);
+ assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-25")).build(), new QueryContext()).getHits()).hasSize(2);
+ }
+
+ @Test
+ public void filter_created_at() throws Exception {
+ IssueDto issue = createIssue().setIssueCreationDate(DateUtils.parseDate("2014-09-20"));
+ db.issueDao().insert(session, issue);
+ session.commit();
+
+ assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(1);
+ assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).isEmpty();
+ }
+
+ @Test
public void paging() throws Exception {
for (int i=0; i<12; i++) {
IssueDto issue = createIssue();
@@ -260,7 +299,6 @@ public class IssueIndexMediumTest {
}
@Test
- @Ignore("TODO")
public void sort_by_assignee() throws Exception {
IssueDto issue1 = createIssue().setAssignee("steph");
IssueDto issue2 = createIssue().setAssignee("simon");
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
index fc4458e49a5..015b1ef7930 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
@@ -202,14 +202,26 @@ public class IssueQuery {
return asc;
}
+ /**
+ * @deprecated since 5.0, use {@link org.sonar.server.search.QueryContext} instead
+ */
+ @Deprecated
public int pageSize() {
return pageSize;
}
+ /**
+ * @deprecated since 5.0, use {@link org.sonar.server.search.QueryContext} instead
+ */
+ @Deprecated
public int pageIndex() {
return pageIndex;
}
+ /**
+ * @deprecated since 5.0, now useless with the usage of E/S
+ */
+ @Deprecated
public int maxResults() {
return MAX_RESULTS;
}
@@ -377,11 +389,19 @@ public class IssueQuery {
return this;
}
+ /**
+ * @deprecated since 5.0, use {@link org.sonar.server.search.QueryContext} instead
+ */
+ @Deprecated
public Builder pageSize(@Nullable Integer i) {
this.pageSize = i;
return this;
}
+ /**
+ * @deprecated since 5.0, use {@link org.sonar.server.search.QueryContext} instead
+ */
+ @Deprecated
public Builder pageIndex(@Nullable Integer i) {
this.pageIndex = i;
return this;