diff --git a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java index d90baffa5..381340af6 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java @@ -256,7 +256,7 @@ public SearchResult search(Options options) { } var queryBuilder = new NativeQueryBuilder(); - queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options))); + createQuery(queryBuilder, options); // Sort search results according to 'sortOrder' and 'sortBy' options sortResults(queryBuilder, options.sortOrder(), options.sortBy()); @@ -351,6 +351,22 @@ private ObjectBuilder createTextSearchQuery(BoolQuery.Builder boolQue return boolQuery; } + private void createQuery(NativeQueryBuilder queryBuilder, Options options) { + if(SortBy.RELEVANCE.equals(options.sortBy())) { + queryBuilder.withQuery( + builder -> builder.functionScore( + scoreQuery -> scoreQuery.query( + sortQueryBuilder -> sortQueryBuilder.bool(boolQuery -> createSearchQuery(boolQuery, options)) + ).functions( + functionBuilder -> functionBuilder.fieldValueFactor(factor -> factor.field("relevance").factor(1.0)) + ) + ) + ); + } else { + queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options))); + } + } + private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, String sortBy) { sortOrder = sortOrder.toLowerCase(); var orders = Map.of("asc", SortOrder.Asc, "desc", SortOrder.Desc); @@ -373,7 +389,7 @@ private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, Stri var scoreSort = new SortOptions.Builder().score(builder -> builder.order(order)).build(); var fieldSort = new SortOptions.Builder().field(builder -> builder.field(sortBy).unmappedType(type).order(order)).build(); - var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort, fieldSort) : List.of(fieldSort, scoreSort); + var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort) : List.of(fieldSort, scoreSort); queryBuilder.withSort(sortOptions); } diff --git a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java index aa206cb7a..6f98793a1 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java @@ -73,15 +73,7 @@ private double calculateRating(Extension extension, SearchStats stats) { private double calculateRelevance(Extension extension, ExtensionVersion latest, SearchStats stats, ExtensionSearch entry) { var extensionId = NamingUtil.toExtensionId(extension); logger.debug(">> [{}] CALCULATE RELEVANCE", extensionId); - var ratingValue = 0.0; - if (extension.getAverageRating() != null) { - logger.debug("[{}] INCLUDE AVG RATING", extensionId); - var reviewCount = extension.getReviewCount(); - // Reduce the rating relevance if there are only few reviews - var countRelevance = saturate(reviewCount, 0.25); - ratingValue = (extension.getAverageRating() / 5.0) * countRelevance; - logger.debug("[{}] {} = {} * {} | {}", extensionId, ratingValue, extension.getAverageRating() / 5.0, countRelevance, reviewCount); - } + var ratingValue = calculateRating(extension, stats) / 5.0; var downloadsValue = entry.getDownloadCount() / stats.downloadRef; var timestamp = latest.getTimestamp(); var timestampValue = Duration.between(stats.oldest, timestamp).toSeconds() / stats.timestampRef; @@ -127,10 +119,6 @@ else if (value > 1.0) return value; } - private double saturate(double value, double factor) { - return 1 - 1.0 / (value * factor + 1); - } - private boolean isVerified(ExtensionVersion extVersion) { if (extVersion.getPublishedWith() == null) return false; @@ -147,9 +135,8 @@ public static class SearchStats { public SearchStats(RepositoryService repositories) { var now = TimeUtil.getCurrentUTC(); - var maxDownloads = repositories.getMaxExtensionDownloadCount(); var oldestTimestamp = repositories.getOldestExtensionTimestamp(); - this.downloadRef = maxDownloads * 1.5 + 100; + this.downloadRef = Math.max(repositories.getMaxExtensionDownloadCount(), 1); this.oldest = oldestTimestamp == null ? now : oldestTimestamp; this.timestampRef = Duration.between(this.oldest, now).toSeconds() + 60; this.averageReviewRating = repositories.getAverageReviewRating(); diff --git a/server/src/test/java/org/eclipse/openvsx/search/DatabaseSearchServiceTest.java b/server/src/test/java/org/eclipse/openvsx/search/DatabaseSearchServiceTest.java index 5396026a8..51c43b5e8 100644 --- a/server/src/test/java/org/eclipse/openvsx/search/DatabaseSearchServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/search/DatabaseSearchServiceTest.java @@ -61,6 +61,8 @@ void testRelevance() { var ext2 = mockExtension("java", 4.0, 100, 10000, "redhat", List.of("Snippets", "Programming Languages")); var ext3 = mockExtension("openshift", 1.0, 100, 10, "redhat", List.of("Snippets", "Other")); Mockito.when(repositories.findAllActiveExtensions()).thenReturn(Streamable.of(List.of(ext1, ext2, ext3))); + Mockito.when(repositories.getMaxExtensionDownloadCount()).thenReturn(10000); + Mockito.when(repositories.getAverageReviewRating()).thenReturn(3.96735905); var searchOptions = searchOptions(null, null, 50, 0, null, SortBy.RELEVANCE); var result = search.search(searchOptions); @@ -79,6 +81,8 @@ void testReverse() { var ext1 = mockExtension("yaml", 3.0, 100, 0, "redhat", List.of("Snippets", "Programming Languages")); var ext2 = mockExtension("java", 4.0, 100, 0, "redhat", List.of("Snippets", "Programming Languages")); Mockito.when(repositories.findAllActiveExtensions()).thenReturn(Streamable.of(List.of(ext1, ext2))); + Mockito.when(repositories.getMaxExtensionDownloadCount()).thenReturn(0); + Mockito.when(repositories.getAverageReviewRating()).thenReturn(3.5); var searchOptions = searchOptions(null, "Programming Languages", 50, 0, "desc", null); var result = search.search(searchOptions);