_TextResults? _searchText()

in app/lib/search/mem_index.dart [316:433]


  _TextResults? _searchText(
    Set<String> packages,
    String? text, {
    required bool hasHighlightedHit,
  }) {
    final sw = Stopwatch()..start();
    if (text != null && text.isNotEmpty) {
      final words = splitForQuery(text);
      if (words.isEmpty) {
        return _TextResults(Score.empty(), <String, List<String>>{});
      }

      bool aborted = false;

      bool checkAborted() {
        if (!aborted && sw.elapsed > _textSearchTimeout) {
          aborted = true;
          _logger.info(
              '[pub-aborted-search-query] Aborted text search after ${sw.elapsedMilliseconds} ms.');
        }
        return aborted;
      }

      final nameScore =
          _packageNameIndex.searchWords(words, packages: packages);

      final descr =
          _descrIndex.searchWords(words, weight: 0.90, limitToIds: packages);
      final readme =
          _readmeIndex.searchWords(words, weight: 0.75, limitToIds: packages);

      final core = Score.max([nameScore, descr, readme]);

      var symbolPages = Score.empty();
      if (!checkAborted()) {
        symbolPages = _apiSymbolIndex.searchWords(words, weight: 0.70);
      }

      // Do documentation text search only when there was no reasonable core result
      // and no reasonable API symbol result.
      var dartdocPages = Score.empty();
      final shouldSearchApiText = !hasHighlightedHit &&
          core.maxValue < 0.4 &&
          symbolPages.maxValue < 0.3;
      if (!checkAborted() && shouldSearchApiText) {
        final sw = Stopwatch()..start();
        dartdocPages = _apiDartdocIndex.searchWords(words, weight: 0.40);
        _logger.info('[pub-search-query-with-api-dartdoc-index] '
            'core: ${core.length}/${core.maxValue} '
            'symbols: ${symbolPages.length}/${symbolPages.maxValue} '
            'documentation: ${dartdocPages.length}/${dartdocPages.maxValue} '
            'elapsed: ${sw.elapsed}');
      } else {
        _logger.info('[pub-search-query-without-api-dartdoc-index] '
            'hasHighlightedHit: $hasHighlightedHit '
            'core: ${core.length}/${core.maxValue} '
            'symbols: ${symbolPages.length}/${symbolPages.maxValue}');
      }
      final logTags = [
        if (symbolPages.isNotEmpty) '[pub-search-query-api-symbols-found]',
        if (dartdocPages.isNotEmpty) '[pub-search-query-api-dartdoc-found]',
        if (!hasHighlightedHit && symbolPages.maxValue > core.maxValue)
          '[pub-search-query-api-symbols-better-than-core]',
        if (!hasHighlightedHit && dartdocPages.maxValue > core.maxValue)
          '[pub-search-query-api-dartdoc-better-than-core]',
        if (!hasHighlightedHit && symbolPages.maxValue > dartdocPages.maxValue)
          '[pub-search-query-api-dartdoc-better-than-symbols]',
      ];
      if (logTags.isNotEmpty) {
        _logger.info('[pub-search-query-api-improved] ${logTags.join(' ')}');
      }

      final apiDocScore = Score.max([symbolPages, dartdocPages]);
      final apiPackages = <String, double>{};
      for (String key in apiDocScore.getKeys()) {
        final pkg = _apiDocPkg(key);
        if (!packages.contains(pkg)) continue;
        final value = apiDocScore[key];
        apiPackages[pkg] = math.max(value, apiPackages[pkg] ?? 0.0);
      }
      final apiPkgScore = Score(apiPackages);
      var score = Score.max([core, apiPkgScore])
          .project(packages)
          .removeLowValues(fraction: 0.2, minValue: 0.01);

      // filter results based on exact phrases
      final phrases = extractExactPhrases(text);
      if (!aborted && phrases.isNotEmpty) {
        final Map<String, double> matched = <String, double>{};
        for (String package in score.getKeys()) {
          final doc = _packages[package]!;
          final bool matchedAllPhrases = phrases.every((phrase) =>
              doc.package.contains(phrase) ||
              doc.description!.contains(phrase) ||
              doc.readme!.contains(phrase));
          if (matchedAllPhrases) {
            matched[package] = score[package];
          }
        }
        score = Score(matched);
      }

      final apiDocKeys = apiDocScore.getKeys().toList()
        ..sort((a, b) => -apiDocScore[a].compareTo(apiDocScore[b]));
      final topApiPages = <String, List<String>>{};
      for (String key in apiDocKeys) {
        final pkg = _apiDocPkg(key);
        final pages = topApiPages.putIfAbsent(pkg, () => []);
        if (pages.length < 3) {
          final page = _apiDocPath(key);
          pages.add(page);
        }
      }

      return _TextResults(score, topApiPages);
    }
    return null;
  }