aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Schwarz <daniel.schwarz@sonarsource.com>2017-01-12 12:38:19 +0100
committerDaniel Schwarz <daniel.schwarz@sonarsource.com>2017-01-16 17:42:59 +0100
commit377c284feae0459ea81eb867a20d4de12068eedd (patch)
tree4c5a7079e6b86373d83535c21678351fd7371c9e
parent5a4c43a4b1c4d41211c141e2682c813d06c280ab (diff)
downloadsonarqube-377c284feae0459ea81eb867a20d4de12068eedd.tar.gz
sonarqube-377c284feae0459ea81eb867a20d4de12068eedd.zip
SONAR-7282 improve relevancy of results
Changes regarding index definition, code style, tests.
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndex.java25
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexDefinition.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexQuery.java33
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexComponentsStep.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/DefaultIndexSettings.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java84
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java237
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java2
11 files changed, 286 insertions, 138 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
index 64ec07cb2bf..0be403673bb 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
@@ -142,7 +142,7 @@ public class ComponentService {
private void index(String projectUuid) {
projectMeasuresIndexer.index(projectUuid);
- componentIndexer.index(projectUuid);
+ componentIndexer.indexByProjectUuid(projectUuid);
}
/**
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 360a5fa226d..e60bf8136d2 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
@@ -21,12 +21,14 @@ package org.sonar.server.component.index;
import java.util.Arrays;
import java.util.List;
+import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.sonar.core.util.stream.Collectors;
import org.sonar.server.es.BaseIndex;
+import org.sonar.server.es.DefaultIndexSettings;
import org.sonar.server.es.EsClient;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
@@ -45,33 +47,30 @@ public class ComponentIndex extends BaseIndex {
}
public List<String> search(ComponentIndexQuery query) {
- SearchRequestBuilder requestBuilder = getClient()
+ SearchRequestBuilder request = getClient()
.prepareSearch(INDEX_COMPONENTS)
.setTypes(TYPE_COMPONENT)
.setFetchSource(false);
- query.getLimit().ifPresent(requestBuilder::setSize);
+ query.getLimit().ifPresent(request::setSize);
- requestBuilder.setQuery(
- createQuery(query));
+ request.setQuery(createQuery(query));
- return Arrays.stream(requestBuilder.get().getHits().hits())
+ return Arrays.stream(request.get().getHits().hits())
.map(SearchHit::getId)
.collect(Collectors.toList());
}
private static QueryBuilder createQuery(ComponentIndexQuery query) {
BoolQueryBuilder esQuery = boolQuery();
+ query.getQualifier().ifPresent(q -> esQuery.filter(termQuery(FIELD_QUALIFIER, q)));
- query
- .getQualifiers()
- .forEach(qualifier -> esQuery.filter(termQuery(FIELD_QUALIFIER, qualifier)));
+ // We will truncate the search to the maximum length of nGrams in the index.
+ // Otherwise the search would for sure not find any results.
+ String truncatedQuery = StringUtils.left(query.getQuery(), DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH);
return esQuery
- .must(
- boolQuery()
- .should(matchQuery(FIELD_NAME + "." + SEARCH_PARTIAL_SUFFIX, query.getQuery()))
- .should(termQuery(FIELD_KEY, query.getQuery()).boost(3f))
- .minimumNumberShouldMatch(1));
+ .should(matchQuery(FIELD_NAME + "." + SEARCH_PARTIAL_SUFFIX, truncatedQuery))
+ .should(matchQuery(FIELD_KEY + "." + SORT_SUFFIX, query.getQuery()).boost(3f));
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexDefinition.java
index ed2d22cdcf4..4c0b229ec2f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexDefinition.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexDefinition.java
@@ -50,7 +50,7 @@ public class ComponentIndexDefinition implements IndexDefinition {
// type "component"
NewIndex.NewIndexType mapping = index.createType(TYPE_COMPONENT);
mapping.stringFieldBuilder(FIELD_PROJECT_UUID).build();
- mapping.stringFieldBuilder(FIELD_KEY).build();
+ mapping.stringFieldBuilder(FIELD_KEY).enableSorting().build();
mapping.stringFieldBuilder(FIELD_NAME).enableGramSearch().build();
mapping.stringFieldBuilder(FIELD_QUALIFIER).build();
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 f700206a41d..da85a3b0e41 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
@@ -19,45 +19,42 @@
*/
package org.sonar.server.component.index;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Optional;
-import java.util.Set;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
public class ComponentIndexQuery {
private final String query;
- private Set<String> qualifiers = new HashSet<>();
+ private Optional<String> qualifier = Optional.empty();
private Optional<Integer> limit = Optional.empty();
public ComponentIndexQuery(String query) {
+ requireNonNull(query);
+ checkArgument(query.length() >= 2, "Query must be at least two characters long: %s", query);
this.query = query;
}
- public ComponentIndexQuery addQualifier(String qualifier) {
- this.qualifiers.add(qualifier);
- return this;
- }
-
- public ComponentIndexQuery addQualifiers(Collection<String> qualifiers) {
- this.qualifiers.addAll(qualifiers);
+ public ComponentIndexQuery setQualifier(@Nullable String qualifier) {
+ this.qualifier = Optional.ofNullable(qualifier);
return this;
}
- public boolean hasQualifiers() {
- return !qualifiers.isEmpty();
- }
-
- public Collection<String> getQualifiers() {
- return Collections.unmodifiableSet(qualifiers);
+ public Optional<String> getQualifier() {
+ return qualifier;
}
public String getQuery() {
return query;
}
+ /**
+ * The number of search hits to return per Qualifier. Defaults to <tt>10</tt>.
+ */
public ComponentIndexQuery setLimit(int limit) {
+ checkArgument(limit >= 1, "Limit has to be strictly positive: %s", limit);
this.limit = Optional.of(limit);
return this;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
index 9c147fc268a..386711534bf 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
@@ -36,6 +36,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.server.es.BulkIndexer;
import org.sonar.server.es.EsClient;
+import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.sonar.server.component.index.ComponentIndexDefinition.FIELD_PROJECT_UUID;
import static org.sonar.server.component.index.ComponentIndexDefinition.INDEX_COMPONENTS;
@@ -56,23 +57,28 @@ public class ComponentIndexer implements Startable {
/**
* Copy all components of all projects to the elastic search index.
* <p>
- * <b>Warning</b>: This should only be called on an empty index. It does not purge anything.
+ * <b>Warning</b>: This should only be called on an empty index. It does not delete anything.
*/
public void index() {
try (DbSession dbSession = dbClient.openSession(false)) {
+ BulkIndexer bulk = new BulkIndexer(esClient, INDEX_COMPONENTS);
+ bulk.setLarge(true);
+ bulk.start();
dbClient.componentDao()
- .selectProjects(dbSession)
- .stream()
- .forEach(this::index);
+ .selectAll(dbSession, context -> {
+ ComponentDto dto = (ComponentDto) context.getResultObject();
+ bulk.add(newIndexRequest(toDocument(dto)));
+ });
+ bulk.stop();
}
}
/**
- * Update the elastic search for one specific project. The current data from the database is used.
+ * Update the index for one specific project. The current data from the database is used.
*/
- public void index(String projectUuid) {
+ public void indexByProjectUuid(String projectUuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
- purge(projectUuid);
+ deleteByProjectUuid(projectUuid);
index(
dbClient
.componentDao()
@@ -81,12 +87,12 @@ public class ComponentIndexer implements Startable {
}
}
- private void purge(String projectUuid) {
+ private void deleteByProjectUuid(String projectUuid) {
BulkIndexer.delete(esClient, INDEX_COMPONENTS, esClient
.prepareSearch(INDEX_COMPONENTS)
.setTypes(TYPE_COMPONENT)
.setFetchSource(false)
- .setQuery(termQuery(FIELD_PROJECT_UUID, projectUuid)));
+ .setQuery(boolQuery().filter(termQuery(FIELD_PROJECT_UUID, projectUuid))));
}
void index(ComponentDto... docs) {
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 29e507a7fcb..30be47d01a9 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
@@ -44,7 +44,7 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SU
public class SuggestionsAction implements ComponentsWsAction {
- static final String URL_PARAM_QUERY = "s";
+ private static final String URL_PARAM_QUERY = "s";
private static final String[] QUALIFIERS = {
Qualifiers.VIEW,
@@ -104,7 +104,7 @@ public class SuggestionsAction implements ComponentsWsAction {
private Optional<Qualifier> getResultsOfQualifier(String query, String qualifier) {
ComponentIndexQuery componentIndexQuery = new ComponentIndexQuery(query)
- .addQualifier(qualifier)
+ .setQualifier(qualifier)
.setLimit(NUMBER_OF_RESULTS_PER_QUALIFIER);
List<String> uuids = searchInIndex(componentIndexQuery);
@@ -128,7 +128,7 @@ public class SuggestionsAction implements ComponentsWsAction {
return index.search(componentIndexQuery);
}
- public List<ComponentDto> fetchFromDatabase(List<String> uuids) {
+ private List<ComponentDto> fetchFromDatabase(List<String> uuids) {
DbSession dbSession = dbClient.openSession(false);
try {
return dbClient.componentDao().selectByUuids(dbSession, uuids);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexComponentsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexComponentsStep.java
index 058dc894797..40f38b6ce73 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexComponentsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/IndexComponentsStep.java
@@ -40,7 +40,7 @@ public class IndexComponentsStep implements ComputationStep {
public void execute() {
String projectUuid = treeRootHolder.getRoot().getUuid();
resourceIndexDao.indexProject(projectUuid);
- elasticSearchIndexer.index(projectUuid);
+ elasticSearchIndexer.indexByProjectUuid(projectUuid);
}
@Override
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/DefaultIndexSettings.java b/server/sonar-server/src/main/java/org/sonar/server/es/DefaultIndexSettings.java
index 3fe2311efe6..67344cce005 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/DefaultIndexSettings.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/DefaultIndexSettings.java
@@ -22,7 +22,10 @@ package org.sonar.server.es;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
-class DefaultIndexSettings {
+public class DefaultIndexSettings {
+
+ /** Maximum length of ngrams. */
+ public static final int MAXIMUM_NGRAM_LENGTH = 15;
private DefaultIndexSettings() {
// only static stuff
@@ -72,7 +75,7 @@ class DefaultIndexSettings {
// Edge NGram filter
.put("index.analysis.filter.gram_filter.type", "nGram")
.put("index.analysis.filter.gram_filter.min_gram", 2)
- .put("index.analysis.filter.gram_filter.max_gram", 15)
+ .put("index.analysis.filter.gram_filter.max_gram", MAXIMUM_NGRAM_LENGTH)
.putArray("index.analysis.filter.gram_filter.token_chars", "letter", "digit", "punctuation", "symbol")
// Word filter
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
new file mode 100644
index 00000000000..3fd67b6df54
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexQueryTest.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.component.index;
+
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ComponentIndexQueryTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void should_fail_with_IAE_if_query_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Query must be at least two characters long");
+
+ new ComponentIndexQuery("");
+ }
+
+ @Test
+ public void should_fail_with_IAE_if_query_is_one_character_long() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Query must be at least two characters long");
+
+ new ComponentIndexQuery("a");
+ }
+
+ @Test
+ public void should_support_query_with_two_characters_long() {
+ ComponentIndexQuery query = new ComponentIndexQuery("ab");
+
+ assertThat(query.getQuery()).isEqualTo("ab");
+ }
+
+ @Test
+ public void should_fail_with_IAE_if_limit_is_negative() {
+ ComponentIndexQuery query = new ComponentIndexQuery("ab");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Limit has to be strictly positive");
+
+ query.setLimit(-1);
+ }
+
+ @Test
+ public void should_fail_with_IAE_if_limit_is_zero() {
+ ComponentIndexQuery query = new ComponentIndexQuery("ab");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Limit has to be strictly positive");
+
+ query.setLimit(0);
+ }
+
+ @Test
+ public void should_support_positive_limit() {
+ ComponentIndexQuery query = new ComponentIndexQuery("ab")
+ .setLimit(1);
+
+ assertThat(query.getLimit()).isEqualTo(Optional.of(1));
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java
index 4ed8479ea64..c9bfe62b3e2 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexTest.java
@@ -19,10 +19,10 @@
*/
package org.sonar.server.component.index;
-import java.util.Collection;
+import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import org.assertj.core.api.AbstractListAssert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -36,23 +36,10 @@ import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.organization.OrganizationTesting;
import org.sonar.server.es.EsTester;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
public class ComponentIndexTest {
- private static final String BLA = "bla";
- private static final String UUID_DOC = "UUID-DOC-";
- private static final String UUID_DOC_1 = UUID_DOC + "1";
- private static final String KEY = "KEY-";
- private static final String KEY_1 = KEY + "1";
-
- private static final String PREFIX = "Son";
- private static final String MIDDLE = "arQ";
- private static final String SUFFIX = "ube";
- private static final String PREFIX_MIDDLE_SUFFIX = PREFIX + MIDDLE + SUFFIX;
-
@Rule
public EsTester es = new EsTester(new ComponentIndexDefinition(new MapSettings()));
@@ -71,133 +58,205 @@ public class ComponentIndexTest {
}
@Test
- public void empty_search() {
- assertSearch(emptyList(), BLA, emptyList());
+ public void return_empty_list_if_no_fields_match_query() {
+ indexProject("struts", "Apache Struts");
+
+ assertThat(index.search(new ComponentIndexQuery("missing"))).isEmpty();
}
@Test
- public void exact_match_search() {
- assertMatch(BLA, BLA);
+ public void search_projects_by_exact_name() {
+ ComponentDto struts = indexProject("struts", "Apache Struts");
+ indexProject("sonarqube", "SonarQube");
+
+ assertSearchResults("Apache Struts", struts);
+ assertSearchResults("APACHE STRUTS", struts);
+ assertSearchResults("APACHE struTS", struts);
}
@Test
- public void ignore_case() {
- assertMatch("bLa", "BlA");
+ public void search_file_with_long_name() {
+ ComponentDto project = indexProject("struts", "Apache Struts");
+ ComponentDto file1 = indexFile(project, "src/main/java/DefaultRubyComponentServiceTestManagerFactory.java", "DefaultRubyComponentServiceTestManagerFactory.java");
+
+ assertSearchResults("DefaultRubyComponentServiceTestManagerFactory", file1);
+ assertSearchResults("DefaultRubyComponentServiceTestManagerFactory.java", file1);
+ assertSearchResults("RubyComponentServiceTestManager", file1);
+ assertSearchResults("te", file1);
}
@Test
- public void search_for_different_qualifier() {
+ public void should_search_by_name_with_two_characters() {
+ ComponentDto project = indexProject("struts", "Apache Struts");
- // create a component of type project
- ComponentDto project = ComponentTesting
- .newProjectDto(organization, UUID_DOC_1)
- .setName(BLA)
- .setKey(BLA);
+ assertSearchResults("st", project);
+ assertSearchResults("tr", project);
+ }
- // search for components of type file
- ComponentIndexQuery fileQuery = new ComponentIndexQuery(BLA);
- fileQuery.addQualifier(Qualifiers.FILE);
+ @Test
+ public void search_projects_by_partial_name() {
+ ComponentDto struts = indexProject("struts", "Apache Struts");
- // should not have any results
- assertThat(search(asList(project), fileQuery)).isEmpty();
+ assertSearchResults("truts", struts);
+ assertSearchResults("pache", struts);
+ assertSearchResults("apach", struts);
+ assertSearchResults("che stru", struts);
}
@Test
- public void prefix_match_search() {
- assertMatch(PREFIX_MIDDLE_SUFFIX, PREFIX);
+ public void search_projects_and_files_by_partial_name() {
+ ComponentDto project = indexProject("struts", "Apache Struts");
+ ComponentDto file1 = indexFile(project, "src/main/java/StrutsManager.java", "StrutsManager.java");
+ indexFile(project, "src/main/java/Foo.java", "Foo.java");
+
+ assertSearchResults("struts", project, file1);
+ assertSearchResults("Struts", project, file1);
+ assertSearchResults("StrutsManager", file1);
+ assertSearchResults("STRUTSMA", file1);
+ assertSearchResults("utsManag", file1);
}
@Test
- public void middle_match_search() {
- assertMatch(PREFIX_MIDDLE_SUFFIX, MIDDLE);
+ public void should_find_file_by_file_extension() {
+ ComponentDto project = indexProject("struts", "Apache Struts");
+ ComponentDto file1 = indexFile(project, "src/main/java/StrutsManager.java", "StrutsManager.java");
+ ComponentDto file2 = indexFile(project, "src/main/java/Foo.java", "Foo.java");
+
+ assertSearchResults(".java", file1, file2);
+ assertSearchResults("manager.java", file1);
+
+ // do not match
+ assertNoSearchResults("strutsmanager.txt");
+ assertNoSearchResults("strutsmanagerjava");
+ assertNoSearchResults("somethingStrutsManager.java");
}
@Test
- public void suffix_match_search() {
- assertMatch(PREFIX_MIDDLE_SUFFIX, SUFFIX);
+ public void should_search_projects_by_exact_case_insensitive_key() {
+ ComponentDto project1 = indexProject("keyOne", "Project One");
+ indexProject("keyTwo", "Project Two");
+
+ assertSearchResults("keyOne", project1);
+ assertSearchResults("keyone", project1);
+ assertSearchResults("KEYone", project1);
}
@Test
- public void exact_match_should_be_shown_first() {
- ComponentDto good = newDoc(UUID_DOC + 1, "Current SonarQube Plattform");
- ComponentDto better = newDoc(UUID_DOC + 2, "SonarQube Plattform");
+ public void should_search_project_with_dot_in_key() {
+ ComponentDto project = indexProject("org.sonarqube", "SonarQube");
- assertThat(search(asList(good, better), "SonarQube"))
- .containsExactly(better.uuid(), good.uuid());
+ assertSearchResults("org.sonarqube", project);
+ assertNoSearchResults("orgsonarqube");
}
@Test
- public void do_not_interpret_input() {
- assertNotMatch(BLA, "*");
+ public void should_search_project_with_dash_in_key() {
+ ComponentDto project = indexProject("org-sonarqube", "SonarQube");
+
+ assertSearchResults("org-sonarqube", project);
+ assertNoSearchResults("orgsonarqube");
}
@Test
- public void key_match_search() {
- assertSearch(
- asList(newDoc(UUID_DOC_1, "name is not a match", "matchingKey")),
- "matchingKey",
- asList(UUID_DOC_1));
+ public void should_search_project_with_colon_in_key() {
+ ComponentDto project = indexProject("org:sonarqube", "SonarQube");
+
+ assertSearchResults("org:sonarqube", project);
+ assertNoSearchResults("orgsonarqube");
+ assertNoSearchResults("org-sonarqube");
+ assertNoSearchResults("org_sonarqube");
}
@Test
- public void unmatching_search() {
- assertNotMatch(BLA, "blubb");
+ public void should_search_project_with_all_special_characters_in_key() {
+ ComponentDto project = indexProject("org.sonarqube:sonar-sérvèr_ç", "SonarQube");
+
+ assertSearchResults("org.sonarqube:sonar-sérvèr_ç", project);
}
@Test
- public void limit_number_of_documents() {
- Collection<ComponentDto> docs = IntStream
- .rangeClosed(1, 42)
- .mapToObj(i -> newDoc(UUID_DOC + i, BLA, KEY + i))
- .collect(Collectors.toList());
+ public void should_not_return_results_when_searching_by_partial_key() {
+ indexProject("theKey", "the name");
- int pageSize = 41;
- ComponentIndexQuery componentIndexQuery = new ComponentIndexQuery(BLA)
- .addQualifier(Qualifiers.PROJECT)
- .setLimit(pageSize);
- assertThat(search(docs, componentIndexQuery)).hasSize(pageSize);
+ assertNoSearchResults("theke");
+ assertNoSearchResults("hekey");
}
- private void assertMatch(String name, String query) {
- assertSearch(
- asList(newDoc(name)),
- query,
- asList(UUID_DOC_1));
+ @Test
+ public void filter_results_by_qualifier() {
+ ComponentDto project = indexProject("struts", "Apache Struts");
+ indexFile(project, "src/main/java/StrutsManager.java", "StrutsManager.java");
+
+ assertSearchResults(new ComponentIndexQuery("struts").setQualifier(Qualifiers.PROJECT), project);
}
- private void assertNotMatch(String name, String query) {
- assertSearch(
- asList(newDoc(name)),
- query,
- emptyList());
+ @Test
+ public void should_order_results_by_score() {
+ ComponentDto project1 = indexProject("keyOne", "Struts");
+ ComponentDto project2 = indexProject("keyTwo", "Apache Struts Two");
+ ComponentDto project3 = indexProject("keyThree", "Apache Struts");
+
+ assertSearch("struts").containsExactly(project1.uuid(), project3.uuid(), project2.uuid());
}
- private ComponentDto newDoc(String name) {
- return newDoc(UUID_DOC_1, name);
+ @Test
+ public void should_prefer_key_matching_over_name_matching() {
+ ComponentDto project1 = indexProject("quality", "SonarQube");
+ ComponentDto project2 = indexProject("sonarqube", "Quality Product");
+
+ assertSearch("sonarqube").containsExactly(project2.uuid(), project1.uuid());
+ }
+
+ @Test
+ public void should_limit_the_number_of_results() {
+ IntStream.rangeClosed(0, 10).forEach(i -> indexProject("sonarqube" + i, "SonarQube" + i));
+
+ assertSearch(new ComponentIndexQuery("sonarqube").setLimit(5)).hasSize(5);
+ }
+
+ @Test
+ public void should_not_support_wildcards() {
+ indexProject("theKey", "the name");
+
+ assertNoSearchResults("*the*");
+ assertNoSearchResults("th?Key");
+ }
+
+ private AbstractListAssert<?, ? extends List<? extends String>, String> assertSearch(String query) {
+ return assertSearch(new ComponentIndexQuery(query));
}
- private ComponentDto newDoc(String uuid, String name) {
- return newDoc(uuid, name, KEY_1);
+ private AbstractListAssert<?, ? extends List<? extends String>, String> assertSearch(ComponentIndexQuery query) {
+ return assertThat(index.search(query));
}
- private ComponentDto newDoc(String uuid, String name, String key) {
- return ComponentTesting
- .newProjectDto(organization, uuid)
- .setName(name)
- .setKey(key);
+ private void assertSearchResults(String query, ComponentDto... expectedComponents) {
+ assertSearchResults(new ComponentIndexQuery(query), expectedComponents);
}
- private void assertSearch(Collection<ComponentDto> input, String queryText, Collection<String> expectedOutput) {
- assertThat(search(input, queryText))
- .hasSameElementsAs(expectedOutput);
+ private void assertSearchResults(ComponentIndexQuery query, ComponentDto... expectedComponents) {
+ String[] expectedUuids = Arrays.stream(expectedComponents).map(ComponentDto::uuid).toArray(String[]::new);
+ assertSearch(query).containsOnly(expectedUuids);
}
- private List<String> search(Collection<ComponentDto> input, String query) {
- return search(input, new ComponentIndexQuery(query).addQualifier(Qualifiers.PROJECT));
+ private void assertNoSearchResults(String query) {
+ assertSearchResults(query);
}
- private List<String> search(Collection<ComponentDto> input, ComponentIndexQuery query) {
- input.stream().forEach(indexer::index);
- return index.search(query);
+ private ComponentDto indexProject(String key, String name) {
+ ComponentDto dto = ComponentTesting.newProjectDto(organization, "UUID_" + key)
+ .setKey(key)
+ .setName(name);
+ indexer.index(dto);
+ return dto;
}
+
+ private ComponentDto indexFile(ComponentDto project, String fileKey, String fileName) {
+ ComponentDto dto = ComponentTesting.newFileDto(project)
+ .setKey(fileKey)
+ .setName(fileName);
+ indexer.index(dto);
+ return dto;
+ }
+
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java
index 64e89c164f0..23bea430a5b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java
@@ -143,7 +143,7 @@ public class ComponentIndexerTest {
}
private void index(String uuid) {
- createIndexer().index(uuid);
+ createIndexer().indexByProjectUuid(uuid);
}
private long count() {