refactor: lokale Nachrichtensuche als eigenes Modul (lib/features/search) + 5 Vertragstests
Verhaltensgleich aus search_modal.dart herausgeloest (gleiche Deckel 1500/60/500, gleiche Schleife; Medien-Filter jetzt Parameter). Tests laufen ueber die echte SDK-DB-Schicht mit synthetischen Daten (test/support/synthetic_matrix_data.dart). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b2413ef627
commit
d5ff7982a0
@@ -0,0 +1,62 @@
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
/// Lokale Nachrichtensuche über die entschlüsselten Events in der Client-DB.
|
||||
///
|
||||
/// Die server-seitige `/search`-API sieht bei E2EE nur Chiffretext und liefert
|
||||
/// für verschlüsselte Räume nichts – deshalb wird (wie bei Element) die lokale
|
||||
/// Datenbank durchsucht. Aus `search_modal.dart` herausgelöst, damit Tests und
|
||||
/// `benchmark/` genau diesen Codepfad prüfen/messen können.
|
||||
///
|
||||
/// Bei [mediaOnly] werden nur Medien-Nachrichten geliefert, deren
|
||||
/// Dateiname/Beschriftung passt. Ergebnis: neueste zuerst, höchstens
|
||||
/// [searchTotalCap] Treffer; pro Raum werden höchstens
|
||||
/// [searchPerRoomScanCap] Events gescannt.
|
||||
Future<List<Event>> localMessageSearch(
|
||||
Client client,
|
||||
String query, {
|
||||
bool mediaOnly = false,
|
||||
}) async {
|
||||
final db = client.database;
|
||||
final lower = query.toLowerCase();
|
||||
const mediaTypes = {'m.image', 'm.video', 'm.file', 'm.audio'};
|
||||
|
||||
final results = <Event>[];
|
||||
for (final room in client.rooms.where((r) => !r.isSpace)) {
|
||||
var start = 0;
|
||||
var scanned = 0;
|
||||
try {
|
||||
while (scanned < searchPerRoomScanCap) {
|
||||
final events = await db.getEventList(
|
||||
room,
|
||||
start: start,
|
||||
limit: searchScanChunk,
|
||||
);
|
||||
if (events.isEmpty) break;
|
||||
for (final e in events) {
|
||||
if (e.type != EventTypes.Message || e.redacted) continue;
|
||||
final msgtype = e.content.tryGet<String>('msgtype') ?? '';
|
||||
if (mediaOnly && !mediaTypes.contains(msgtype)) continue;
|
||||
if (!e.body.toLowerCase().contains(lower)) continue;
|
||||
results.add(e);
|
||||
}
|
||||
start += searchScanChunk;
|
||||
scanned += events.length;
|
||||
final roomExhausted = events.length < searchScanChunk;
|
||||
if (roomExhausted || results.length >= searchTotalCap) break;
|
||||
}
|
||||
} catch (_) {}
|
||||
if (results.length >= searchTotalCap) break;
|
||||
}
|
||||
|
||||
results.sort((a, b) => b.originServerTs.compareTo(a.originServerTs));
|
||||
return results.take(searchTotalCap).toList();
|
||||
}
|
||||
|
||||
/// Höchstens so viele Events werden pro Raum gescannt.
|
||||
const searchPerRoomScanCap = 1500;
|
||||
|
||||
/// Höchstens so viele Treffer liefert die Suche insgesamt.
|
||||
const searchTotalCap = 60;
|
||||
|
||||
/// So viele Events werden pro DB-Abfrage geladen.
|
||||
const searchScanChunk = 500;
|
||||
Reference in New Issue
Block a user