Skip to content

Commit 71fa552

Browse files
authored
Use extracted dartdoc texts in search. (#1428)
* Use extracted dartdoc texts in search. * Fix code review notes: wrapping and log level.
1 parent 519bf35 commit 71fa552

File tree

5 files changed

+99
-46
lines changed

5 files changed

+99
-46
lines changed

app/lib/search/backend.dart

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:gcloud/storage.dart';
1414
import 'package:gcloud/service_scope.dart' as ss;
1515
import 'package:json_annotation/json_annotation.dart';
1616

17+
import '../dartdoc/pub_dartdoc_data.dart';
1718
import '../frontend/model_properties.dart';
1819
import '../frontend/models.dart';
1920
import '../shared/analyzer_client.dart';
@@ -70,15 +71,15 @@ class SearchBackend {
7071
versionList.where((pv) => pv != null),
7172
key: (pv) => (pv as PackageVersion).package);
7273

73-
final indexJsonFutures = Future.wait(packages.map((p) =>
74-
dartdocClient.getContentBytes(p.name, 'latest', 'index.json',
74+
final pubDataFutures = Future.wait(packages.map((p) =>
75+
dartdocClient.getContentBytes(p.name, 'latest', 'pub-data.json',
7576
timeout: const Duration(seconds: 10))));
7677

7778
final List<AnalysisView> analysisViews =
7879
await analyzerClient.getAnalysisViews(packages.map((p) =>
7980
p == null ? null : new AnalysisKey(p.name, p.latestVersion)));
8081

81-
final indexJsonContents = await indexJsonFutures;
82+
final pubDataContents = await pubDataFutures;
8283

8384
final List<PackageDocument> results = new List(packages.length);
8485
for (int i = 0; i < packages.length; i++) {
@@ -90,8 +91,15 @@ class SearchBackend {
9091
final analysisView = analysisViews[i];
9192
final double popularity = popularityStorage.lookup(pv.package) ?? 0.0;
9293

93-
final List<int> indexJsonContent = indexJsonContents[i];
94-
final apiDocPages = _apiDocPagesFromIndexJson(indexJsonContent);
94+
final List<int> pubDataContent = pubDataContents[i];
95+
List<ApiDocPage> apiDocPages;
96+
if (pubDataContent != null) {
97+
try {
98+
apiDocPages = _apiDocPagesFromPubData(pubDataContent);
99+
} catch (e, st) {
100+
_logger.severe('Parsing pub-data.json failed.', e, st);
101+
}
102+
}
95103

96104
results[i] = new PackageDocument(
97105
package: pv.package,
@@ -135,43 +143,56 @@ class SearchBackend {
135143
return emails.toList()..sort();
136144
}
137145

138-
List<ApiDocPage> _apiDocPagesFromIndexJson(List<int> bytes) {
139-
if (bytes == null) return null;
140-
try {
141-
final list = json.decode(utf8.decode(bytes));
142-
143-
final pathMap = <String, String>{};
144-
final symbolMap = <String, Set<String>>{};
145-
for (Map map in list) {
146-
final String name = map['name'];
147-
final type = map['type'];
148-
if (isCommonApiSymbol(name) && type != 'library') {
149-
continue;
150-
}
146+
List<ApiDocPage> _apiDocPagesFromPubData(List<int> bytes) {
147+
final decodedMap = json.decode(utf8.decode(bytes)) as Map;
148+
final pubData = new PubDartdocData.fromJson(decodedMap.cast());
149+
150+
final nameToKindMap = <String, String>{};
151+
pubData.apiElements.forEach((e) {
152+
nameToKindMap[e.name] = e.kind;
153+
});
151154

152-
final String qualifiedName = map['qualifiedName'];
153-
final enclosedBy = map['enclosedBy'];
154-
final enclosedByType = enclosedBy is Map ? enclosedBy['type'] : null;
155-
final parentLevel = enclosedByType == 'class' ? 2 : 1;
156-
final String key = qualifiedName.split('.').take(parentLevel).join('.');
155+
final pathMap = <String, String>{};
156+
final symbolMap = <String, Set<String>>{};
157+
final docMap = <String, List<String>>{};
157158

158-
if (key == qualifiedName) {
159-
pathMap[key] = map['href'] as String;
160-
}
161-
symbolMap.putIfAbsent(key, () => new Set()).add(name);
162-
}
159+
bool isTopLevel(String kind) => kind == 'library' || kind == 'class';
163160

164-
final results = pathMap.keys.map((key) {
165-
final path = pathMap[key];
166-
final symbols = symbolMap[key].toList()..sort();
167-
return new ApiDocPage(relativePath: path, symbols: symbols);
168-
}).toList();
169-
results.sort((a, b) => a.relativePath.compareTo(b.relativePath));
170-
return results;
171-
} catch (e, st) {
172-
_logger.warning('Parsing dartdoc index.json failed.', e, st);
161+
void update(String key, String name, String documentation) {
162+
final set = symbolMap.putIfAbsent(key, () => new Set<String>());
163+
set.addAll(name.split('.'));
164+
165+
documentation = documentation?.trim();
166+
if (documentation != null && documentation.isNotEmpty) {
167+
final list = docMap.putIfAbsent(key, () => []);
168+
list.add(compactReadme(documentation));
169+
}
173170
}
174-
return null;
171+
172+
pubData.apiElements.forEach((apiElement) {
173+
if (isTopLevel(apiElement.kind)) {
174+
pathMap[apiElement.name] = apiElement.href;
175+
update(apiElement.name, apiElement.name, apiElement.documentation);
176+
}
177+
178+
if (!isTopLevel(apiElement.kind) &&
179+
apiElement.parent != null &&
180+
isTopLevel(nameToKindMap[apiElement.parent])) {
181+
update(apiElement.parent, apiElement.name, apiElement.documentation);
182+
}
183+
});
184+
185+
final results = pathMap.keys.map((key) {
186+
final path = pathMap[key];
187+
final symbols = symbolMap[key].toList()..sort();
188+
return new ApiDocPage(
189+
relativePath: path,
190+
symbols: symbols,
191+
textBlocks: docMap[key],
192+
);
193+
}).toList();
194+
results.sort((a, b) => a.relativePath.compareTo(b.relativePath));
195+
return results;
175196
}
176197
}
177198

app/lib/search/index_simple.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ class SimplePackageIndex implements PackageIndex {
8585
_descrIndex.add(doc.package, doc.description);
8686
_readmeIndex.add(doc.package, doc.readme);
8787
for (ApiDocPage page in doc.apiDocPages ?? const []) {
88-
_apiDocIndex.add(
89-
_apiDocPageId(doc.package, page), page.symbols?.join(' '));
88+
final text = [page.symbols, page.textBlocks]
89+
.where((list) => list != null && list.isNotEmpty)
90+
.expand((list) => list)
91+
.join(' ');
92+
_apiDocIndex.add(_apiDocPageId(doc.package, page), text);
9093
}
9194
final String allText = [doc.package, doc.description, doc.readme]
9295
.where((s) => s != null)

app/lib/shared/search_service.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ class PackageDocument extends Object with _$PackageDocumentSerializerMixin {
116116
class ApiDocPage extends Object with _$ApiDocPageSerializerMixin {
117117
final String relativePath;
118118
final List<String> symbols;
119+
final List<String> textBlocks;
119120

120-
ApiDocPage({this.relativePath, this.symbols});
121+
ApiDocPage({this.relativePath, this.symbols, this.textBlocks});
121122

122123
factory ApiDocPage.fromJson(Map<String, dynamic> json) =>
123124
_$ApiDocPageFromJson(json);
@@ -126,6 +127,7 @@ class ApiDocPage extends Object with _$ApiDocPageSerializerMixin {
126127
return new ApiDocPage(
127128
relativePath: internFn(relativePath),
128129
symbols: symbols?.map(internFn)?.toList(),
130+
textBlocks: textBlocks,
129131
);
130132
}
131133
}

app/lib/shared/search_service.g.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,20 @@ abstract class _$PackageDocumentSerializerMixin {
8686
ApiDocPage _$ApiDocPageFromJson(Map<String, dynamic> json) {
8787
return new ApiDocPage(
8888
relativePath: json['relativePath'] as String,
89-
symbols: (json['symbols'] as List)?.map((e) => e as String)?.toList());
89+
symbols: (json['symbols'] as List)?.map((e) => e as String)?.toList(),
90+
textBlocks:
91+
(json['textBlocks'] as List)?.map((e) => e as String)?.toList());
9092
}
9193

9294
abstract class _$ApiDocPageSerializerMixin {
9395
String get relativePath;
9496
List<String> get symbols;
95-
Map<String, dynamic> toJson() =>
96-
<String, dynamic>{'relativePath': relativePath, 'symbols': symbols};
97+
List<String> get textBlocks;
98+
Map<String, dynamic> toJson() => <String, dynamic>{
99+
'relativePath': relativePath,
100+
'symbols': symbols,
101+
'textBlocks': textBlocks
102+
};
97103
}
98104

99105
PackageSearchResult _$PackageSearchResultFromJson(Map<String, dynamic> json) {

app/test/search/api_doc_page_test.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ void main() {
2727
'generateWebPage',
2828
'WebPageGenerator',
2929
],
30+
textBlocks: [
31+
'Some fancy goal is described here.',
32+
],
3033
),
3134
],
3235
));
@@ -100,7 +103,7 @@ void main() {
100103
'packages': [
101104
{
102105
'package': 'foo',
103-
'score': closeTo(0.481, 0.001), // find WebPageGenerator
106+
'score': closeTo(0.478, 0.001), // find WebPageGenerator
104107
'apiPages': [
105108
{'title': null, 'path': 'generator.html'},
106109
],
@@ -119,7 +122,7 @@ void main() {
119122
'packages': [
120123
{
121124
'package': 'foo',
122-
'score': closeTo(0.572, 0.001), // find WebPageGenerator
125+
'score': closeTo(0.570, 0.001), // find WebPageGenerator
123126
'apiPages': [
124127
{'title': null, 'path': 'generator.html'},
125128
],
@@ -135,5 +138,23 @@ void main() {
135138
],
136139
});
137140
});
141+
142+
test('text block', () async {
143+
final PackageSearchResult result = await index.search(
144+
new SearchQuery.parse(query: 'goal fancy', order: SearchOrder.text));
145+
expect(json.decode(json.encode(result)), {
146+
'indexUpdated': isNotNull,
147+
'totalCount': 1,
148+
'packages': [
149+
{
150+
'package': 'foo',
151+
'score': closeTo(0.624, 0.001),
152+
'apiPages': [
153+
{'title': null, 'path': 'generator.html'},
154+
],
155+
},
156+
],
157+
});
158+
});
138159
});
139160
}

0 commit comments

Comments
 (0)