@@ -226,6 +226,7 @@ class InMemoryPackageIndex {
226226 packageScores,
227227 parsedQueryText,
228228 includeNameMatches: (query.offset ?? 0 ) == 0 ,
229+ textMatchExtent: query.textMatchExtent,
229230 );
230231
231232 final nameMatches = textResults? .nameMatches;
@@ -287,7 +288,9 @@ class InMemoryPackageIndex {
287288 boundedList (indexedHits, offset: query.offset, limit: query.limit);
288289
289290 late List <PackageHit > packageHits;
290- if (textResults != null && (textResults.topApiPages? .isNotEmpty ?? false )) {
291+ if (TextMatchExtent .shouldMatchApi (query.textMatchExtent) &&
292+ textResults != null &&
293+ (textResults.topApiPages? .isNotEmpty ?? false )) {
291294 packageHits = indexedHits.map ((ps) {
292295 final apiPages = textResults.topApiPages? [ps.index]
293296 // TODO(https://github.com/dart-lang/pub-dev/issues/7106): extract title for the page
@@ -305,6 +308,7 @@ class InMemoryPackageIndex {
305308 nameMatches: nameMatches,
306309 topicMatches: topicMatches,
307310 packageHits: packageHits,
311+ errorMessage: textResults? .errorMessage,
308312 );
309313 }
310314
@@ -332,61 +336,82 @@ class InMemoryPackageIndex {
332336 IndexedScore <String > packageScores,
333337 String ? text, {
334338 required bool includeNameMatches,
339+ required int ? textMatchExtent,
335340 }) {
341+ if (text == null || text.isEmpty) {
342+ return null ;
343+ }
344+
336345 final sw = Stopwatch ()..start ();
337- if (text != null && text.isNotEmpty) {
338- final words = splitForQuery (text);
339- if (words.isEmpty) {
340- for (var i = 0 ; i < packageScores.length; i++ ) {
341- packageScores.setValue (i, 0 );
342- }
343- return _TextResults .empty ();
344- }
346+ final words = splitForQuery (text);
347+ if (words.isEmpty) {
348+ packageScores.fillRange (0 , packageScores.length, 0 );
349+ return _TextResults .empty ();
350+ }
345351
346- bool aborted = false ;
352+ final matchName = TextMatchExtent .shouldMatchName (textMatchExtent);
353+ if (! matchName) {
354+ packageScores.fillRange (0 , packageScores.length, 0 );
355+ return _TextResults .empty (
356+ errorMessage:
357+ 'Search index in reduced mode: unable to match query text.' );
358+ }
347359
348- bool checkAborted () {
349- if (! aborted && sw.elapsed > _textSearchTimeout) {
350- aborted = true ;
351- _logger.info (
352- '[pub-aborted-search-query] Aborted text search after ${sw .elapsedMilliseconds } ms.' );
353- }
354- return aborted;
360+ bool aborted = false ;
361+ bool checkAborted () {
362+ if (! aborted && sw.elapsed > _textSearchTimeout) {
363+ aborted = true ;
364+ _logger.info (
365+ '[pub-aborted-search-query] Aborted text search after ${sw .elapsedMilliseconds } ms.' );
355366 }
367+ return aborted;
368+ }
369+
370+ Set <String >? nameMatches;
371+ if (includeNameMatches && _documentsByName.containsKey (text)) {
372+ nameMatches ?? = < String > {};
373+ nameMatches.add (text);
374+ }
375+
376+ // Multiple words are scored separately, and then the individual scores
377+ // are multiplied. We can use a package filter that is applied after each
378+ // word to reduce the scope of the later words based on the previous results.
379+ /// However, API docs search should be filtered on the original list.
380+ final indexedPositiveList = packageScores.toIndexedPositiveList ();
356381
357- Set <String >? nameMatches;
358- if (includeNameMatches && _documentsByName.containsKey (text)) {
382+ final matchDescription =
383+ TextMatchExtent .souldMatchDescription (textMatchExtent);
384+ final matchReadme = TextMatchExtent .shouldMatchReadme (textMatchExtent);
385+ final matchApi = TextMatchExtent .shouldMatchApi (textMatchExtent);
386+
387+ for (final word in words) {
388+ if (includeNameMatches && _documentsByName.containsKey (word)) {
359389 nameMatches ?? = < String > {};
360- nameMatches.add (text );
390+ nameMatches.add (word );
361391 }
362392
363- // Multiple words are scored separately, and then the individual scores
364- // are multiplied. We can use a package filter that is applied after each
365- // word to reduce the scope of the later words based on the previous results.
366- /// However, API docs search should be filtered on the original list.
367- final indexedPositiveList = packageScores.toIndexedPositiveList ();
368-
369- for (final word in words) {
370- if (includeNameMatches && _documentsByName.containsKey (word)) {
371- nameMatches ?? = < String > {};
372- nameMatches.add (word);
373- }
393+ _scorePool.withScore (
394+ value: 0.0 ,
395+ fn: (wordScore) {
396+ _packageNameIndex.searchWord (word,
397+ score: wordScore, filterOnNonZeros: packageScores);
374398
375- _scorePool.withScore (
376- value: 0.0 ,
377- fn: (wordScore) {
378- _packageNameIndex.searchWord (word,
379- score: wordScore, filterOnNonZeros: packageScores);
399+ if (matchDescription) {
380400 _descrIndex.searchAndAccumulate (word, score: wordScore);
401+ }
402+ if (matchReadme) {
381403 _readmeIndex.searchAndAccumulate (word,
382404 weight: 0.75 , score: wordScore);
383- packageScores.multiplyAllFrom (wordScore);
384- },
385- );
386- }
405+ }
406+ packageScores.multiplyAllFrom (wordScore);
407+ },
408+ );
409+ }
387410
388- final topApiPages =
389- List <List <MapEntry <String , double >>?>.filled (_documents.length, null );
411+ final topApiPages =
412+ List <List <MapEntry <String , double >>?>.filled (_documents.length, null );
413+
414+ if (matchApi) {
390415 const maxApiPageCount = 2 ;
391416 if (! checkAborted ()) {
392417 _apiSymbolIndex.withSearchWords (words, weight: 0.70 , (symbolPages) {
@@ -420,29 +445,28 @@ class InMemoryPackageIndex {
420445 }
421446 });
422447 }
448+ }
423449
424- // filter results based on exact phrases
425- final phrases = extractExactPhrases (text);
426- if (! aborted && phrases.isNotEmpty) {
427- for (var i = 0 ; i < packageScores.length; i++ ) {
428- if (packageScores.isNotPositive (i)) continue ;
429- final doc = _documents[i];
430- final matchedAllPhrases = phrases.every ((phrase) =>
431- doc.package.contains (phrase) ||
432- doc.description! .contains (phrase) ||
433- doc.readme! .contains (phrase));
434- if (! matchedAllPhrases) {
435- packageScores.setValue (i, 0 );
436- }
450+ // filter results based on exact phrases
451+ final phrases = extractExactPhrases (text);
452+ if (! aborted && phrases.isNotEmpty) {
453+ for (var i = 0 ; i < packageScores.length; i++ ) {
454+ if (packageScores.isNotPositive (i)) continue ;
455+ final doc = _documents[i];
456+ final matchedAllPhrases = phrases.every ((phrase) =>
457+ (matchName && doc.package.contains (phrase)) ||
458+ (matchDescription && doc.description! .contains (phrase)) ||
459+ (matchReadme && doc.readme! .contains (phrase)));
460+ if (! matchedAllPhrases) {
461+ packageScores.setValue (i, 0 );
437462 }
438463 }
439-
440- return _TextResults (
441- topApiPages,
442- nameMatches: nameMatches? .toList (),
443- );
444464 }
445- return null ;
465+
466+ return _TextResults (
467+ topApiPages,
468+ nameMatches: nameMatches? .toList (),
469+ );
446470 }
447471
448472 List <IndexedPackageHit > _rankWithValues (
@@ -521,15 +545,20 @@ class InMemoryPackageIndex {
521545class _TextResults {
522546 final List <List <MapEntry <String , double >>?>? topApiPages;
523547 final List <String >? nameMatches;
548+ final String ? errorMessage;
524549
525- factory _TextResults .empty () => _TextResults (
526- null ,
527- nameMatches: null ,
528- );
550+ factory _TextResults .empty ({String ? errorMessage}) {
551+ return _TextResults (
552+ null ,
553+ nameMatches: null ,
554+ errorMessage: errorMessage,
555+ );
556+ }
529557
530558 _TextResults (
531559 this .topApiPages, {
532560 required this .nameMatches,
561+ this .errorMessage,
533562 });
534563}
535564
0 commit comments