public async Task<SearchResponse> SearchAsync(SearchRequest request, CancellationToken cancellationToken)
{
var frameworks = GetCompatibleFrameworksOrNull(request.Framework);
IQueryable<Package> search = _context.Packages;
search = ApplySearchQuery(search, request.Query);
search = ApplySearchFilters(
search,
request.IncludePrerelease,
request.IncludeSemVer2,
request.PackageType,
frameworks);
var packageIds = search
.Select(p => p.Id)
.Distinct()
.OrderBy(id => id)
.Skip(request.Skip)
.Take(request.Take);
// This query MUST fetch all versions for each package that matches the search,
// otherwise the results for a package's latest version may be incorrect.
// If possible, we'll find all these packages in a single query by matching
// the package IDs in a subquery. Otherwise, run two queries:
// 1. Find the package IDs that match the search
// 2. Find all package versions for these package IDs
if (_context.SupportsLimitInSubqueries)
{
search = _context.Packages.Where(p => packageIds.Contains(p.Id));
}
else
{
var packageIdResults = await packageIds.ToListAsync(cancellationToken);
search = _context.Packages.Where(p => packageIdResults.Contains(p.Id));
}
search = ApplySearchFilters(
search,
request.IncludePrerelease,
request.IncludeSemVer2,
request.PackageType,
frameworks);
var results = await search.ToListAsync(cancellationToken);
var groupedResults = results
.GroupBy(p => p.Id, StringComparer.OrdinalIgnoreCase)
.Select(group => new PackageRegistration(group.Key, group.ToList()))
.ToList();
return _searchBuilder.BuildSearch(groupedResults);
}