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]>
63 lines
2.2 KiB
Dart
63 lines
2.2 KiB
Dart
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;
|