Skip to content

Commit ca45313

Browse files
amvanbarennetomi
authored andcommitted
Fix search relevance
- Use function_score query when sort by relevance - Use IMDB rating formula for rating relevance - Remove magic constants for downloadRef
1 parent aa42b5c commit ca45313

3 files changed

Lines changed: 24 additions & 17 deletions

File tree

server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ public SearchResult search(Options options) {
284284
}
285285

286286
var queryBuilder = new NativeQueryBuilder();
287-
queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options)));
287+
createQuery(queryBuilder, options);
288288

289289
// Sort search results according to 'sortOrder' and 'sortBy' options
290290
sortResults(queryBuilder, options.sortOrder(), options.sortBy());
@@ -396,6 +396,22 @@ private ObjectBuilder<BoolQuery> createTextSearchQuery(BoolQuery.Builder boolQue
396396
return boolQuery;
397397
}
398398

399+
private void createQuery(NativeQueryBuilder queryBuilder, Options options) {
400+
if(SortBy.RELEVANCE.equals(options.sortBy())) {
401+
queryBuilder.withQuery(
402+
builder -> builder.functionScore(
403+
scoreQuery -> scoreQuery.query(
404+
sortQueryBuilder -> sortQueryBuilder.bool(boolQuery -> createSearchQuery(boolQuery, options))
405+
).functions(
406+
functionBuilder -> functionBuilder.fieldValueFactor(factor -> factor.field("relevance").factor(1.0))
407+
)
408+
)
409+
);
410+
} else {
411+
queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options)));
412+
}
413+
}
414+
399415
private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, String sortBy) {
400416
sortOrder = sortOrder.toLowerCase();
401417
var orders = Map.of("asc", SortOrder.Asc, "desc", SortOrder.Desc);
@@ -418,7 +434,7 @@ private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, Stri
418434

419435
var scoreSort = new SortOptions.Builder().score(builder -> builder.order(order)).build();
420436
var fieldSort = new SortOptions.Builder().field(builder -> builder.field(sortBy).unmappedType(type).order(order)).build();
421-
var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort, fieldSort) : List.of(fieldSort, scoreSort);
437+
var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort) : List.of(fieldSort, scoreSort);
422438
queryBuilder.withSort(sortOptions);
423439
}
424440
}

server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,7 @@ private double calculateRating(Extension extension, SearchStats stats) {
7979
private double calculateRelevance(Extension extension, ExtensionVersion latest, SearchStats stats, ExtensionSearch entry) {
8080
var extensionId = NamingUtil.toExtensionId(extension);
8181
logger.debug(">> [{}] CALCULATE RELEVANCE", extensionId);
82-
var ratingValue = 0.0;
83-
if (extension.getAverageRating() != null) {
84-
logger.debug("[{}] INCLUDE AVG RATING", extensionId);
85-
var reviewCount = extension.getReviewCount();
86-
// Reduce the rating relevance if there are only few reviews
87-
var countRelevance = saturate(reviewCount, 0.25);
88-
ratingValue = (extension.getAverageRating() / 5.0) * countRelevance;
89-
logger.debug("[{}] {} = {} * {} | {}", extensionId, ratingValue, extension.getAverageRating() / 5.0, countRelevance, reviewCount);
90-
}
82+
var ratingValue = calculateRating(extension, stats) / 5.0;
9183
var downloadsValue = entry.getDownloadCount() / stats.downloadRef;
9284
var timestamp = latest.getTimestamp();
9385
var timestampValue = Duration.between(stats.oldest, timestamp).toSeconds() / stats.timestampRef;
@@ -128,10 +120,6 @@ private double limit(double value) {
128120
return Math.clamp(value, 0.0, 1.0);
129121
}
130122

131-
private double saturate(double value, double factor) {
132-
return 1 - 1.0 / (value * factor + 1);
133-
}
134-
135123
private boolean isVerified(ExtensionVersion extVersion) {
136124
if (extVersion.getPublishedWith() == null)
137125
return false;
@@ -148,9 +136,8 @@ public static class SearchStats {
148136

149137
public SearchStats(RepositoryService repositories) {
150138
var now = TimeUtil.getCurrentUTC();
151-
var maxDownloads = repositories.getMaxExtensionDownloadCount();
152139
var oldestTimestamp = repositories.getOldestExtensionTimestamp();
153-
this.downloadRef = maxDownloads * 1.5 + 100;
140+
this.downloadRef = Math.max(repositories.getMaxExtensionDownloadCount(), 1);
154141
this.oldest = oldestTimestamp == null ? now : oldestTimestamp;
155142
this.timestampRef = Duration.between(this.oldest, now).toSeconds() + 60;
156143
this.averageReviewRating = repositories.getAverageReviewRating();

server/src/test/java/org/eclipse/openvsx/search/DatabaseSearchServiceTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ void testRelevance() {
6161
var ext2 = mockExtension("java", 4.0, 100, 10000, "redhat", List.of("Snippets", "Programming Languages"));
6262
var ext3 = mockExtension("openshift", 1.0, 100, 10, "redhat", List.of("Snippets", "Other"));
6363
Mockito.when(repositories.findAllActiveExtensions()).thenReturn(Streamable.of(List.of(ext1, ext2, ext3)));
64+
Mockito.when(repositories.getMaxExtensionDownloadCount()).thenReturn(10000);
65+
Mockito.when(repositories.getAverageReviewRating()).thenReturn(3.96735905);
6466

6567
var searchOptions = searchOptions(null, null, 50, 0, null, SortBy.RELEVANCE);
6668
var result = search.search(searchOptions);
@@ -79,6 +81,8 @@ void testReverse() {
7981
var ext1 = mockExtension("yaml", 3.0, 100, 0, "redhat", List.of("Snippets", "Programming Languages"));
8082
var ext2 = mockExtension("java", 4.0, 100, 0, "redhat", List.of("Snippets", "Programming Languages"));
8183
Mockito.when(repositories.findAllActiveExtensions()).thenReturn(Streamable.of(List.of(ext1, ext2)));
84+
Mockito.when(repositories.getMaxExtensionDownloadCount()).thenReturn(0);
85+
Mockito.when(repositories.getAverageReviewRating()).thenReturn(3.5);
8286

8387
var searchOptions = searchOptions(null, "Programming Languages", 50, 0, "desc", null);
8488
var result = search.search(searchOptions);

0 commit comments

Comments
 (0)