diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java index 2c8b90e565de47d744a32a680231d115806c7638..2bdea4961d12421af92a5b6f94abe87699c305a5 100644 --- a/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java +++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java @@ -4,14 +4,14 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import at.ac.uibk.gitsearch.config.ApplicationProperties; +import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; import at.ac.uibk.gitsearch.service.dto.SearchInputDTO; import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO; import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO.GitProject; @@ -40,7 +41,7 @@ import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO.SearchResultDTO; @Repository public class MetaDataRepository { - + /** * just a local wrapper, to decrypt JSON * @@ -66,8 +67,6 @@ public class MetaDataRepository { this.project = project; } - - } private final RestHighLevelClient elasticsearchClient; @@ -83,10 +82,11 @@ public class MetaDataRepository { } /** - * contains for each Field, a list of Strings mapped to a completion. - * E.g. "metadata.creator" -> Kerstin -> {"Kerstin Limbeck", "Kerstin Mair"} + * contains for each Field, a list of Strings mapped to a completion. Together + * with hit counts E.g. "metadata.creator" -> Kerstin -> {"Kerstin Limbeck" -> + * 3, "Kerstin Mair" -> 2)} */ - private Map<String, Map<String, Set<String>>> cachedCompletions = null; + private Map<String, Map<String, Map<String, Integer>>> cachedCompletions = null; private static final int MAX_AUTO_COMPLETION_RESULTS = 10; @@ -97,7 +97,7 @@ public class MetaDataRepository { * @return * @throws IOException */ - public List<String> getKeywordsAutoComplete(String keyWordPrefix) throws IOException { + public List<AutoCompleteEntry> getKeywordsAutoComplete(String keyWordPrefix) throws IOException { return getFieldAutoCompletion(keyWordPrefix, SearchRepositoryConstants.METADATA_KEYWORDS); } @@ -108,8 +108,9 @@ public class MetaDataRepository { * @return * @throws IOException */ - public List<String> getProgrammingLanguageAutoComplete(String progammingLanguagePrefix) throws IOException { - return getFieldAutoCompletion(progammingLanguagePrefix, SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES); + public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(String progammingLanguagePrefix) throws IOException { + return getFieldAutoCompletion(progammingLanguagePrefix, + SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES); } /** @@ -119,7 +120,7 @@ public class MetaDataRepository { * @return * @throws IOException */ - public List<String> getCreatorAutoComplete(String creatorPrefix) throws IOException { + public List<AutoCompleteEntry> getCreatorAutoComplete(String creatorPrefix) throws IOException { return getFieldAutoCompletion(creatorPrefix, SearchRepositoryConstants.METADATA_CREATOR); } @@ -130,67 +131,115 @@ public class MetaDataRepository { * @return * @throws IOException */ - public List<String> getContributorAutoComplete(String contributorPrefix) throws IOException { + public List<AutoCompleteEntry> getContributorAutoComplete(String contributorPrefix) throws IOException { return getFieldAutoCompletion(contributorPrefix, SearchRepositoryConstants.METADATA_CONTRIBUTOR); } - private List<String> getFieldAutoCompletion(String keyWordPrefix, final String metaDataField) + /** + * returns a list of hits together with the number of occurences + * @param keyWordPrefix + * @param metaDataField + * @return + * @throws IOException + * @throws JsonProcessingException + * @throws JsonMappingException + */ + private List<AutoCompleteEntry> getFieldAutoCompletion(String keyWordPrefix, final String metaDataField) throws IOException, JsonProcessingException, JsonMappingException { fillAutoCompletion(); - return cachedCompletions.get(metaDataField).entrySet().stream() - .filter(s -> s.getKey().startsWith(keyWordPrefix)) - .map(Map.Entry::getValue) - .flatMap(java.util.Collection::stream).distinct() - .limit(MAX_AUTO_COMPLETION_RESULTS).collect(Collectors.toList()); + + // höllisch kompliziert und höllisch unverständlich mit Streams :-) + // idea + // 1. Filtern nach Prefix + // 2. Zusammenzählen der möglichen Hits (Besser Max statt Sum?) + // 3. Auf MAX_AUTO_COMPLETION_RESULTS begrenzen und nur die Trefferstrings + // ausgeben. + final Stream<Entry<String, Map<String, Integer>>> filteredEntries = cachedCompletions.get(metaDataField) + .entrySet().stream() // Map<String, Map<String, Integer>> -> Stream<Entry<String, Map<String, + // Integer>> + .filter(s -> s.getKey().startsWith(keyWordPrefix)); + final Map<String, Integer> combinedHits = filteredEntries // s = String -> bool -> Stream<Entry<String, + // Map<String, Integer>> + .flatMap(e -> e.getValue().entrySet().stream()) // Stream< Map<String, Integer>> with duplicates + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum)); + return combinedHits.entrySet().stream().sorted((e1, e2) -> e2.getValue() - e1.getValue()) + .limit(MAX_AUTO_COMPLETION_RESULTS).map(s -> new AutoCompleteEntry(s.getKey(), s.getValue())).collect(Collectors.toList()); // Map.Entry<String, + // Map<String, + // Integer>> + } private synchronized void fillAutoCompletion() throws IOException, JsonProcessingException, JsonMappingException { if (cachedCompletions == null) { - Map<String, Map<String, Set<String>>> reloadedCachedCompletions = new HashMap<>(); + Map<String, Map<String, Map<String, Integer>>> reloadedCachedCompletions = new HashMap<>(); reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_KEYWORDS, new TreeMap<>()); reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_CREATOR, new TreeMap<>()); reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_CONTRIBUTOR, new TreeMap<>()); reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES, new TreeMap<>()); SearchRequest searchRequest = new SearchRequest(SearchRepositoryConstants.INDEX_METADATA); - SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().size(1000).from(0); - - searchRequest.source(sourceBuilder); - - SearchResponse searchResponse = elasticsearchClient.search(searchRequest, RequestOptions.DEFAULT); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JavaTimeModule()); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - for (SearchHit searchHit : searchResponse.getHits().getHits()) { - final String sourceAsJSON = searchHit.getSourceAsString(); - SearchResultDTOWrapper entry = objectMapper.readValue(sourceAsJSON, SearchResultDTOWrapper.class); - if (entry.getMetadata().getKeyword() != null) - for (String keyWord : entry.getMetadata().getKeyword()) - addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_KEYWORDS), split(keyWord), keyWord); - if (entry.getMetadata().getContributor() != null) - for (Person contributor : entry.getMetadata().getContributor()) - addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CONTRIBUTOR), split(contributor.getName()), contributor.getName()); - if (entry.getMetadata().getCreator() != null) - for (Person creator : entry.getMetadata().getCreator()) - addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CREATOR), - split(creator.getName()), creator.getName()); - if (entry.getMetadata().getProgrammingLanguage() != null) - for (String language : entry.getMetadata().getProgrammingLanguage()) - addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES), Collections.singletonList(language), language); + + final int pageSize = 20; + int currentPage = 0; + boolean tryNextPage = true; + + while (tryNextPage) { + + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().size(pageSize).from(currentPage++*pageSize); + + searchRequest.source(sourceBuilder); + + SearchResponse searchResponse = elasticsearchClient.search(searchRequest, RequestOptions.DEFAULT); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + tryNextPage = searchResponse.getHits().getHits().length > 0; + + for (SearchHit searchHit : searchResponse.getHits().getHits()) { + final String sourceAsJSON = searchHit.getSourceAsString(); + SearchResultDTOWrapper entry = objectMapper.readValue(sourceAsJSON, SearchResultDTOWrapper.class); + if (entry.getMetadata().getKeyword() != null) + for (String keyWord : entry.getMetadata().getKeyword()) + addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_KEYWORDS), + split(keyWord), keyWord); + if (entry.getMetadata().getContributor() != null) + for (Person contributor : entry.getMetadata().getContributor()) + addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CONTRIBUTOR), + split(contributor.getName()), contributor.getName()); + if (entry.getMetadata().getCreator() != null) + for (Person creator : entry.getMetadata().getCreator()) + addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CREATOR), + split(creator.getName()), creator.getName()); + if (entry.getMetadata().getProgrammingLanguage() != null) + for (String language : entry.getMetadata().getProgrammingLanguage()) + addTo(reloadedCachedCompletions + .get(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES), + Collections.singletonList(language), language); + } } cachedCompletions = reloadedCachedCompletions; } } - private void addTo(Map<String, Set<String>> completionMap, List<String> tokenList, String target) { + private void addTo(Map<String, Map<String, Integer>> completionMap, List<String> tokenList, String target) { tokenList.forEach(token -> { - Set<String> existingTargets = completionMap.get(token); - if(existingTargets == null) { - existingTargets = new HashSet<>(); - completionMap.put(token, existingTargets); + Map<String, Integer> existingTargets = completionMap.get(token); + if (existingTargets == null) { // none found + existingTargets = new HashMap<>(); + existingTargets.put(target, 1); + completionMap.put(token, existingTargets); + } else { + final Integer count = existingTargets.get(target); + if (count == null) { + existingTargets.put(target, 1); + } else { + existingTargets.put(target, count + 1); + } + } - existingTargets.add(target); +// existingTargets.add(target); }); } @@ -209,55 +258,44 @@ public class MetaDataRepository { BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); forEachTerm(searchInputDTO.getFulltextQuery(), - term -> - queryBuilder.must(QueryBuilders.multiMatchQuery( - term, - SearchRepositoryConstants.METADATA_DESCRIPTION, - SearchRepositoryConstants.METADATA_KEYWORDS, - SearchRepositoryConstants.METADATA_TITLE) - .type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX) - )); - - forEachTerm(searchInputDTO.getMetadata().getProgrammingLanguage(), - term -> - queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES, term))); - - forEachTerm(searchInputDTO.getMetadata().getKeyword(), - term -> - queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_KEYWORDS, term))); - - if(searchInputDTO.getMetadata().getAuthor()!=null) - queryBuilder.must(QueryBuilders.multiMatchQuery(searchInputDTO.getMetadata().getAuthor(), - SearchRepositoryConstants.METADATA_CREATOR, - SearchRepositoryConstants.METADATA_CONTRIBUTOR, - SearchRepositoryConstants.METADATA_PUBLISHER).type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX)); + term -> queryBuilder.must(QueryBuilders + .multiMatchQuery(term, SearchRepositoryConstants.METADATA_DESCRIPTION, + SearchRepositoryConstants.METADATA_KEYWORDS, SearchRepositoryConstants.METADATA_TITLE) + .type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX))); + + forEachTerm(searchInputDTO.getMetadata().getProgrammingLanguage(), term -> queryBuilder + .must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES, term))); + + forEachTerm(searchInputDTO.getMetadata().getKeyword(), term -> queryBuilder + .must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_KEYWORDS, term))); + + if (searchInputDTO.getMetadata().getAuthor() != null) + queryBuilder.must(QueryBuilders + .multiMatchQuery(searchInputDTO.getMetadata().getAuthor(), + SearchRepositoryConstants.METADATA_CREATOR, SearchRepositoryConstants.METADATA_CONTRIBUTOR, + SearchRepositoryConstants.METADATA_PUBLISHER) + .type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX)); // forEachTerm(searchInputDTO.getMetadata().getAuthor(), // term -> // queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_CREATOR, term))); - forEachTerm(searchInputDTO.getMetadata().getLicense(), - term -> - queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_LICENSE, term))); + forEachTerm(searchInputDTO.getMetadata().getLicense(), + term -> queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_LICENSE, term))); - char[] boundaryChars = {'\n'}; + char[] boundaryChars = { '\n' }; - HighlightBuilder highlightBuilder = new HighlightBuilder() - .highlighterType("plain") - .preTags(properties.getSearch().getHighlightPre()) - .postTags(properties.getSearch().getHighlightPost()) - .field(SearchRepositoryConstants.METADATA_DESCRIPTION) - .boundaryChars(boundaryChars) - .boundaryScannerType(HighlightBuilder.BoundaryScannerType.SENTENCE) - .fragmentSize(FRAGMENT_SIZE); + HighlightBuilder highlightBuilder = new HighlightBuilder().highlighterType("plain") + .preTags(properties.getSearch().getHighlightPre()).postTags(properties.getSearch().getHighlightPost()) + .field(SearchRepositoryConstants.METADATA_DESCRIPTION).boundaryChars(boundaryChars) + .boundaryScannerType(HighlightBuilder.BoundaryScannerType.SENTENCE).fragmentSize(FRAGMENT_SIZE); - // String[] includes = {}; + // String[] includes = {}; // String[] excludes = {HighlightRepositoryConstants.CONTENT_FIELD}; SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); - sourceBuilder.query(queryBuilder) - .highlighter(highlightBuilder) - .size(pageSize).from((searchInputDTO.getPage()) * pageSize); + sourceBuilder.query(queryBuilder).highlighter(highlightBuilder).size(pageSize) + .from((searchInputDTO.getPage()) * pageSize); // .fetchSource(includes, excludes); searchRequest.source(sourceBuilder); @@ -271,7 +309,8 @@ public class MetaDataRepository { final SearchResultsDTO results = new SearchResultsDTO(); List<SearchResultDTO> searchResults = new ArrayList<>(); for (SearchHit searchHit : searchResponse.getHits().getHits()) { - SearchResultDTOWrapper entry = objectMapper.readValue(searchHit.getSourceAsString(), SearchResultDTOWrapper.class); + SearchResultDTOWrapper entry = objectMapper.readValue(searchHit.getSourceAsString(), + SearchResultDTOWrapper.class); final SearchResultDTO metadata = entry.getMetadata(); metadata.setProject(entry.getProject()); searchResults.add(metadata); @@ -281,12 +320,13 @@ public class MetaDataRepository { results.setSearchResult(searchResults); return results; } - + /** Helper **/ private void forEachTerm(String searchTerm, Consumer<String> exec) { - if(searchTerm == null) return; + if (searchTerm == null) + return; StringTokenizer st = new StringTokenizer(searchTerm); - while(st.hasMoreTokens()) { + while (st.hasMoreTokens()) { exec.accept(st.nextToken()); } } diff --git a/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java index 09f5af13f4570986bb16e3d45a89e4e7bea9149f..2e1bb5a94c12a16ad99ab724acccd488895285ee 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java @@ -7,6 +7,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; import java.util.Optional; import java.util.stream.Collectors; @@ -28,6 +29,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import at.ac.uibk.gitsearch.repository.search.MetaDataRepository; import at.ac.uibk.gitsearch.security.jwt.TokenProvider; +import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; import at.ac.uibk.gitsearch.service.dto.SearchInputDTO; import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO; import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO.SearchResultDTO; @@ -60,7 +62,7 @@ public class SearchService { * @return * @throws IOException */ - public List<String> getKeywordsAutoComplete(String keyWordPrefix) throws IOException { + public List<AutoCompleteEntry> getKeywordsAutoComplete(String keyWordPrefix) throws IOException { return metaDataRepository.getKeywordsAutoComplete(keyWordPrefix); } @@ -71,7 +73,7 @@ public class SearchService { * @return * @throws IOException */ - public List<String> getCreatorAutoComplete(String creatorPrefix) throws IOException { + public List<AutoCompleteEntry> getCreatorAutoComplete(String creatorPrefix) throws IOException { return metaDataRepository.getCreatorAutoComplete(creatorPrefix); } @@ -82,7 +84,7 @@ public class SearchService { * @return * @throws IOException */ - public List<String> getContributorAutoComplete(String contributorPrefix) throws IOException { + public List<AutoCompleteEntry> getContributorAutoComplete(String contributorPrefix) throws IOException { return metaDataRepository.getContributorAutoComplete(contributorPrefix); } @@ -93,7 +95,7 @@ public class SearchService { * @return * @throws IOException */ - public List<String> getProgrammingLanguageAutoComplete(String programmingLanguagePrefix) throws IOException { + public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(String programmingLanguagePrefix) throws IOException { return metaDataRepository.getProgrammingLanguageAutoComplete(programmingLanguagePrefix); } diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..c39397da596acb154a45404b077f4953830847b6 --- /dev/null +++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java @@ -0,0 +1,41 @@ +package at.ac.uibk.gitsearch.service.dto; + +/** + * just contains a pair of a target string and the hit count. + * @author Michael Breu + * + */ +public class AutoCompleteEntry { + private String target; + private int hitCount; + + public AutoCompleteEntry(String target, int hitCount) { + super(); + this.target = target; + this.hitCount = hitCount; + } + + public AutoCompleteEntry(String target) { + super(); + this.target = target; + this.hitCount = 1; + } + + public void increaseHitCount() { + this.hitCount++; + } + + /** + * @return the target + */ + public String getTarget() { + return target; + } + /** + * @return the hitCount + */ + public int getHitCount() { + return hitCount; + } + +} \ No newline at end of file diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java index 49c7793445a48e69ad4b554a5484f1267e050a94..5c77753c0c1b2559a41f11744ee388f683676528 100644 --- a/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java +++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java @@ -2,6 +2,7 @@ package at.ac.uibk.gitsearch.web.rest; import java.io.IOException; import java.util.List; +import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController; import at.ac.uibk.gitsearch.es.model.DocumentInfo; import at.ac.uibk.gitsearch.service.SearchService; +import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; import at.ac.uibk.gitsearch.service.dto.SearchInputDTO; import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO; @@ -56,7 +58,7 @@ public class SearchResource { * @throws IOException */ @GetMapping("/search/keywordsAutoComplete") - public List<String> getKeywordsAutoComplete(@RequestParam String keyWordPrefix) throws IOException { + public List<AutoCompleteEntry> getKeywordsAutoComplete(@RequestParam String keyWordPrefix) throws IOException { return searchService.getKeywordsAutoComplete(keyWordPrefix); } @@ -68,7 +70,7 @@ public class SearchResource { * @throws IOException */ @GetMapping("/search/creatorAutoComplete") - public List<String> getCreatorAutoComplete(@RequestParam String creatorPrefix) throws IOException { + public List<AutoCompleteEntry> getCreatorAutoComplete(@RequestParam String creatorPrefix) throws IOException { return searchService.getCreatorAutoComplete(creatorPrefix); } @@ -80,7 +82,7 @@ public class SearchResource { * @throws IOException */ @GetMapping("/search/contributorAutoComplete") - public List<String> getContributorAutoComplete(@RequestParam String contributorPrefix) throws IOException { + public List<AutoCompleteEntry> getContributorAutoComplete(@RequestParam String contributorPrefix) throws IOException { return searchService.getContributorAutoComplete(contributorPrefix); } @@ -92,7 +94,7 @@ public class SearchResource { * @throws IOException */ @GetMapping("/search/programmingLanguageAutoComplete") - public List<String> getProgrammingLanguageAutoComplete(@RequestParam String programmingLanguagePrefix) throws IOException { + public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(@RequestParam String programmingLanguagePrefix) throws IOException { return searchService.getProgrammingLanguageAutoComplete(programmingLanguagePrefix); } diff --git a/src/main/webapp/app/search/service/search-service.ts b/src/main/webapp/app/search/service/search-service.ts index 8fb2dc0f36d8064ed1bc56d922ffd76355c0a2c1..55fd7220f4c51c107cf7cd8382e4d5358cc6037f 100644 --- a/src/main/webapp/app/search/service/search-service.ts +++ b/src/main/webapp/app/search/service/search-service.ts @@ -1,12 +1,11 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; import { SERVER_API_URL } from 'app/app.constants'; import { SearchResultsDTO } from 'app/shared/model/search/search-results-dto.model'; import { SearchInput } from 'app/shared/model/search/search-input.model'; -type EntityResponseTypeAutoComplete = HttpResponse<Array<string>>; @Injectable({ providedIn: 'root' }) export class SearchService { @@ -21,27 +20,38 @@ export class SearchService { return this.http.post<SearchResultsDTO>(this.resourceSearchUrlPageDetails, searchInput); } - getKeywordsAutoComplete(prefix: string): Observable<Array<string>> { + getKeywordsAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> { const options: HttpParams = new HttpParams(); options.append('keyWordPrefix', prefix); - return this.http.get<Array<string>>(this.resourceKeywordsAutoCompleteDetails, { + return this.http.get<Array<AutoCompletionEntry>>(this.resourceKeywordsAutoCompleteDetails, { params: new HttpParams().set('keyWordPrefix', prefix), }); } - getProgrammingLanguageAutoComplete(prefix: string): Observable<Array<string>> { + getProgrammingLanguageAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> { const options: HttpParams = new HttpParams(); options.append('keyWordPrefix', prefix); - return this.http.get<Array<string>>(this.resourceProgrammingLanguageAutoCompleteDetails, { + return this.http.get<Array<AutoCompletionEntry>>(this.resourceProgrammingLanguageAutoCompleteDetails, { params: new HttpParams().set('programmingLanguagePrefix', prefix), }); } - getContributorAutoComplete(prefix: string): Observable<Array<string>> { + getContributorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> { const options: HttpParams = new HttpParams(); options.append('keyWordPrefix', prefix); - return this.http.get<Array<string>>(this.resourceContributorAutoCompleteDetails, { + return this.http.get<Array<AutoCompletionEntry>>(this.resourceContributorAutoCompleteDetails, { params: new HttpParams().set('contributorPrefix', prefix), }); } } + +export class AutoCompletionEntry { + public target: String; + public hitCount: Number; + + constructor() { + this.target=''; + this.hitCount=0; + } +} + diff --git a/src/main/webapp/app/teaserContent/teaserContent.component.html b/src/main/webapp/app/teaserContent/teaserContent.component.html index a62deef5cf7ee67c74dbd6284f224938caf8b427..bc3c3d65adf969c3ffe926fe752efdecc8554494 100644 --- a/src/main/webapp/app/teaserContent/teaserContent.component.html +++ b/src/main/webapp/app/teaserContent/teaserContent.component.html @@ -3,19 +3,19 @@ <div class="col-sm-3"> <p style="padding-left: 30px;"><strong>Keywords</strong></p> <ul style="list-style-type: circle;"> - <li *ngFor="let keyWord of keywords"><a (click)="clickKeyword(keyWord)" style="cursor:pointer;">{{keyWord}}</a></li> + <li *ngFor="let keyWord of keywords"><a (click)="clickKeyword(keyWord.target)" style="cursor:pointer;">{{keyWord.target}} ({{keyWord.hitCount}})</a></li> </ul> </div> <div class="col-sm-3"> <p style="padding-left: 30px;"><strong>Programming Languages</strong></p> <ul style="list-style-type: circle;"> - <li *ngFor="let programmingLanguage of programmingLanguages"><a (click)="clickLanguage(programmingLanguage)" style="cursor:pointer;">{{programmingLanguage}}</a></li> + <li *ngFor="let programmingLanguage of programmingLanguages"><a (click)="clickLanguage(programmingLanguage.target)" style="cursor:pointer;">{{programmingLanguage.target}} ({{programmingLanguage.hitCount}})</a></li> </ul> </div> <div class="col-sm-3"> <p style="padding-left: 30px;"><strong>Contributors</strong></p> <ul style="list-style-type: circle;"> - <li *ngFor="let contributor of contributors"><a (click)="clickContributor(contributor)" style="cursor:pointer;">{{contributor}}</a></li> + <li *ngFor="let contributor of contributors"><a (click)="clickContributor(contributor.target)" style="cursor:pointer;">{{contributor.target}} ({{contributor.hitCount}})</a></li> </ul> </div> </div> \ No newline at end of file diff --git a/src/main/webapp/app/teaserContent/teaserContent.component.ts b/src/main/webapp/app/teaserContent/teaserContent.component.ts index 5740293cdd97c9a336f7d067d6af5b75a7eeef8a..d927d7b1f47d02a954d373325ed61a325af6c054 100644 --- a/src/main/webapp/app/teaserContent/teaserContent.component.ts +++ b/src/main/webapp/app/teaserContent/teaserContent.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { SearchService } from 'app/search/service/search-service'; +import { SearchService, AutoCompletionEntry } from 'app/search/service/search-service'; import {SearchInputComponent} from 'app/search-input/search-input.component' import { Router } from '@angular/router'; @@ -13,9 +13,9 @@ import { Router } from '@angular/router'; providers: [SearchInputComponent] }) export class TeaserContentComponent implements OnInit { - public keywords: Array<String> = new Array<String>(); - public contributors: Array<String> = new Array<String>(); - public programmingLanguages: Array<String> = new Array<String>(); + public keywords: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>(); + public contributors: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>(); + public programmingLanguages: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>(); constructor( private searchService: SearchService, @@ -24,7 +24,7 @@ export class TeaserContentComponent implements OnInit { ngOnInit(): void { this.searchService.getKeywordsAutoComplete('').subscribe( - (data: Array<string>) => { + (data: Array<AutoCompletionEntry>) => { this.keywords = data; }, () => { @@ -33,7 +33,7 @@ export class TeaserContentComponent implements OnInit { ); this.searchService.getProgrammingLanguageAutoComplete('').subscribe( - (data: Array<string>) => { + (data: Array<AutoCompletionEntry>) => { this.programmingLanguages = data; }, () => { @@ -41,7 +41,7 @@ export class TeaserContentComponent implements OnInit { } ); this.searchService.getContributorAutoComplete('').subscribe( - (data: Array<string>) => { + (data: Array<AutoCompletionEntry>) => { this.contributors = data; }, () => { diff --git a/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java b/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java index 5810cc31ec405cc73490114ab76fc19fc1c683f9..aaa29eedb9039f19fae0b4557a0cfe1f3dcd3ec9 100644 --- a/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java +++ b/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java @@ -1,5 +1,9 @@ package at.ac.uibk.gitsearch.repository.search; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; + import java.io.IOException; import java.util.List; @@ -8,7 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import at.ac.uibk.gitsearch.GitsearchApp; -import static org.hamcrest.Matchers.*; +import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; @SpringBootTest(classes = GitsearchApp.class) @@ -18,22 +22,22 @@ public class MetaDataRepositoryTests { @Test public void testKeywordAutocompletion() throws IOException { - final List<String> keywordsAutoComplete = metaDataRepository.getKeywordsAutoComplete("Jav"); - org.junit.Assert.assertThat(keywordsAutoComplete, contains(is("Java"))); + final List<AutoCompleteEntry> keywordsAutoComplete = metaDataRepository.getKeywordsAutoComplete("Jav"); + org.junit.Assert.assertThat(keywordsAutoComplete, contains(hasProperty("target",is("Java")))); } @Test public void testCreatorAutocompletion() throws IOException { - final List<String> creatorAutoComplete = metaDataRepository.getCreatorAutoComplete("Pod"); - org.junit.Assert.assertThat(creatorAutoComplete, contains(is("Stefan Podlipnig"))); - final List<String> creatorAutoComplete2 = metaDataRepository.getCreatorAutoComplete("Po"); - org.junit.Assert.assertThat(creatorAutoComplete2, contains(is("Stefan Podlipnig"))); + final List<AutoCompleteEntry> creatorAutoComplete = metaDataRepository.getCreatorAutoComplete("Pod"); + org.junit.Assert.assertThat(creatorAutoComplete, contains(hasProperty("target",is("Stefan Podlipnig")))); + final List<AutoCompleteEntry> creatorAutoComplete2 = metaDataRepository.getCreatorAutoComplete("Po"); + org.junit.Assert.assertThat(creatorAutoComplete2, contains(hasProperty("target",is("Stefan Podlipnig")))); } @Test public void testContributorAutocompletion() throws IOException { - final List<String> contributorAutoComplete = metaDataRepository.getContributorAutoComplete("Bast"); - org.junit.Assert.assertThat(contributorAutoComplete, contains(is("Daniel Bastta"))); + final List<AutoCompleteEntry> contributorAutoComplete = metaDataRepository.getContributorAutoComplete("Bast"); + org.junit.Assert.assertThat(contributorAutoComplete, contains(hasProperty("target",is("Daniel Bastta")))); } }