in app/lib/search/mem_index.dart [128:275]
Future<PackageSearchResult> search(ServiceSearchQuery query) async {
final Set<String> packages = Set.from(_packages.keys);
// filter on package prefix
if (query.parsedQuery.packagePrefix != null) {
final String prefix = query.parsedQuery.packagePrefix!.toLowerCase();
packages.removeWhere(
(package) =>
!_packages[package]!.package.toLowerCase().startsWith(prefix),
);
}
// filter on tags
final combinedTagsPredicate =
query.tagsPredicate.appendPredicate(query.parsedQuery.tagsPredicate);
if (combinedTagsPredicate.isNotEmpty) {
packages.retainWhere(
(package) => combinedTagsPredicate.matches(_packages[package]!.tags));
}
// filter on dependency
if (query.parsedQuery.hasAnyDependency) {
packages.removeWhere((package) {
final doc = _packages[package]!;
if (doc.dependencies.isEmpty) return true;
for (String dependency in query.parsedQuery.allDependencies) {
if (!doc.dependencies.containsKey(dependency)) return true;
}
for (String dependency in query.parsedQuery.refDependencies) {
final type = doc.dependencies[dependency];
if (type == null || type == DependencyTypes.transitive) return true;
}
return false;
});
}
// filter on publisher
if (query.publisherId != null || query.parsedQuery.publisher != null) {
final publisherId = query.publisherId ?? query.parsedQuery.publisher;
packages.removeWhere((package) {
final doc = _packages[package]!;
return doc.publisherId != publisherId;
});
}
// filter on points
if (query.minPoints != null && query.minPoints! > 0) {
packages.removeWhere((package) {
final doc = _packages[package]!;
return (doc.grantedPoints ?? 0) < query.minPoints!;
});
}
// filter on updatedInDays
if (query.updatedInDays != null && query.updatedInDays! > 0) {
final threshold =
Duration(days: query.updatedInDays!, hours: 11, minutes: 59);
final now = clock.now();
packages.removeWhere((package) {
final doc = _packages[package]!;
final diff = now.difference(doc.updated!);
return diff > threshold;
});
}
PackageHit? highlightedHit;
if (query.considerHighlightedHit) {
final queryText = query.parsedQuery.text;
final matchingPackage =
_packages[queryText] ?? _packages[queryText!.toLowerCase()];
if (matchingPackage != null) {
// Remove higlighted package from the final packages set.
packages.remove(matchingPackage.package);
// higlight only if we are on the first page
if (query.includeHighlightedHit) {
highlightedHit = PackageHit(package: matchingPackage.package);
}
}
}
// do text matching
final textResults = _searchText(
packages,
query.parsedQuery.text,
hasHighlightedHit: highlightedHit != null,
);
// filter packages that doesn't match text query
if (textResults != null) {
final keys = textResults.pkgScore.getKeys();
packages.removeWhere((x) => !keys.contains(x));
}
late List<PackageHit> packageHits;
switch (query.order ?? SearchOrder.top) {
case SearchOrder.top:
final List<Score> scores = [
_getOverallScore(packages),
if (textResults != null) textResults.pkgScore,
];
final overallScore = Score.multiply(scores);
packageHits = _rankWithValues(overallScore.getValues());
break;
case SearchOrder.text:
final score = textResults?.pkgScore ?? Score.empty();
packageHits = _rankWithValues(score.getValues());
break;
case SearchOrder.created:
packageHits = _rankWithComparator(packages, _compareCreated);
break;
case SearchOrder.updated:
packageHits = _rankWithComparator(packages, _compareUpdated);
break;
case SearchOrder.popularity:
packageHits = _rankWithValues(getPopularityScore(packages));
break;
case SearchOrder.like:
packageHits = _rankWithValues(getLikeScore(packages));
break;
case SearchOrder.points:
packageHits = _rankWithValues(getPubPoints(packages));
break;
}
// bound by offset and limit (or randomize items)
final totalCount = packageHits.length + (highlightedHit == null ? 0 : 1);
packageHits =
boundedList(packageHits, offset: query.offset, limit: query.limit);
if (textResults != null && textResults.topApiPages.isNotEmpty) {
packageHits = packageHits.map((ps) {
final apiPages = textResults.topApiPages[ps.package]
// TODO: extract title for the page
?.map((String page) => ApiPageRef(path: page))
.toList();
return ps.change(apiPages: apiPages);
}).toList();
}
return PackageSearchResult(
timestamp: clock.now().toUtc(),
totalCount: totalCount,
highlightedHit: highlightedHit,
packageHits: packageHits,
);
}