Skip to content

Commit 0adff0e

Browse files
author
amvanbaren
committed
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 de4c295 commit 0adff0e

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
@@ -256,7 +256,7 @@ public SearchResult search(Options options) {
256256
}
257257

258258
var queryBuilder = new NativeQueryBuilder();
259-
queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options)));
259+
createQuery(queryBuilder, options);
260260

261261
// Sort search results according to 'sortOrder' and 'sortBy' options
262262
sortResults(queryBuilder, options.sortOrder(), options.sortBy());
@@ -351,6 +351,22 @@ private ObjectBuilder<BoolQuery> createTextSearchQuery(BoolQuery.Builder boolQue
351351
return boolQuery;
352352
}
353353

354+
private void createQuery(NativeQueryBuilder queryBuilder, Options options) {
355+
if(SortBy.RELEVANCE.equals(options.sortBy())) {
356+
queryBuilder.withQuery(
357+
builder -> builder.functionScore(
358+
scoreQuery -> scoreQuery.query(
359+
sortQueryBuilder -> sortQueryBuilder.bool(boolQuery -> createSearchQuery(boolQuery, options))
360+
).functions(
361+
functionBuilder -> functionBuilder.fieldValueFactor(factor -> factor.field("relevance").factor(1.0))
362+
)
363+
)
364+
);
365+
} else {
366+
queryBuilder.withQuery(builder -> builder.bool(boolQuery -> createSearchQuery(boolQuery, options)));
367+
}
368+
}
369+
354370
private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, String sortBy) {
355371
sortOrder = sortOrder.toLowerCase();
356372
var orders = Map.of("asc", SortOrder.Asc, "desc", SortOrder.Desc);
@@ -373,7 +389,7 @@ private void sortResults(NativeQueryBuilder queryBuilder, String sortOrder, Stri
373389

374390
var scoreSort = new SortOptions.Builder().score(builder -> builder.order(order)).build();
375391
var fieldSort = new SortOptions.Builder().field(builder -> builder.field(sortBy).unmappedType(type).order(order)).build();
376-
var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort, fieldSort) : List.of(fieldSort, scoreSort);
392+
var sortOptions = sortBy.equals(SortBy.RELEVANCE) ? List.of(scoreSort) : List.of(fieldSort, scoreSort);
377393
queryBuilder.withSort(sortOptions);
378394
}
379395

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

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,7 @@ private double calculateRating(Extension extension, SearchStats stats) {
7373
private double calculateRelevance(Extension extension, ExtensionVersion latest, SearchStats stats, ExtensionSearch entry) {
7474
var extensionId = NamingUtil.toExtensionId(extension);
7575
logger.debug(">> [{}] CALCULATE RELEVANCE", extensionId);
76-
var ratingValue = 0.0;
77-
if (extension.getAverageRating() != null) {
78-
logger.debug("[{}] INCLUDE AVG RATING", extensionId);
79-
var reviewCount = extension.getReviewCount();
80-
// Reduce the rating relevance if there are only few reviews
81-
var countRelevance = saturate(reviewCount, 0.25);
82-
ratingValue = (extension.getAverageRating() / 5.0) * countRelevance;
83-
logger.debug("[{}] {} = {} * {} | {}", extensionId, ratingValue, extension.getAverageRating() / 5.0, countRelevance, reviewCount);
84-
}
76+
var ratingValue = calculateRating(extension, stats) / 5.0;
8577
var downloadsValue = entry.getDownloadCount() / stats.downloadRef;
8678
var timestamp = latest.getTimestamp();
8779
var timestampValue = Duration.between(stats.oldest, timestamp).toSeconds() / stats.timestampRef;
@@ -127,10 +119,6 @@ else if (value > 1.0)
127119
return value;
128120
}
129121

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

148136
public SearchStats(RepositoryService repositories) {
149137
var now = TimeUtil.getCurrentUTC();
150-
var maxDownloads = repositories.getMaxExtensionDownloadCount();
151138
var oldestTimestamp = repositories.getOldestExtensionTimestamp();
152-
this.downloadRef = maxDownloads * 1.5 + 100;
139+
this.downloadRef = Math.max(repositories.getMaxExtensionDownloadCount(), 1);
153140
this.oldest = oldestTimestamp == null ? now : oldestTimestamp;
154141
this.timestampRef = Duration.between(this.oldest, now).toSeconds() + 60;
155142
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)