@@ -19,11 +19,12 @@ final _logger = Logger('search.mem_index');
1919final _textSearchTimeout = Duration (milliseconds: 500 );
2020
2121class InMemoryPackageIndex {
22- final Map <String , PackageDocument > _packages = < String , PackageDocument > {};
22+ final List <PackageDocument > _documents;
23+ final _documentsByName = < String , PackageDocument > {};
2324 final _packageNameIndex = PackageNameIndex ();
24- final TokenIndex _descrIndex = TokenIndex () ;
25- final TokenIndex _readmeIndex = TokenIndex () ;
26- final TokenIndex _apiSymbolIndex = TokenIndex () ;
25+ late final TokenIndex _descrIndex;
26+ late final TokenIndex _readmeIndex;
27+ late final TokenIndex _apiSymbolIndex;
2728
2829 /// Adjusted score takes the overall score and transforms
2930 /// it linearly into the [0.4-1.0] range.
@@ -39,13 +40,38 @@ class InMemoryPackageIndex {
3940
4041 InMemoryPackageIndex ({
4142 required Iterable <PackageDocument > documents,
42- }) {
43- for (final doc in documents) {
44- _addPackage (doc);
43+ }) : _documents = [...documents] {
44+ final apiDocPageKeys = < String > [];
45+ final apiDocPageValues = < String > [];
46+ for (final doc in _documents) {
47+ _documentsByName[doc.package] = doc;
48+ _packageNameIndex.add (doc.package);
49+
50+ final apiDocPages = doc.apiDocPages;
51+ if (apiDocPages != null ) {
52+ for (final page in apiDocPages) {
53+ if (page.symbols != null && page.symbols! .isNotEmpty) {
54+ apiDocPageKeys.add (_apiDocPageId (doc.package, page));
55+ apiDocPageValues.add (page.symbols! .join (' ' ));
56+ }
57+ }
58+ }
4559 }
60+
61+ final packageKeys = _documents.map ((d) => d.package).toList ();
62+ _descrIndex = TokenIndex (
63+ packageKeys,
64+ _documents.map ((d) => d.description).toList (),
65+ );
66+ _readmeIndex = TokenIndex (
67+ packageKeys,
68+ _documents.map ((d) => d.readme).toList (),
69+ );
70+ _apiSymbolIndex = TokenIndex (apiDocPageKeys, apiDocPageValues);
71+
4672 // update like scores only if they were not set (should happen only in local tests)
47- if (_packages .values.any ((e) => e.likeScore == null )) {
48- _packages .values.updateLikeScores ();
73+ if (_documentsByName .values.any ((e) => e.likeScore == null )) {
74+ _documentsByName .values.updateLikeScores ();
4975 }
5076 _updateOverallScores ();
5177 _lastUpdated = clock.now ().toUtc ();
@@ -64,49 +90,37 @@ class InMemoryPackageIndex {
6490 IndexInfo indexInfo () {
6591 return IndexInfo (
6692 isReady: true ,
67- packageCount: _packages .length,
93+ packageCount: _documentsByName .length,
6894 lastUpdated: _lastUpdated,
6995 );
7096 }
7197
72- void _addPackage (PackageDocument doc) {
73- _packages[doc.package] = doc;
74- _packageNameIndex.add (doc.package);
75- _descrIndex.add (doc.package, doc.description);
76- _readmeIndex.add (doc.package, doc.readme);
77-
78- for (final ApiDocPage page in doc.apiDocPages ?? const []) {
79- final pageId = _apiDocPageId (doc.package, page);
80- if (page.symbols != null && page.symbols! .isNotEmpty) {
81- _apiSymbolIndex.add (pageId, page.symbols! .join (' ' ));
82- }
83- }
84- }
85-
8698 PackageSearchResult search (ServiceSearchQuery query) {
87- final packages = Set <String >.of (_packages .keys);
99+ final packages = Set <String >.of (_documentsByName .keys);
88100
89101 // filter on package prefix
90102 if (query.parsedQuery.packagePrefix != null ) {
91103 final String prefix = query.parsedQuery.packagePrefix! .toLowerCase ();
92104 packages.removeWhere (
93- (package) =>
94- ! _packages[package]! .package.toLowerCase ().startsWith (prefix),
105+ (package) => ! _documentsByName[package]!
106+ .package
107+ .toLowerCase ()
108+ .startsWith (prefix),
95109 );
96110 }
97111
98112 // filter on tags
99113 final combinedTagsPredicate =
100114 query.tagsPredicate.appendPredicate (query.parsedQuery.tagsPredicate);
101115 if (combinedTagsPredicate.isNotEmpty) {
102- packages.retainWhere ((package) =>
103- combinedTagsPredicate .matches (_packages [package]! .tagsForLookup));
116+ packages.retainWhere ((package) => combinedTagsPredicate
117+ .matches (_documentsByName [package]! .tagsForLookup));
104118 }
105119
106120 // filter on dependency
107121 if (query.parsedQuery.hasAnyDependency) {
108122 packages.removeWhere ((package) {
109- final doc = _packages [package]! ;
123+ final doc = _documentsByName [package]! ;
110124 if (doc.dependencies.isEmpty) return true ;
111125 for (final dependency in query.parsedQuery.allDependencies) {
112126 if (! doc.dependencies.containsKey (dependency)) return true ;
@@ -122,7 +136,7 @@ class InMemoryPackageIndex {
122136 // filter on points
123137 if (query.minPoints != null && query.minPoints! > 0 ) {
124138 packages.removeWhere ((package) {
125- final doc = _packages [package]! ;
139+ final doc = _documentsByName [package]! ;
126140 return doc.grantedPoints < query.minPoints! ;
127141 });
128142 }
@@ -132,7 +146,7 @@ class InMemoryPackageIndex {
132146 if (updatedDuration != null && updatedDuration > Duration .zero) {
133147 final now = clock.now ();
134148 packages.removeWhere ((package) {
135- final doc = _packages [package]! ;
149+ final doc = _documentsByName [package]! ;
136150 final diff = now.difference (doc.updated);
137151 return diff > updatedDuration;
138152 });
@@ -163,7 +177,8 @@ class InMemoryPackageIndex {
163177 .map ((key, value) => value * _adjustedOverallScores[key]! );
164178 // If the search hits have an exact name match, we move it to the front of the result list.
165179 final parsedQueryText = query.parsedQuery.text;
166- if (parsedQueryText != null && _packages.containsKey (parsedQueryText)) {
180+ if (parsedQueryText != null &&
181+ _documentsByName.containsKey (parsedQueryText)) {
167182 nameMatches = < String > [parsedQueryText];
168183 }
169184 packageHits = _rankWithValues (overallScore.getValues ());
@@ -215,7 +230,7 @@ class InMemoryPackageIndex {
215230
216231 /// Update the overall score both on [PackageDocument] and in the [_adjustedOverallScores] map.
217232 void _updateOverallScores () {
218- for (final doc in _packages .values) {
233+ for (final doc in _documentsByName .values) {
219234 final downloadScore = doc.popularityScore ?? 0.0 ;
220235 final likeScore = doc.likeScore ?? 0.0 ;
221236 final popularity = (downloadScore + likeScore) / 2 ;
@@ -316,7 +331,7 @@ class InMemoryPackageIndex {
316331 if (! aborted && phrases.isNotEmpty) {
317332 final matched = < String , double > {};
318333 for (final package in score.getKeys ()) {
319- final doc = _packages [package]! ;
334+ final doc = _documentsByName [package]! ;
320335 final bool matchedAllPhrases = phrases.every ((phrase) =>
321336 doc.package.contains (phrase) ||
322337 doc.description! .contains (phrase) ||
@@ -341,7 +356,8 @@ class InMemoryPackageIndex {
341356 final int scoreCompare = - a.score! .compareTo (b.score! );
342357 if (scoreCompare != 0 ) return scoreCompare;
343358 // if two packages got the same score, order by last updated
344- return _compareUpdated (_packages[a.package]! , _packages[b.package]! );
359+ return _compareUpdated (
360+ _documentsByName[a.package]! , _documentsByName[b.package]! );
345361 });
346362 return list;
347363 }
@@ -350,11 +366,12 @@ class InMemoryPackageIndex {
350366 int Function (PackageDocument a, PackageDocument b) compare, {
351367 double Function (PackageDocument doc)? score,
352368 }) {
353- final list = _packages .values
369+ final list = _documentsByName .values
354370 .map ((doc) => PackageHit (
355371 package: doc.package, score: score == null ? null : score (doc)))
356372 .toList ();
357- list.sort ((a, b) => compare (_packages[a.package]! , _packages[b.package]! ));
373+ list.sort ((a, b) =>
374+ compare (_documentsByName[a.package]! , _documentsByName[b.package]! ));
358375 return list;
359376 }
360377
0 commit comments