This commit is contained in:
parent
515248d438
commit
98f21323fb
|
|
@ -199,8 +199,9 @@ class GraphExplorerService:
|
||||||
# Case B: Edge zeigt direkt auf unsere Note ID
|
# Case B: Edge zeigt direkt auf unsere Note ID
|
||||||
shoulds.append(models.FieldCondition(key="target_id", match=models.MatchAny(any=note_ids)))
|
shoulds.append(models.FieldCondition(key="target_id", match=models.MatchAny(any=note_ids)))
|
||||||
|
|
||||||
# Case C: Edge zeigt auf unseren Titel (Wikilinks) - auch wenn note_title None ist, versuchen wir es mit den Titeln der Notes
|
# Case C: Edge zeigt auf unseren Titel (Wikilinks)
|
||||||
# WICHTIG: Wir müssen auch nach "Titel#Abschnitt" Format suchen!
|
# WICHTIG: target_id ist der vollständige Wikilink-Text ohne [[]], z.B. "Meine Prinzipien 2025#P3 – Disziplin"
|
||||||
|
# Das kann sein: "Titel" oder "Titel#Abschnitt" oder "Titel#Abschnitt (Details)"
|
||||||
note_titles_to_search = []
|
note_titles_to_search = []
|
||||||
if note_title:
|
if note_title:
|
||||||
note_titles_to_search.append(note_title)
|
note_titles_to_search.append(note_title)
|
||||||
|
|
@ -211,13 +212,13 @@ class GraphExplorerService:
|
||||||
if note and note.get("title"):
|
if note and note.get("title"):
|
||||||
note_titles_to_search.append(note.get("title"))
|
note_titles_to_search.append(note.get("title"))
|
||||||
|
|
||||||
# Für jeden Titel: Suche nach exaktem Match UND nach "Titel#*" Varianten
|
# Für jeden Titel: Suche nach exaktem Match
|
||||||
# Da Qdrant keine Wildcard-Suche hat, müssen wir explizit nach bekannten Varianten suchen
|
# WICHTIG: target_id kann "Titel" oder "Titel#Abschnitt" oder "Titel#Abschnitt (Details)" sein
|
||||||
# ABER: Wir wissen nicht, welche Abschnitte existieren, also müssen wir alle möglichen target_ids prüfen
|
# Wir suchen nach exaktem Match für "Titel"
|
||||||
for title in note_titles_to_search:
|
for title in note_titles_to_search:
|
||||||
# Exakte Übereinstimmung
|
# Exakte Übereinstimmung (für target_id = "Titel")
|
||||||
shoulds.append(models.FieldCondition(key="target_id", match=models.MatchValue(value=title)))
|
shoulds.append(models.FieldCondition(key="target_id", match=models.MatchValue(value=title)))
|
||||||
# WICHTIG: "Titel#Abschnitt" Varianten werden in Case D gefunden (clientseitige Filterung)
|
# WICHTIG: "Titel#*" Varianten werden in Case D gefunden (clientseitige Filterung)
|
||||||
|
|
||||||
if shoulds:
|
if shoulds:
|
||||||
in_filter = models.Filter(
|
in_filter = models.Filter(
|
||||||
|
|
@ -229,7 +230,7 @@ class GraphExplorerService:
|
||||||
results.extend(res_in)
|
results.extend(res_in)
|
||||||
|
|
||||||
# Case D: ZUSÄTZLICHE Suche für "Titel#Abschnitt" Format (nur für INCOMING edges)
|
# Case D: ZUSÄTZLICHE Suche für "Titel#Abschnitt" Format (nur für INCOMING edges)
|
||||||
# PROBLEM: Wikilinks wie [[Titel#Abschnitt]] werden als target_id="Titel#Abschnitt" gespeichert
|
# PROBLEM: target_id ist der vollständige Wikilink-Text, z.B. "Meine Prinzipien 2025#P3 – Disziplin"
|
||||||
# Da Qdrant keine Wildcard-Suche hat, müssen wir breiter suchen und clientseitig filtern
|
# Da Qdrant keine Wildcard-Suche hat, müssen wir breiter suchen und clientseitig filtern
|
||||||
# WICHTIG: Diese Suche ist nur für eingehende Kanten relevant
|
# WICHTIG: Diese Suche ist nur für eingehende Kanten relevant
|
||||||
# Für ausgehende Kanten werden alle über note_id gefunden, unabhängig vom target_id Format
|
# Für ausgehende Kanten werden alle über note_id gefunden, unabhängig vom target_id Format
|
||||||
|
|
@ -254,12 +255,12 @@ class GraphExplorerService:
|
||||||
for edge in res_extended:
|
for edge in res_extended:
|
||||||
tgt_id = edge.payload.get("target_id", "")
|
tgt_id = edge.payload.get("target_id", "")
|
||||||
if tgt_id and edge.id not in existing_edge_ids:
|
if tgt_id and edge.id not in existing_edge_ids:
|
||||||
# Prüfe, ob target_id mit einem unserer Titel beginnt (für "Titel#Abschnitt" Format)
|
# Prüfe, ob target_id mit einem unserer Titel beginnt
|
||||||
# ODER exakt dem Titel entspricht
|
# target_id kann sein: "Titel", "Titel#Abschnitt", "Titel#Abschnitt (Details)"
|
||||||
for title in note_titles_to_search:
|
for title in note_titles_to_search:
|
||||||
# Exakte Übereinstimmung oder beginnt mit "Titel#"
|
# Exakte Übereinstimmung ODER beginnt mit "Titel#"
|
||||||
# WICHTIG: startswith prüft auch exakte Übereinstimmung (title == tgt_id)
|
# WICHTIG: startswith mit "#" findet alle Varianten wie "Titel#P3 – Disziplin"
|
||||||
if tgt_id.startswith(title + "#") or tgt_id == title:
|
if tgt_id == title or tgt_id.startswith(title + "#"):
|
||||||
results.append(edge)
|
results.append(edge)
|
||||||
existing_edge_ids.add(edge.id)
|
existing_edge_ids.add(edge.id)
|
||||||
break # Nur einmal hinzufügen, auch wenn mehrere Titel passen
|
break # Nur einmal hinzufügen, auch wenn mehrere Titel passen
|
||||||
|
|
@ -339,10 +340,18 @@ class GraphExplorerService:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _resolve_note_from_ref(self, ref_str):
|
def _resolve_note_from_ref(self, ref_str):
|
||||||
"""Löst eine ID (Chunk, Note oder Titel) zu einer Note Payload auf."""
|
"""
|
||||||
|
Löst eine Referenz zu einer Note Payload auf.
|
||||||
|
|
||||||
|
ref_str kann sein:
|
||||||
|
- Note-ID: "20250101-meine-note"
|
||||||
|
- Chunk-ID: "20250101-meine-note#c01"
|
||||||
|
- Titel: "Meine Prinzipien 2025"
|
||||||
|
- Wikilink-Text: "Meine Prinzipien 2025#P3 – Disziplin (Selbstführung & Familie)"
|
||||||
|
"""
|
||||||
if not ref_str: return None
|
if not ref_str: return None
|
||||||
|
|
||||||
# Fall A: Chunk ID oder Titel#Abschnitt (enthält #)
|
# Fall A: Enthält # (kann Chunk-ID oder Wikilink mit Abschnitt sein)
|
||||||
if "#" in ref_str:
|
if "#" in ref_str:
|
||||||
try:
|
try:
|
||||||
# Versuch 1: Chunk ID direkt (Format: note_id#c01)
|
# Versuch 1: Chunk ID direkt (Format: note_id#c01)
|
||||||
|
|
@ -356,24 +365,41 @@ class GraphExplorerService:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Versuch 2: NoteID#Section (Hash abtrennen und als Note-ID versuchen)
|
# Versuch 2: NoteID#Section (Hash abtrennen und als Note-ID versuchen)
|
||||||
|
# z.B. "20250101-meine-note#Abschnitt" -> "20250101-meine-note"
|
||||||
possible_note_id = ref_str.split("#")[0]
|
possible_note_id = ref_str.split("#")[0]
|
||||||
note = self._fetch_note_cached(possible_note_id)
|
note = self._fetch_note_cached(possible_note_id)
|
||||||
if note: return note
|
if note: return note
|
||||||
|
|
||||||
# Versuch 3: Titel#Abschnitt (Hash abtrennen und als Titel suchen)
|
# Versuch 3: Wikilink-Text mit Abschnitt (z.B. "Meine Prinzipien 2025#P3 – Disziplin")
|
||||||
# Dies ist wichtig für Wikilinks im Format [[Titel#Abschnitt]]
|
# WICHTIG: target_id ist der vollständige Wikilink-Text, wir müssen den Titel-Teil extrahieren
|
||||||
possible_title = ref_str.split("#")[0]
|
# Der Teil vor dem ersten "#" ist der Titel
|
||||||
try:
|
possible_title = ref_str.split("#")[0].strip()
|
||||||
res, _ = self.client.scroll(
|
if possible_title:
|
||||||
collection_name=self.notes_col,
|
try:
|
||||||
scroll_filter=models.Filter(must=[models.FieldCondition(key="title", match=models.MatchValue(value=possible_title))]),
|
# Suche nach exaktem Titel-Match
|
||||||
limit=1, with_payload=True
|
res, _ = self.client.scroll(
|
||||||
)
|
collection_name=self.notes_col,
|
||||||
if res and res[0].payload:
|
scroll_filter=models.Filter(must=[models.FieldCondition(key="title", match=models.MatchValue(value=possible_title))]),
|
||||||
self._note_cache[res[0].payload['note_id']] = res[0].payload
|
limit=1, with_payload=True
|
||||||
return res[0].payload
|
)
|
||||||
except Exception:
|
if res and res[0].payload:
|
||||||
pass
|
self._note_cache[res[0].payload['note_id']] = res[0].payload
|
||||||
|
return res[0].payload
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: Text-Suche für Fuzzy-Matching
|
||||||
|
try:
|
||||||
|
res, _ = self.client.scroll(
|
||||||
|
collection_name=self.notes_col,
|
||||||
|
scroll_filter=models.Filter(must=[models.FieldCondition(key="title", match=models.MatchText(text=possible_title))]),
|
||||||
|
limit=1, with_payload=True
|
||||||
|
)
|
||||||
|
if res and res[0].payload:
|
||||||
|
self._note_cache[res[0].payload['note_id']] = res[0].payload
|
||||||
|
return res[0].payload
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Fall B: Note ID direkt
|
# Fall B: Note ID direkt
|
||||||
note = self._fetch_note_cached(ref_str)
|
note = self._fetch_note_cached(ref_str)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user