bug fix
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 3s

This commit is contained in:
Lars 2025-12-28 11:49:41 +01:00
parent 3fe8463a03
commit 53058d1504
2 changed files with 78 additions and 35 deletions

View File

@ -311,27 +311,36 @@ class GraphExplorerService:
# ZUSÄTZLICH: Fuzzy-Matching für ähnliche Titel # ZUSÄTZLICH: Fuzzy-Matching für ähnliche Titel
# PROBLEM: Wikilinks können andere Titel verwenden als der gespeicherte Note-Titel # PROBLEM: Wikilinks können andere Titel verwenden als der gespeicherte Note-Titel
# Beispiel: Note-Titel = "Persönliches Leitbild (20252029)", aber Wikilink = "Mein Persönliches Leitbild 2025" # Beispiel: Note-Titel = "Persönliches Leitbild (20252029)", aber Wikilink = "Mein Persönliches Leitbild 2025"
# Strategie: Prüfe, ob target_id mit dem Kern-Titel beginnt (ohne Jahreszahl/Varianten) # Strategie: Normalisiere beide Titel und prüfe, ob sie ähnlich sind
if not any(tgt_id == t or tgt_id.startswith(t + "#") for t in note_titles_to_search): if not any(tgt_id == t or tgt_id.startswith(t + "#") for t in note_titles_to_search):
# Extrahiere Kern-Titel (ohne Jahreszahl, ohne Klammern) # Normalisiere target_id (entferne #Abschnitt)
tgt_base = tgt_id.split("#")[0].strip()
# Normalisiere jeden Titel und prüfe auf Ähnlichkeit
for title in note_titles_to_search: for title in note_titles_to_search:
# Entferne Jahreszahlen und Klammern für Vergleich # Normalisiere: Entferne Klammern, Jahreszahlen, Präfixe
core_title = re.sub(r'\s*\([^)]*\)', '', title) # Entferne (20252029) def normalize_title(t):
core_title = re.sub(r'\s+\d{4}', '', core_title) # Entferne Jahreszahlen if not t:
core_title = core_title.strip() return ""
# Entferne Klammern und deren Inhalt (z.B. "(20252029)")
t = re.sub(r'\s*\([^)]*\)', '', t)
# Entferne Jahreszahlen (4-stellig, mit oder ohne Bindestrich/En-Dash)
# Beispiele: "2025", "20252029", "2025-2029"
t = re.sub(r'\s*\d{4}[\s\-]*\d{0,4}', '', t)
# Entferne "Mein/Meine" Präfixe
t = re.sub(r'^(Mein|Meine)\s+', '', t, flags=re.IGNORECASE)
# Normalisiere Whitespace
t = re.sub(r'\s+', ' ', t).strip()
return t.lower() # Case-insensitive Vergleich
# Entferne auch "Mein/Meine" Präfixe für Vergleich title_norm = normalize_title(title)
core_title_clean = re.sub(r'^(Mein|Meine)\s+', '', core_title, flags=re.IGNORECASE).strip() tgt_norm = normalize_title(tgt_base)
# Prüfe, ob target_id mit Kern-Titel beginnt # Prüfe auf Ähnlichkeit: Entweder exakt gleich oder einer beginnt mit dem anderen
tgt_core = re.sub(r'\s*\([^)]*\)', '', tgt_id.split("#")[0]) # Entferne Klammern aus target_id if title_norm and tgt_norm and len(title_norm) > 5:
tgt_core = re.sub(r'\s+\d{4}', '', tgt_core).strip() if (title_norm == tgt_norm or
tgt_core_clean = re.sub(r'^(Mein|Meine)\s+', '', tgt_core, flags=re.IGNORECASE).strip() title_norm.startswith(tgt_norm) or
tgt_norm.startswith(title_norm)):
# Vergleich: Wenn Kern-Titel ähnlich ist, akzeptiere
if (core_title_clean and tgt_core_clean and
(tgt_core_clean.startswith(core_title_clean) or core_title_clean.startswith(tgt_core_clean)) and
len(core_title_clean) > 5): # Mindestlänge, um False Positives zu vermeiden
results.append(edge) results.append(edge)
existing_edge_ids.add(edge.id) existing_edge_ids.add(edge.id)
matched_count += 1 matched_count += 1

View File

