@@ -12,6 +12,7 @@ import 'package:meta/meta.dart';
1212import 'package:pub_dev/service/topics/models.dart' ;
1313import 'package:pub_dev/third_party/bit_array/bit_array.dart' ;
1414
15+ import '../shared/utils.dart' show TopKSortedListBuilder;
1516import 'models.dart' ;
1617import 'search_service.dart' ;
1718import 'text_utils.dart' ;
@@ -263,6 +264,13 @@ class InMemoryPackageIndex {
263264 // extra item, that will be addressed after the ranking score is determined.
264265 var totalCount = packageScores? .positiveCount () ?? predicateFilterCount;
265266
267+ // Checking if it is worth to calculate the sorted order, estimating the
268+ // total count by overcounting the best name matches.
269+ final maximumTotalCount = totalCount + (bestNameIndex != null ? 1 : 0 );
270+ if (maximumTotalCount < query.offset) {
271+ return PackageSearchResult .empty ();
272+ }
273+
266274 Iterable <IndexedPackageHit > indexedHits;
267275 switch (query.effectiveOrder) {
268276 case SearchOrder .top:
@@ -285,8 +293,8 @@ class InMemoryPackageIndex {
285293 }
286294 indexedHits = _rankWithValues (
287295 packageScores,
288- requiredLengthThreshold: query.offset,
289296 bestNameIndex: bestNameIndex ?? - 1 ,
297+ topK: query.offset + query.limit,
290298 );
291299 break ;
292300 case SearchOrder .created:
@@ -512,33 +520,31 @@ class InMemoryPackageIndex {
512520 return _TextResults (topApiPages);
513521 }
514522
515- List <IndexedPackageHit > _rankWithValues (
523+ Iterable <IndexedPackageHit > _rankWithValues (
516524 IndexedScore <String > score, {
517- // if the item count is fewer than this threshold, an empty list will be returned
518- required int requiredLengthThreshold,
519- // When no best name match is applied, this parameter will be `-1`
525+ /// When no best name match is applied, this parameter will be `-1`
520526 required int bestNameIndex,
527+
528+ /// Return (and sort) only the top-k results.
529+ required int topK,
521530 }) {
522- final list = < IndexedPackageHit > [];
531+ final builder = TopKSortedListBuilder <int >(topK, (aIndex, bIndex) {
532+ if (aIndex == bestNameIndex) return - 1 ;
533+ if (bIndex == bestNameIndex) return 1 ;
534+ final aScore = score.getValue (aIndex);
535+ final bScore = score.getValue (bIndex);
536+ final scoreCompare = - aScore.compareTo (bScore);
537+ if (scoreCompare != 0 ) return scoreCompare;
538+ // if two packages got the same score, order by last updated
539+ return _compareUpdated (_documents[aIndex], _documents[bIndex]);
540+ });
523541 for (var i = 0 ; i < score.length; i++ ) {
524542 final value = score.getValue (i);
525543 if (value <= 0.0 && i != bestNameIndex) continue ;
526- list.add (IndexedPackageHit (
527- i, PackageHit (package: score.keys[i], score: value)));
528- }
529- if (requiredLengthThreshold > list.length) {
530- // There is no point to sort or even keep the results, as the search query offset ignores these anyway.
531- return [];
544+ builder.add (i);
532545 }
533- list.sort ((a, b) {
534- if (a.index == bestNameIndex) return - 1 ;
535- if (b.index == bestNameIndex) return 1 ;
536- final scoreCompare = - a.hit.score! .compareTo (b.hit.score! );
537- if (scoreCompare != 0 ) return scoreCompare;
538- // if two packages got the same score, order by last updated
539- return _compareUpdated (_documents[a.index], _documents[b.index]);
540- });
541- return list;
546+ return builder.getTopK ().map ((i) => IndexedPackageHit (
547+ i, PackageHit (package: score.keys[i], score: score.getValue (i))));
542548 }
543549
544550 List <IndexedPackageHit > _rankWithComparator (
0 commit comments