Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 12 additions & 19 deletions app/lib/search/mem_index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,31 +113,28 @@ class InMemoryPackageIndex {
}

PackageSearchResult search(ServiceSearchQuery query) {
final packages = Set<String>.of(_documentsByName.keys);
final packageScores = IndexedScore(_packageNameIndex._packageNames, 1.0);

// filter on package prefix
if (query.parsedQuery.packagePrefix != null) {
final String prefix = query.parsedQuery.packagePrefix!.toLowerCase();
packages.removeWhere(
(package) => !_documentsByName[package]!
.package
.toLowerCase()
.startsWith(prefix),
packageScores.retainWhere(
(i, _) => _documents[i].packageNameLowerCased.startsWith(prefix),
);
}

// filter on tags
final combinedTagsPredicate =
query.tagsPredicate.appendPredicate(query.parsedQuery.tagsPredicate);
if (combinedTagsPredicate.isNotEmpty) {
packages.retainWhere((package) => combinedTagsPredicate
.matches(_documentsByName[package]!.tagsForLookup));
packageScores.retainWhere(
(i, _) => combinedTagsPredicate.matches(_documents[i].tagsForLookup));
}

// filter on dependency
if (query.parsedQuery.hasAnyDependency) {
packages.removeWhere((package) {
final doc = _documentsByName[package]!;
packageScores.removeWhere((i, _) {
final doc = _documents[i];
if (doc.dependencies.isEmpty) return true;
for (final dependency in query.parsedQuery.allDependencies) {
if (!doc.dependencies.containsKey(dependency)) return true;
Expand All @@ -152,22 +149,18 @@ class InMemoryPackageIndex {

// filter on points
if (query.minPoints != null && query.minPoints! > 0) {
packages.removeWhere((package) {
final doc = _documentsByName[package]!;
return doc.grantedPoints < query.minPoints!;
});
packageScores.removeWhere(
(i, _) => _documents[i].grantedPoints < query.minPoints!);
}

// filter on updatedDuration
final updatedDuration = query.parsedQuery.updatedDuration;
if (updatedDuration != null && updatedDuration > Duration.zero) {
final now = clock.now();
packages.removeWhere((package) {
final doc = _documentsByName[package]!;
final diff = now.difference(doc.updated);
return diff > updatedDuration;
});
packageScores.removeWhere(
(i, _) => now.difference(_documents[i].updated) > updatedDuration);
}
final packages = packageScores.toKeySet();

// do text matching
final parsedQueryText = query.parsedQuery.text;
Expand Down
2 changes: 2 additions & 0 deletions app/lib/search/search_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class PackageDocument {

@JsonKey(includeFromJson: false, includeToJson: false)
late final Set<String> tagsForLookup = Set.of(tags);

late final packageNameLowerCased = package.toLowerCase();
}

/// A reference to an API doc page
Expand Down
67 changes: 58 additions & 9 deletions app/lib/search/token_index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,27 +214,26 @@ class TokenIndex {
Map<String, double> _scoreDocs(TokenMatch tokenMatch,
{double weight = 1.0, Set<String>? limitToIds}) {
// Summarize the scores for the documents.
final docScores = List<double>.filled(_length, 0.0);
final scores = IndexedScore(_ids);
for (final token in tokenMatch.tokens) {
final docWeights = _inverseIds[token]!;
for (final e in docWeights.entries) {
final i = e.key;
docScores[i] = math.max(docScores[i], tokenMatch[token]! * e.value);
scores.setValueMaxOf(e.key, tokenMatch[token]! * e.value);
}
}

if (limitToIds != null) {
scores.retainWhere((_, id) => limitToIds.contains(id));
}
final result = <String, double>{};
// post-process match weights
for (var i = 0; i < _length; i++) {
final id = _ids[i];
final w = docScores[i];
final w = scores._values[i];
if (w <= 0.0) {
continue;
}
if (limitToIds != null && !limitToIds.contains(id)) {
continue;
}
result[id] = w * weight;
final id = _ids[i];
result[id] = scores._values[i] * weight;
}
return result;
}
Expand Down Expand Up @@ -269,3 +268,53 @@ class TokenIndex {
return Score.multiply(scores);
}
}

/// Mutable score list that can accessed via integer index.
class IndexedScore {
final List<String> _keys;
final List<double> _values;

IndexedScore._(this._keys, this._values);

factory IndexedScore(List<String> keys, [double value = 0.0]) =>
IndexedScore._(keys, List<double>.filled(keys.length, value));

late final length = _values.length;

bool isNotPositive(int index) {
return _values[index] <= 0.0;
}

void setValueMaxOf(int index, double value) {
_values[index] = math.max(_values[index], value);
}

void removeWhere(bool Function(int index, String key) fn) {
for (var i = 0; i < length; i++) {
if (isNotPositive(i)) continue;
if (fn(i, _keys[i])) {
_values[i] = 0.0;
}
}
}

void retainWhere(bool Function(int index, String key) fn) {
for (var i = 0; i < length; i++) {
if (isNotPositive(i)) continue;
if (!fn(i, _keys[i])) {
_values[i] = 0.0;
}
}
}

Set<String> toKeySet() {
final set = <String>{};
for (var i = 0; i < _values.length; i++) {
final v = _values[i];
if (v > 0.0) {
set.add(_keys[i]);
}
}
return set;
}
}
Loading