@ -76,9 +76,10 @@ def find_edges_for_note(note_id: str, prefix: str = "mindnet"):
else: else:
print() print()
# 4. Eingehende Kanten - Titel#Abschnitt Varianten # 4. Eingehende Kanten - Titel#Abschnitt Varianten mit Fuzzy-Matching
print("🔍 EINGEHENDE KANTEN (target_id beginnt mit 'Titel#'):") print("🔍 EINGEHENDE KANTEN (target_id beginnt mit 'Titel#' + Fuzzy-Matching):")
if note_title: if note_title:
import re
# Lade alle Kanten und filtere clientseitig (wie Case D in ui_graph_service.py) # Lade alle Kanten und filtere clientseitig (wie Case D in ui_graph_service.py)
all_filter = rest.Filter( all_filter = rest.Filter(
must=[rest.FieldCondition(key="kind", match=rest.MatchExcept(**{"except": ["prev", "next", "belongs_to"]}))] must=[rest.FieldCondition(key="kind", match=rest.MatchExcept(**{"except": ["prev", "next", "belongs_to"]}))]
@ -86,17 +87,47 @@ def find_edges_for_note(note_id: str, prefix: str = "mindnet"):
all_edges, _ = client.scroll(edges_col, scroll_filter=all_filter, limit=10000, with_payload=True) all_edges, _ = client.scroll(edges_col, scroll_filter=all_filter, limit=10000, with_payload=True)
print(f" Gesamt geladen: {len(all_edges)} Kanten aus der Datenbank") print(f" Gesamt geladen: {len(all_edges)} Kanten aus der Datenbank")
# Clientseitige Filterung # Normalisierungs-Funktion (wie in ui_graph_service.py)
matched = [] def normalize_title(t):
if not t:
return ""
t = re.sub(r'\s*\([^)]*\)', '', t)
t = re.sub(r'\s*\d{4}[\s\-]*\d{0,4}', '', t)
t = re.sub(r'^(Mein|Meine)\s+', '', t, flags=re.IGNORECASE)
t = re.sub(r'\s+', ' ', t).strip()
return t.lower()
note_title_norm = normalize_title(note_title)
print(f" Normalisierter Note-Titel: '{note_title_norm}'")
# Clientseitige Filterung: Exakte Matches
matched_exact = []
for e in all_edges: for e in all_edges:
tgt_id = e.payload.get("target_id", "") tgt_id = e.payload.get("target_id", "")
if tgt_id and (tgt_id == note_title or tgt_id.startswith(note_title + "#")): if tgt_id and (tgt_id == note_title or tgt_id.startswith(note_title + "#")):
matched.append(e) matched_exact.append(e)
print(f" Gefunden: {len(matched)} Kanten (mit Titel#Abschnitt Varianten)") # Clientseitige Filterung: Fuzzy-Matches
matched_fuzzy = []
for e in all_edges:
tgt_id = e.payload.get("target_id", "")
if not tgt_id or e in matched_exact:
continue
tgt_base = tgt_id.split("#")[0].strip()
tgt_norm = normalize_title(tgt_base)
if tgt_norm and note_title_norm and len(note_title_norm) > 5:
if (tgt_norm == note_title_norm or
tgt_norm.startswith(note_title_norm) or
note_title_norm.startswith(tgt_norm)):
matched_fuzzy.append((e, tgt_norm))
matched = matched_exact + [e for e, _ in matched_fuzzy]
print(f" Gefunden: {len(matched_exact)} exakte Matches, {len(matched_fuzzy)} Fuzzy-Matches")
print(f" Gesamt: {len(matched)} Kanten")
for i, e in enumerate(matched[:10], 1): for i, e in enumerate(matched[:10], 1):
pl = e.payload pl = e.payload
print(f" {i}. {pl.get('kind')}: {pl.get('source_id')} -> {pl.get('target_id')}") match_type = "EXAKT" if e in matched_exact else "FUZZY"
print(f" {i}. [{match_type}] {pl.get('kind')}: {pl.get('source_id')} -> {pl.get('target_id')}")
if len(matched) > 10: if len(matched) > 10:
print(f" ... und {len(matched) - 10} weitere\n") print(f" ... und {len(matched) - 10} weitere\n")
else: else:
@ -105,14 +136,17 @@ def find_edges_for_note(note_id: str, prefix: str = "mindnet"):
# Zeige auch einige Beispiele von target_ids, die NICHT matchen # Zeige auch einige Beispiele von target_ids, die NICHT matchen
print(" 🔍 DEBUG: Beispiel target_ids die NICHT matchen (erste 10):") print(" 🔍 DEBUG: Beispiel target_ids die NICHT matchen (erste 10):")
non_matched = [] non_matched = []
for e in all_edges[:100]: # Nur erste 100 prüfen for e in all_edges[:200]: # Erste 200 prüfen
tgt_id = e.payload.get("target_id", "") tgt_id = e.payload.get("target_id", "")
if tgt_id and tgt_id != note_title and not tgt_id.startswith(note_title + "#"): if not tgt_id or e in matched:
non_matched.append(tgt_id) continue
tgt_base = tgt_id.split("#")[0].strip()
tgt_norm = normalize_title(tgt_base)
non_matched.append((tgt_id, tgt_norm))
if len(non_matched) >= 10: if len(non_matched) >= 10:
break break
for i, tgt in enumerate(non_matched, 1): for i, (tgt, tgt_norm) in enumerate(non_matched, 1):
print(f" {i}. '{tgt}'") print(f" {i}. '{tgt}' (normalisiert: '{tgt_norm}')")
print() print()
# 5. Zeige Beispiel target_ids aus der Datenbank # 5. Zeige Beispiel target_ids aus der Datenbank