diff options
author | Daniel Schwarz <daniel.schwarz@sonarsource.com> | 2017-05-10 15:54:47 +0200 |
---|---|---|
committer | Daniel Schwarz <bartfastiel@users.noreply.github.com> | 2017-05-11 09:31:11 +0200 |
commit | 2fc84f863e47cdd5c833d4804a640a214bb60920 (patch) | |
tree | 66c7b634e01bea9ed3c99fdabd43b2cab67755e6 /server | |
parent | 044686172e636433f6c1eb0af0f687e7442974f2 (diff) | |
download | sonarqube-2fc84f863e47cdd5c833d4804a640a214bb60920.tar.gz sonarqube-2fc84f863e47cdd5c833d4804a640a214bb60920.zip |
SONAR-9075 “more” skips first 6 results, in api/components/suggestions
Diffstat (limited to 'server')
6 files changed, 88 insertions, 55 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentHitsPerQualifier.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentHitsPerQualifier.java index 6576b7fdac9..45069a2506c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentHitsPerQualifier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentHitsPerQualifier.java @@ -44,8 +44,4 @@ public class ComponentHitsPerQualifier { public long getTotalHits() { return totalHits; } - - public long getNumberOfFurtherResults() { - return Math.max(totalHits - hits.size(), 0L); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndex.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndex.java index 60d288028a9..d03b2220fb3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndex.java @@ -114,8 +114,9 @@ public class ComponentIndex { .setHighlighterEncoder("html") .setHighlighterPreTags("<mark>") .setHighlighterPostTags("</mark>") - .addHighlightedField(createHighlighter()); - query.getLimit().ifPresent(sub::setSize); + .addHighlightedField(createHighlighter()) + .setFrom(query.getSkip()) + .setSize(query.getLimit()); return sub.setFetchSource(false); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexQuery.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexQuery.java index 74a62b04677..e314afce4db 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexQuery.java @@ -21,7 +21,6 @@ package org.sonar.server.component.index; import java.util.Collection; import java.util.Collections; -import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -35,6 +34,7 @@ public class ComponentIndexQuery { private final Collection<String> qualifiers; private final Set<String> recentlyBrowsedKeys; private final Set<String> favoriteKeys; + private final int skip; private final int limit; private ComponentIndexQuery(Builder builder) { @@ -42,6 +42,7 @@ public class ComponentIndexQuery { this.qualifiers = requireNonNull(builder.qualifiers); this.recentlyBrowsedKeys = requireNonNull(builder.recentlyBrowsedKeys); this.favoriteKeys = requireNonNull(builder.favoriteKeys); + this.skip = builder.skip; this.limit = builder.limit; } @@ -57,8 +58,12 @@ public class ComponentIndexQuery { return recentlyBrowsedKeys; } - public Optional<Integer> getLimit() { - return Optional.ofNullable(limit); + public int getSkip() { + return skip; + } + + public int getLimit() { + return limit; } public static Builder builder() { @@ -74,6 +79,7 @@ public class ComponentIndexQuery { private Collection<String> qualifiers = Collections.emptyList(); private Set<String> recentlyBrowsedKeys = Collections.emptySet(); private Set<String> favoriteKeys = Collections.emptySet(); + private int skip = 0; private int limit = DEFAULT_LIMIT; private Builder() { @@ -100,6 +106,12 @@ public class ComponentIndexQuery { return this; } + public Builder setSkip(int skip) { + checkArgument(limit > 0, "Skip has to be strictly positive: %s", limit); + this.skip = skip; + return this; + } + public Builder setLimit(int limit) { checkArgument(limit > 0, "Limit has to be strictly positive: %s", limit); this.limit = limit; diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java index 12328d0f655..8c4ea02e2c9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java @@ -63,6 +63,7 @@ import static java.util.Optional.ofNullable; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.core.util.stream.MoreCollectors.toSet; +import static org.sonar.server.component.index.ComponentIndexQuery.DEFAULT_LIMIT; import static org.sonar.server.es.DefaultIndexSettings.MINIMUM_NGRAM_LENGTH; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.WsComponents.SuggestionsWsResponse.Organization; @@ -77,7 +78,7 @@ public class SuggestionsAction implements ComponentsWsAction { static final String SHORT_INPUT_WARNING = "short_input"; private static final int MAXIMUM_RECENTLY_BROWSED = 50; - static final int EXTENDED_LIMIT = 20; + private static final int EXTENDED_LIMIT = 20; private final ComponentIndex index; private final FavoriteFinder favoriteFinder; @@ -117,7 +118,7 @@ public class SuggestionsAction implements ComponentsWsAction { .setExampleValue("sonar"); action.createParam(PARAM_MORE) - .setDescription("Category, for which to display " + EXTENDED_LIMIT + " instead of " + ComponentIndexQuery.DEFAULT_LIMIT + " results") + .setDescription("Category, for which to display the next " + EXTENDED_LIMIT + " results (skipping the first " + DEFAULT_LIMIT + " results)") .setPossibleValues(stream(SuggestionCategory.values()).map(SuggestionCategory::getName).toArray(String[]::new)) .setSince("6.4"); @@ -136,7 +137,9 @@ public class SuggestionsAction implements ComponentsWsAction { String more = wsRequest.param(PARAM_MORE); Set<String> recentlyBrowsedKeys = getRecentlyBrowsedKeys(wsRequest); List<String> qualifiers = getQualifiers(more); - SuggestionsWsResponse searchWsResponse = loadSuggestions(query, more, recentlyBrowsedKeys, qualifiers); + int skip = more == null ? 0 : DEFAULT_LIMIT; + int limit = more == null ? DEFAULT_LIMIT : EXTENDED_LIMIT; + SuggestionsWsResponse searchWsResponse = loadSuggestions(query, skip, limit, recentlyBrowsedKeys, qualifiers); writeProtobuf(searchWsResponse, wsRequest, wsResponse); } @@ -148,17 +151,17 @@ public class SuggestionsAction implements ComponentsWsAction { return new HashSet<>(recentlyBrowsedParam); } - private SuggestionsWsResponse loadSuggestions(@Nullable String query, String more, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { + private SuggestionsWsResponse loadSuggestions(@Nullable String query, int skip, int limit, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { if (query == null) { - return loadSuggestionsWithoutSearch(more, recentlyBrowsedKeys, qualifiers); + return loadSuggestionsWithoutSearch(skip, limit, recentlyBrowsedKeys, qualifiers); } - return loadSuggestionsWithSearch(query, more, recentlyBrowsedKeys, qualifiers); + return loadSuggestionsWithSearch(query, skip, limit, recentlyBrowsedKeys, qualifiers); } /** * we are generating suggestions, by using (1) favorites and (2) recently browsed components (without searchin in Elasticsearch) */ - private SuggestionsWsResponse loadSuggestionsWithoutSearch(@Nullable String more, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { + private SuggestionsWsResponse loadSuggestionsWithoutSearch(int skip, int limit, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { List<ComponentDto> favoriteDtos = favoriteFinder.list(); if (favoriteDtos.isEmpty() && recentlyBrowsedKeys.isEmpty()) { return newBuilder().build(); @@ -179,12 +182,12 @@ public class SuggestionsAction implements ComponentsWsAction { Comparator<ComponentDto> favoriteComparator = Comparator.comparing(c -> favoriteUuids.contains(c.uuid()) ? -1 : +1); Comparator<ComponentDto> comparator = favoriteComparator.thenComparing(ComponentDto::name); - int limit = more == null ? ComponentIndexQuery.DEFAULT_LIMIT : EXTENDED_LIMIT; ComponentIndexResults componentsPerQualifiers = ComponentIndexResults.newBuilder().setQualifiers( qualifiers.stream().map(q -> { List<ComponentHit> hits = componentsPerQualifier.get(q) .stream() .sorted(comparator) + .skip(skip) .limit(limit) .map(ComponentDto::uuid) .map(ComponentHit::new) @@ -192,21 +195,20 @@ public class SuggestionsAction implements ComponentsWsAction { int totalHits = componentsPerQualifier.size(); return new ComponentHitsPerQualifier(q, hits, totalHits); })).build(); - return buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, componentDtos.stream()).build(); + return buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, componentDtos.stream(), skip + limit).build(); } } - private SuggestionsWsResponse loadSuggestionsWithSearch(String query, @Nullable String more, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { + private SuggestionsWsResponse loadSuggestionsWithSearch(String query, int skip, int limit, Set<String> recentlyBrowsedKeys, List<String> qualifiers) { List<ComponentDto> favorites = favoriteFinder.list(); Set<String> favoriteKeys = favorites.stream().map(ComponentDto::getKey).collect(MoreCollectors.toSet(favorites.size())); ComponentIndexQuery.Builder queryBuilder = ComponentIndexQuery.builder() .setQuery(query) .setRecentlyBrowsedKeys(recentlyBrowsedKeys) .setFavoriteKeys(favoriteKeys) - .setQualifiers(qualifiers); - if (more != null) { - queryBuilder.setLimit(EXTENDED_LIMIT); - } + .setQualifiers(qualifiers) + .setSkip(skip) + .setLimit(limit); ComponentIndexResults componentsPerQualifiers = searchInIndex(queryBuilder.build()); if (componentsPerQualifiers.isEmpty()) { return newBuilder().build(); @@ -219,7 +221,7 @@ public class SuggestionsAction implements ComponentsWsAction { .collect(toSet()); Stream<ComponentDto> componentDtoStream = dbClient.componentDao().selectByUuids(dbSession, componentUuids).stream(); Set<String> favoriteUuids = favorites.stream().map(ComponentDto::uuid).collect(MoreCollectors.toSet(favorites.size())); - SuggestionsWsResponse.Builder searchWsResponse = buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, componentDtoStream); + SuggestionsWsResponse.Builder searchWsResponse = buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, componentDtoStream, skip + limit); getWarning(query).ifPresent(searchWsResponse::setWarning); return searchWsResponse.build(); } @@ -241,12 +243,12 @@ public class SuggestionsAction implements ComponentsWsAction { } private SuggestionsWsResponse.Builder buildResponse(Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, ComponentIndexResults componentsPerQualifiers, - DbSession dbSession, Stream<ComponentDto> stream) { + DbSession dbSession, Stream<ComponentDto> stream, int coveredItems) { Map<String, ComponentDto> componentsByUuids = stream .collect(MoreCollectors.uniqueIndex(ComponentDto::uuid)); Map<String, OrganizationDto> organizationsByUuids = loadOrganizations(dbSession, componentsByUuids.values()); Map<String, ComponentDto> projectsByUuids = loadProjects(dbSession, componentsByUuids.values()); - return toResponse(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, organizationsByUuids, componentsByUuids, projectsByUuids); + return toResponse(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, organizationsByUuids, componentsByUuids, projectsByUuids, coveredItems); } private Map<String, ComponentDto> loadProjects(DbSession dbSession, Collection<ComponentDto> components) { @@ -271,18 +273,18 @@ public class SuggestionsAction implements ComponentsWsAction { } private static SuggestionsWsResponse.Builder toResponse(ComponentIndexResults componentsPerQualifiers, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, - Map<String, OrganizationDto> organizationsByUuids, Map<String, ComponentDto> componentsByUuids, Map<String, ComponentDto> projectsByUuids) { + Map<String, OrganizationDto> organizationsByUuids, Map<String, ComponentDto> componentsByUuids, Map<String, ComponentDto> projectsByUuids, int coveredItems) { if (componentsPerQualifiers.isEmpty()) { return newBuilder(); } return newBuilder() - .addAllResults(toCategories(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, componentsByUuids, organizationsByUuids, projectsByUuids)) + .addAllResults(toCategories(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, componentsByUuids, organizationsByUuids, projectsByUuids, coveredItems)) .addAllOrganizations(toOrganizations(organizationsByUuids)) .addAllProjects(toProjects(projectsByUuids)); } private static List<Category> toCategories(ComponentIndexResults componentsPerQualifiers, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, - Map<String, ComponentDto> componentsByUuids, Map<String, OrganizationDto> organizationByUuids, Map<String, ComponentDto> projectsByUuids) { + Map<String, ComponentDto> componentsByUuids, Map<String, OrganizationDto> organizationByUuids, Map<String, ComponentDto> projectsByUuids, int coveredItems) { return componentsPerQualifiers.getQualifiers().map(qualifier -> { List<Suggestion> suggestions = qualifier.getHits().stream() @@ -291,7 +293,7 @@ public class SuggestionsAction implements ComponentsWsAction { return Category.newBuilder() .setQ(qualifier.getQualifier()) - .setMore(qualifier.getNumberOfFurtherResults()) + .setMore(Math.max(0, qualifier.getTotalHits() - coveredItems)) .addAllItems(suggestions) .build(); }).collect(toList()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java index d1f51eaaaec..7192e929fc8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java @@ -19,7 +19,6 @@ */ package org.sonar.server.component.index; -import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -79,6 +78,6 @@ public class ComponentIndexQueryTest { ComponentIndexQuery query = ComponentIndexQuery.builder().setQuery("ab") .setLimit(1).build(); - assertThat(query.getLimit()).isEqualTo(Optional.of(1)); + assertThat(query.getLimit()).isEqualTo(1); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SuggestionsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SuggestionsActionTest.java index 2ffdd06bdf3..9247f5cb0ef 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SuggestionsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SuggestionsActionTest.java @@ -63,8 +63,6 @@ import static org.mockito.Mockito.mock; import static org.sonar.api.web.UserRole.USER; import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; -import static org.sonar.server.component.index.ComponentIndexQuery.DEFAULT_LIMIT; -import static org.sonar.server.component.ws.SuggestionsAction.EXTENDED_LIMIT; import static org.sonar.server.component.ws.SuggestionsAction.PARAM_MORE; import static org.sonar.server.component.ws.SuggestionsAction.PARAM_QUERY; import static org.sonar.server.component.ws.SuggestionsAction.PARAM_RECENTLY_BROWSED; @@ -274,8 +272,7 @@ public class SuggestionsActionTest { tuple("Bravo", true, false), tuple("Delta", true, false), tuple("Alpha", false, true), - tuple("Charlie", false, true) - ); + tuple("Charlie", false, true)); } @Test @@ -475,26 +472,52 @@ public class SuggestionsActionTest { } @Test - public void should_propose_to_show_more_results_if_7_projects_are_found() throws Exception { - check_proposal_to_show_more_results(7, DEFAULT_LIMIT, 1L, null); + public void should_not_propose_to_show_more_results_if_0_projects_are_found() throws Exception { + check_proposal_to_show_more_results(0, 0, 0L, null); + } + + @Test + public void should_not_propose_to_show_more_results_if_5_projects_are_found() throws Exception { + check_proposal_to_show_more_results(5, 5, 0L, null); } @Test public void should_not_propose_to_show_more_results_if_6_projects_are_found() throws Exception { - check_proposal_to_show_more_results(6, DEFAULT_LIMIT, 0L, null); + check_proposal_to_show_more_results(6, 6, 0L, null); } @Test - public void should_not_propose_to_show_more_results_if_5_projects_are_found() throws Exception { - check_proposal_to_show_more_results(5, DEFAULT_LIMIT, 0L, null); + public void should_propose_to_show_more_results_if_7_projects_are_found() throws Exception { + check_proposal_to_show_more_results(7, 6, 1L, null); } @Test - public void show_show_more_results_if_requested() throws Exception { - check_proposal_to_show_more_results(21, EXTENDED_LIMIT, 1L, SuggestionCategory.PROJECT); + public void show_more_results_if_requested_and_5_projects_are_found() throws Exception { + check_proposal_to_show_more_results(5, 0, 0L, SuggestionCategory.PROJECT); } - private void check_proposal_to_show_more_results(int numberOfProjects, int results, long numberOfMoreResults, @Nullable SuggestionCategory more) throws Exception { + @Test + public void show_more_results_if_requested_and_6_projects_are_found() throws Exception { + check_proposal_to_show_more_results(6, 0, 0L, SuggestionCategory.PROJECT); + } + + @Test + public void show_more_results_if_requested_and_7_projects_are_found() throws Exception { + check_proposal_to_show_more_results(7, 1, 0L, SuggestionCategory.PROJECT); + } + + @Test + public void show_more_results_if_requested_and_26_projects_are_found() throws Exception { + check_proposal_to_show_more_results(26, 20, 0L, SuggestionCategory.PROJECT); + } + + @Test + public void show_more_results_if_requested_and_27_projects_are_found() throws Exception { + check_proposal_to_show_more_results(27, 20, 1L, SuggestionCategory.PROJECT); + } + + private void check_proposal_to_show_more_results(int numberOfProjects, int expectedNumberOfResults, long expectedNumberOfMoreResults, @Nullable SuggestionCategory more) + throws Exception { String namePrefix = "MyProject"; List<ComponentDto> projects = range(0, numberOfProjects) @@ -511,21 +534,21 @@ public class SuggestionsActionTest { SuggestionsWsResponse response = request .executeProtobuf(SuggestionsWsResponse.class); - // assert match in qualifier "TRK" - assertThat(response.getResultsList()) - .filteredOn(q -> q.getItemsCount() > 0) - .extracting(Category::getQ) - .containsExactly(Qualifiers.PROJECT); - // include limited number of results in the response assertThat(response.getResultsList()) .flatExtracting(Category::getItemsList) - .hasSize(Math.min(results, numberOfProjects)); + .hasSize(expectedNumberOfResults); // indicate, that there are more results - assertThat(response.getResultsList()) - .filteredOn(q -> q.getItemsCount() > 0) - .extracting(Category::getMore) - .containsExactly(numberOfMoreResults); + if (expectedNumberOfResults == 0 && expectedNumberOfMoreResults == 0) { + assertThat(response.getResultsList()) + .filteredOn(q -> q.getItemsCount() > 0) + .isEmpty(); + } else { + assertThat(response.getResultsList()) + .filteredOn(q -> q.getItemsCount() > 0) + .extracting(Category::getMore) + .containsExactly(expectedNumberOfMoreResults); + } } } |