Files
pyramid/lib/features/search/local_message_search.dart
T
Bernd SteckmeisterandClaude Opus 5.5 d5ff7982a0 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]>
2026-10-07 11:55:28 +02:00

63 lines
2.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;