@@ -226,6 +226,7 @@ class InMemoryPackageIndex {
226
226
packageScores,
227
227
parsedQueryText,
228
228
includeNameMatches: (query.offset ?? 0 ) == 0 ,
229
+ textMatchExtent: query.textMatchExtent,
229
230
);
230
231
231
232
final nameMatches = textResults? .nameMatches;
@@ -287,7 +288,9 @@ class InMemoryPackageIndex {
287
288
boundedList (indexedHits, offset: query.offset, limit: query.limit);
288
289
289
290
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 )) {
291
294
packageHits = indexedHits.map ((ps) {
292
295
final apiPages = textResults.topApiPages? [ps.index]
293
296
// TODO(https://github.com/dart-lang/pub-dev/issues/7106): extract title for the page
@@ -305,6 +308,7 @@ class InMemoryPackageIndex {
305
308
nameMatches: nameMatches,
306
309
topicMatches: topicMatches,
307
310
packageHits: packageHits,
311
+ errorMessage: textResults? .errorMessage,
308
312
);
309
313
}
310
314
@@ -332,61 +336,82 @@ class InMemoryPackageIndex {
332
336
IndexedScore <String > packageScores,
333
337
String ? text, {
334
338
required bool includeNameMatches,
339
+ required int ? textMatchExtent,
335
340
}) {
341
+ if (text == null || text.isEmpty) {
342
+ return null ;
343
+ }
344
+
336
345
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
+ }
345
351
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
+ }
347
359
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.' );
355
366
}
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 ();
356
381
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)) {
359
389
nameMatches ?? = < String > {};
360
- nameMatches.add (text );
390
+ nameMatches.add (word );
361
391
}
362
392
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);
374
398
375
- _scorePool.withScore (
376
- value: 0.0 ,
377
- fn: (wordScore) {
378
- _packageNameIndex.searchWord (word,
379
- score: wordScore, filterOnNonZeros: packageScores);
399
+ if (matchDescription) {
380
400
_descrIndex.searchAndAccumulate (word, score: wordScore);
401
+ }
402
+ if (matchReadme) {
381
403
_readmeIndex.searchAndAccumulate (word,
382
404
weight: 0.75 , score: wordScore);
383
- packageScores.multiplyAllFrom (wordScore);
384
- },
385
- );
386
- }
405
+ }
406
+ packageScores.multiplyAllFrom (wordScore);
407
+ },
408
+ );
409
+ }
387
410
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) {
390
415
const maxApiPageCount = 2 ;
391
416
if (! checkAborted ()) {
392
417
_apiSymbolIndex.withSearchWords (words, weight: 0.70 , (symbolPages) {
@@ -420,29 +445,28 @@ class InMemoryPackageIndex {
420
445
}
421
446
});
422
447
}
448
+ }
423
449
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 );
437
462
}
438
463
}
439
-
440
- return _TextResults (
441
- topApiPages,
442
- nameMatches: nameMatches? .toList (),
443
- );
444
464
}
445
- return null ;
465
+
466
+ return _TextResults (
467
+ topApiPages,
468
+ nameMatches: nameMatches? .toList (),
469
+ );
446
470
}
447
471
448
472
List <IndexedPackageHit > _rankWithValues (
@@ -521,15 +545,20 @@ class InMemoryPackageIndex {
521
545
class _TextResults {
522
546
final List <List <MapEntry <String , double >>?>? topApiPages;
523
547
final List <String >? nameMatches;
548
+ final String ? errorMessage;
524
549
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
+ }
529
557
530
558
_TextResults (
531
559
this .topApiPages, {
532
560
required this .nameMatches,
561
+ this .errorMessage,
533
562
});
534
563
}
535
564
0 commit comments