41 lines
1.2 KiB
Dart
41 lines
1.2 KiB
Dart
/// Lightweight fuzzy matching for product search.
|
|
library;
|
|
|
|
String _normalize(String value) =>
|
|
value.trim().toLowerCase().replaceAll(RegExp(r'\s+'), ' ');
|
|
|
|
/// True when [query] fuzzily matches [text].
|
|
bool fuzzyMatch(String query, String text) {
|
|
final q = _normalize(query);
|
|
if (q.isEmpty) return true;
|
|
final t = _normalize(text);
|
|
if (t.contains(q)) return true;
|
|
|
|
for (final word in q.split(' ')) {
|
|
if (word.isEmpty) continue;
|
|
if (!t.contains(word) && !_subsequence(word, t)) return false;
|
|
}
|
|
if (q.contains(' ')) return true;
|
|
return _subsequence(q, t);
|
|
}
|
|
|
|
/// Higher is better. Null when [query] does not match.
|
|
int? fuzzyScore(String query, String text) {
|
|
final q = _normalize(query);
|
|
if (q.isEmpty) return 0;
|
|
final t = _normalize(text);
|
|
if (t == q) return 1000;
|
|
if (t.startsWith(q)) return 800;
|
|
if (t.contains(q)) return 600 - t.indexOf(q);
|
|
if (!fuzzyMatch(q, t)) return null;
|
|
return 200 - (t.length - q.length).abs().clamp(0, 150);
|
|
}
|
|
|
|
bool _subsequence(String query, String text) {
|
|
var i = 0;
|
|
for (var c = 0; c < text.length && i < query.length; c++) {
|
|
if (text.codeUnitAt(c) == query.codeUnitAt(i)) i++;
|
|
}
|
|
return i == query.length;
|
|
}
|