From 18b90c8df332baf42d69ea91a92bfb06794ee50f Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 28 Dec 2025 18:16:29 +0100 Subject: [PATCH] bug fix --- app/frontend/ui_graph_service.py | 84 ++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/app/frontend/ui_graph_service.py b/app/frontend/ui_graph_service.py index 090dd9f..009a0ed 100644 --- a/app/frontend/ui_graph_service.py +++ b/app/frontend/ui_graph_service.py @@ -20,6 +20,7 @@ class GraphExplorerService: self.chunks_col = f"{prefix}_chunks" self.edges_col = f"{prefix}_edges" self._note_cache = {} # Cache für Note-Payloads + self._ref_resolution_cache = {} # Cache für aufgelöste Referenzen (ref_str -> note_payload) self._note_cache = {} def get_note_with_full_content(self, note_id): @@ -457,6 +458,11 @@ class GraphExplorerService: """ if not ref_str: return None + # Cache-Check: Wenn wir diese Referenz bereits aufgelöst haben, verwende das Ergebnis + if ref_str in self._ref_resolution_cache: + cached_result = self._ref_resolution_cache[ref_str] + return cached_result + # Fall A: Enthält # (kann Chunk-ID oder Wikilink mit Abschnitt sein) if "#" in ref_str: try: @@ -466,7 +472,9 @@ class GraphExplorerService: note_id = res[0].payload.get("note_id") if note_id: note = self._fetch_note_cached(note_id) - if note: return note + if note: + self._ref_resolution_cache[ref_str] = note + return note except Exception: pass @@ -474,7 +482,9 @@ class GraphExplorerService: # z.B. "20250101-meine-note#Abschnitt" -> "20250101-meine-note" possible_note_id = ref_str.split("#")[0] note = self._fetch_note_cached(possible_note_id) - if note: return note + if note: + self._ref_resolution_cache[ref_str] = note + return note # Versuch 3: Wikilink-Text mit Abschnitt (z.B. "Meine Prinzipien 2025#P3 – Disziplin") # WICHTIG: target_id ist der vollständige Wikilink-Text, wir müssen den Titel-Teil extrahieren @@ -529,26 +539,62 @@ class GraphExplorerService: pass # Versuch 3c: Fallback - Lade alle Notes und filtere clientseitig (nur wenn Text-Suche fehlschlägt) + # OPTIMIERUNG: Verwende einen globalen Cache für alle Notes-Titel, um Performance zu verbessern # Dies ist langsamer, aber findet auch Notes, die die Text-Suche nicht findet if possible_title_norm and len(possible_title_norm) > 5: try: - # Lade eine größere Anzahl von Notes (nur wenn nötig) - res_all, _ = self.client.scroll( - collection_name=self.notes_col, - limit=200, # Begrenzt, um Performance zu gewährleisten - with_payload=True - ) - if res_all: - for r in res_all: - if r.payload: - note_title = r.payload.get("title", "") - note_title_norm = normalize_title(note_title) - if note_title_norm and possible_title_norm: - if (note_title_norm == possible_title_norm or - note_title_norm.startswith(possible_title_norm) or - possible_title_norm.startswith(note_title_norm)): - self._note_cache[r.payload['note_id']] = r.payload - return r.payload + # Lade alle Notes (einmalig, dann Cache) - OPTIMIERT mit paginierter Suche + if not hasattr(self, '_all_notes_cache') or not self._all_notes_cache: + self._all_notes_cache = {} # note_id -> payload + self._all_notes_title_map = {} # normalisierter Titel -> Liste von Notes + + # Paginierte Suche, um ALLE Notes zu laden + next_offset = None + total_loaded = 0 + while True: + res_all, next_offset = self.client.scroll( + collection_name=self.notes_col, + limit=1000, + offset=next_offset, + with_payload=True + ) + for r in res_all: + if r.payload: + note_id = r.payload.get('note_id') + note_title = r.payload.get("title", "") + if note_id and note_title: + self._all_notes_cache[note_id] = r.payload + # Normalisiere Titel und speichere Mapping + note_title_norm_cached = normalize_title(note_title) + if note_title_norm_cached: + if note_title_norm_cached not in self._all_notes_title_map: + self._all_notes_title_map[note_title_norm_cached] = [] + self._all_notes_title_map[note_title_norm_cached].append(r.payload) + total_loaded += 1 + if next_offset is None: + break + + # Suche im Cache (verwende _all_notes_title_map statt _all_notes_cache) + if hasattr(self, '_all_notes_title_map') and possible_title_norm in self._all_notes_title_map: + matches = self._all_notes_title_map[possible_title_norm] + if matches: + # Nimm das erste Match + result = matches[0] + self._note_cache[result['note_id']] = result + self._ref_resolution_cache[ref_str] = result + return result + + # Fallback: Durchsuche alle normalisierten Titel + if hasattr(self, '_all_notes_title_map'): + for norm_title, notes_list in self._all_notes_title_map.items(): + if isinstance(notes_list, list) and notes_list: + if (norm_title == possible_title_norm or + norm_title.startswith(possible_title_norm) or + possible_title_norm.startswith(norm_title)): + result = notes_list[0] + self._note_cache[result['note_id']] = result + self._ref_resolution_cache[ref_str] = result + return result except Exception: pass