Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void changeNamespaceInDatabase(

cache.evictSitemap();
cache.evictNamespaceDetails(oldNamespace);
search.updateSearchEntries(extensions.toList());
search.updateSearchEntries(extensions.filter(Extension::isActive).toList());
}

private void changeExtensionNamespace(Streamable<Extension> extensions, Namespace newNamespace) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.eclipse.openvsx.search.ExtensionSearch;
import org.eclipse.openvsx.util.NamingUtil;

import javax.annotation.Nonnull;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -66,7 +67,7 @@ public class Extension implements Serializable {
/**
* Convert to a search entity for Elasticsearch.
*/
public ExtensionSearch toSearch(ExtensionVersion latest, List<String> targetPlatforms) {
public ExtensionSearch toSearch(@Nonnull ExtensionVersion latest, List<String> targetPlatforms) {
var search = new ExtensionSearch();
search.setId(this.getId());
search.setName(this.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Stream;

import static org.eclipse.openvsx.cache.CacheService.CACHE_AVERAGE_REVIEW_RATING;
Expand All @@ -41,12 +38,21 @@ public class DatabaseSearchService implements ISearchService {
private final RelevanceService relevanceService;
private final RepositoryService repositories;

private final Map<String, Comparator<ExtensionSearch>> comparators;

@Value("${ovsx.databasesearch.enabled:false}")
boolean enableSearch;

public DatabaseSearchService(RelevanceService relevanceService, RepositoryService repositories) {
this.relevanceService = relevanceService;
this.repositories = repositories;

comparators = Map.of(
SortBy.RELEVANCE, new RelevanceComparator(),
SortBy.TIMESTAMP, new TimestampComparator(),
SortBy.RATING, new RatingComparator(),
SortBy.DOWNLOADS, new DownloadedCountComparator()
);
}

public boolean isEnabled() {
Expand Down Expand Up @@ -78,26 +84,21 @@ private List<ExtensionSearch> applyPaging(Options options, List<ExtensionSearch>

private List<ExtensionSearch> sortExtensions(Options options, Streamable<Extension> matchingExtensions) {
Stream<ExtensionSearch> searchEntries;
if(SortBy.RELEVANCE.equals(options.sortBy()) || SortBy.RATING.equals(options.sortBy())) {
if (SortBy.RELEVANCE.equals(options.sortBy()) || SortBy.RATING.equals(options.sortBy())) {
var searchStats = new SearchStats(repositories);
searchEntries = matchingExtensions.stream().map(extension -> relevanceService.toSearchEntry(extension, searchStats));
searchEntries = matchingExtensions.stream()
.map(extension -> relevanceService.toSearchEntry(extension, searchStats))
.filter(Objects::nonNull);
} else {
searchEntries = matchingExtensions.stream().map(extension -> {
var latest = repositories.findLatestVersion(extension, null, false, true);
var targetPlatforms = repositories.findExtensionTargetPlatforms(extension);
return extension.toSearch(latest, targetPlatforms);
});
return latest != null ? extension.toSearch(latest, targetPlatforms) : null;
}).filter(Objects::nonNull);
}

var comparators = Map.of(
SortBy.RELEVANCE, new RelevanceComparator(),
SortBy.TIMESTAMP, new TimestampComparator(),
SortBy.RATING, new RatingComparator(),
SortBy.DOWNLOADS, new DownloadedCountComparator()
);

var comparator = comparators.get(options.sortBy());
if(comparator == null) {
if (comparator == null) {
throw new ErrorResultException("sortBy parameter must be " + SortBy.OPTIONS + ".");
}
if ("desc".equals(options.sortOrder())) {
Expand All @@ -108,7 +109,7 @@ SortBy.DOWNLOADS, new DownloadedCountComparator()
}

private Streamable<Extension> excludeByQueryString(Options options, Streamable<Extension> matchingExtensions) {
if(options.queryString() == null) {
if (options.queryString() == null) {
return matchingExtensions;
}

Expand All @@ -124,7 +125,7 @@ private Streamable<Extension> excludeByQueryString(Options options, Streamable<E
}

private Streamable<Extension> excludeByCategory(Options options, Streamable<Extension> matchingExtensions) {
if(options.category() == null) {
if (options.category() == null) {
return matchingExtensions;
}

Expand All @@ -135,23 +136,23 @@ private Streamable<Extension> excludeByCategory(Options options, Streamable<Exte
}

private Streamable<Extension> excludeByTargetPlatform(Options options, Streamable<Extension> matchingExtensions) {
if(!TargetPlatform.isValid(options.targetPlatform())) {
if (!TargetPlatform.isValid(options.targetPlatform())) {
return matchingExtensions;
}

return matchingExtensions.filter(extension -> extension.getVersions().stream().anyMatch(ev -> ev.getTargetPlatform().equals(options.targetPlatform())));
}

private Streamable<Extension> includeByNamespace(Options options, Streamable<Extension> matchingExtensions) {
if(options.namespace() == null) {
if (options.namespace() == null) {
return matchingExtensions;
}

return matchingExtensions.filter(extension -> extension.getNamespace().getName().equalsIgnoreCase(options.namespace()));
}

private Streamable<Extension> excludeByNamespace(Options options, Streamable<Extension> matchingExtensions) {
if(options.namespacesToExclude() == null) {
if (options.namespacesToExclude() == null) {
return matchingExtensions;
}

Expand Down Expand Up @@ -199,7 +200,7 @@ public void removeSearchEntry(Extension extension) {
/**
* Sort by download count
*/
class DownloadedCountComparator implements Comparator<ExtensionSearch> {
static class DownloadedCountComparator implements Comparator<ExtensionSearch> {

@Override
public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
Expand All @@ -211,7 +212,7 @@ public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
/**
* Sort by averageRating
*/
class RatingComparator implements Comparator<ExtensionSearch> {
static class RatingComparator implements Comparator<ExtensionSearch> {

@Override
public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
Expand All @@ -228,7 +229,7 @@ public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
/**
* Sort by timestamp
*/
class TimestampComparator implements Comparator<ExtensionSearch> {
static class TimestampComparator implements Comparator<ExtensionSearch> {

@Override
public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
Expand All @@ -240,7 +241,7 @@ public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
/**
* Sort by relevance
*/
class RelevanceComparator implements Comparator<ExtensionSearch> {
static class RelevanceComparator implements Comparator<ExtensionSearch> {

@Override
public int compare(ExtensionSearch ext1, ExtensionSearch ext2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.util.ObjectBuilder;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.openvsx.entities.Extension;
import org.eclipse.openvsx.migration.HandlerJobRequest;
import org.eclipse.openvsx.repositories.RepositoryService;
import org.eclipse.openvsx.search.RelevanceService.SearchStats;
import org.eclipse.openvsx.util.ErrorResultException;
import org.eclipse.openvsx.util.NamingUtil;
import org.eclipse.openvsx.util.TargetPlatform;
import org.jobrunr.scheduling.JobRequestScheduler;
import org.jobrunr.scheduling.cron.Cron;
Expand All @@ -38,6 +40,7 @@
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
Expand Down Expand Up @@ -174,11 +177,10 @@ public void updateSearchIndex(boolean clear) {
return;
}
var stats = new SearchStats(repositories);
var indexQueries = allExtensions.map(extension ->
new IndexQueryBuilder()
.withObject(relevanceService.toSearchEntry(extension, stats))
.build()
).toList();
var indexQueries = allExtensions
.map(extension -> buildIndexQuery(extension, stats))
.filter(Objects::nonNull)
.toList();

if (!locked) {
// The write lock has not been acquired upfront, so do it just before submitting the index queries
Expand Down Expand Up @@ -208,11 +210,10 @@ public void updateSearchEntries(List<Extension> extensions) {
rwLock.writeLock().lock();
var indexOps = searchOperations.indexOps(ExtensionSearch.class);
var stats = new SearchStats(repositories);
var indexQueries = extensions.stream().map(extension ->
new IndexQueryBuilder()
.withObject(relevanceService.toSearchEntry(extension, stats))
.build()
).collect(Collectors.toList());
var indexQueries = extensions.stream()
.map(extension -> buildIndexQuery(extension, stats))
.filter(Objects::nonNull)
.collect(Collectors.toList());
searchOperations.bulkIndex(indexQueries, indexOps.getIndexCoordinates());
} finally {
rwLock.writeLock().unlock();
Expand All @@ -224,19 +225,32 @@ public void updateSearchEntry(Extension extension) {
if (!isEnabled()) {
return;
}

try {
rwLock.writeLock().lock();
var stats = new SearchStats(repositories);
var indexQuery = new IndexQueryBuilder()
.withObject(relevanceService.toSearchEntry(extension, stats))
.build();
var indexOps = searchOperations.indexOps(ExtensionSearch.class);
searchOperations.index(indexQuery, indexOps.getIndexCoordinates());
var indexQuery = buildIndexQuery(extension, stats);
if (indexQuery != null) {
var indexOps = searchOperations.indexOps(ExtensionSearch.class);
searchOperations.index(indexQuery, indexOps.getIndexCoordinates());
}
} finally {
rwLock.writeLock().unlock();
}
}

private @Nullable IndexQuery buildIndexQuery(Extension extension, SearchStats stats) {
var searchEntry = relevanceService.toSearchEntry(extension, stats);
if (searchEntry != null) {
return new IndexQueryBuilder()
.withObject(searchEntry)
.build();
} else {
logger.warn("Trying to update search entry for inactive extension '{}'",NamingUtil.toExtensionId(extension));
return null;
}
}

@Retryable(retryFor = DataAccessResourceFailureException.class)
public void removeSearchEntries(Collection<Long> ids) {
if (!isEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nullable;
import org.eclipse.openvsx.entities.Extension;
import org.eclipse.openvsx.entities.ExtensionVersion;
import org.eclipse.openvsx.repositories.RepositoryService;
Expand Down Expand Up @@ -51,8 +52,12 @@ public RelevanceService(RepositoryService repositories) {
this.repositories = repositories;
}

public ExtensionSearch toSearchEntry(Extension extension, SearchStats stats) {
var latest = repositories.findLatestVersion(extension, null, false, true);
public @Nullable ExtensionSearch toSearchEntry(Extension extension, SearchStats stats) {
var latest = repositories.findLatestVersion(extension, null, false, true);
if (latest == null) {
return null;
}

var targetPlatforms = repositories.findExtensionTargetPlatforms(extension);
var entry = extension.toSearch(latest, targetPlatforms);
entry.setRating(calculateRating(extension, stats));
Expand Down
Loading