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;
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;
.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
FilterBuilders.boolFilter()
.must(FilterBuilders.termFilter(IssueAuthorizationNormalizer.IssueAuthorizationField.PERMISSION.field(), UserRole.USER), groupsAndUser)
.cache(true))
- ));
+ ));
// Issue is assigned Filter
if (BooleanUtils.isTrue(query.assigned())) {
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())
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) {
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");
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;
assertThat(result.getHits()).hasSize(2);
}
+ @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++) {
}
@Test
- @Ignore("TODO")
public void sort_by_assignee() throws Exception {
IssueDto issue1 = createIssue().setAssignee("steph");
IssueDto issue2 = createIssue().setAssignee("simon");