mindnet/app/core/ingestion/ingestion_db.py

116 lines
4.4 KiB
Python
Raw Normal View History

"""
FILE: app/core/ingestion/ingestion_db.py
DESCRIPTION: Datenbank-Schnittstelle für Note-Metadaten und Artefakt-Prüfung.
WP-14: Umstellung auf zentrale database-Infrastruktur.
WP-24c: Integration der Authority-Prüfung für Point-IDs.
Ermöglicht dem Prozessor die Unterscheidung zwischen
manueller Nutzer-Autorität und virtuellen Symmetrien.
VERSION: 2.2.0 (WP-24c: Authority Lookup Integration)
STATUS: Active
"""
import logging
from typing import Optional, Tuple, List
from qdrant_client import QdrantClient
from qdrant_client.http import models as rest
# Import der modularisierten Namen-Logik zur Sicherstellung der Konsistenz
2025-12-27 19:47:23 +01:00
from app.core.database import collection_names
logger = logging.getLogger(__name__)
def fetch_note_payload(client: QdrantClient, prefix: str, note_id: str) -> Optional[dict]:
"""
Holt die Metadaten einer Note aus Qdrant via Scroll-API.
Wird primär für die Change-Detection (Hash-Vergleich) genutzt.
"""
2025-12-27 19:47:23 +01:00
notes_col, _, _ = collection_names(prefix)
try:
f = rest.Filter(must=[
rest.FieldCondition(key="note_id", match=rest.MatchValue(value=note_id))
])
pts, _ = client.scroll(
collection_name=notes_col,
scroll_filter=f,
limit=1,
with_payload=True
)
return pts[0].payload if pts else None
except Exception as e:
logger.debug(f"Note {note_id} not found or error during fetch: {e}")
return None
def artifacts_missing(client: QdrantClient, prefix: str, note_id: str) -> Tuple[bool, bool]:
"""
Prüft Qdrant aktiv auf vorhandene Chunks und Edges für eine Note.
Gibt (chunks_missing, edges_missing) als Boolean-Tupel zurück.
"""
2025-12-27 19:47:23 +01:00
_, chunks_col, edges_col = collection_names(prefix)
try:
# Filter für die note_id Suche
f = rest.Filter(must=[
rest.FieldCondition(key="note_id", match=rest.MatchValue(value=note_id))
])
2025-12-27 19:47:23 +01:00
c_pts, _ = client.scroll(collection_name=chunks_col, scroll_filter=f, limit=1)
e_pts, _ = client.scroll(collection_name=edges_col, scroll_filter=f, limit=1)
return (not bool(c_pts)), (not bool(e_pts))
except Exception as e:
logger.error(f"Error checking artifacts for {note_id}: {e}")
return True, True
def is_explicit_edge_present(client: QdrantClient, prefix: str, edge_id: str) -> bool:
"""
WP-24c: Prüft via Point-ID, ob bereits eine explizite Kante existiert.
Wird vom IngestionProcessor in Phase 2 genutzt, um das Überschreiben
von manuellem Wissen durch virtuelle Symmetrie-Kanten zu verhindern.
Args:
edge_id: Die deterministisch berechnete UUID der Kante.
Returns:
True, wenn eine physische Kante (virtual=False) existiert.
"""
if not edge_id:
return False
_, _, edges_col = collection_names(prefix)
try:
# retrieve ist die effizienteste Methode für den Zugriff via ID
res = client.retrieve(
collection_name=edges_col,
ids=[edge_id],
with_payload=True
)
if res and len(res) > 0:
# Wir prüfen das 'virtual' Flag im Payload
is_virtual = res[0].payload.get("virtual", False)
if not is_virtual:
return True # Es ist eine explizite Nutzer-Kante
return False
except Exception as e:
logger.debug(f"Authority check failed for ID {edge_id}: {e}")
return False
def purge_artifacts(client: QdrantClient, prefix: str, note_id: str):
"""
Löscht verwaiste Chunks und Edges einer Note vor einem Re-Import.
Stellt sicher, dass keine Duplikate bei Inhaltsänderungen entstehen.
"""
2025-12-27 19:47:23 +01:00
_, chunks_col, edges_col = collection_names(prefix)
try:
f = rest.Filter(must=[
rest.FieldCondition(key="note_id", match=rest.MatchValue(value=note_id))
])
# Chunks löschen
client.delete(
collection_name=chunks_col,
points_selector=rest.FilterSelector(filter=f)
)
# Edges löschen
client.delete(
collection_name=edges_col,
points_selector=rest.FilterSelector(filter=f)
)
logger.info(f"🧹 [PURGE] Local artifacts for '{note_id}' cleared.")
except Exception as e:
logger.error(f"❌ [PURGE ERROR] Failed to clear artifacts for {note_id}: {e}")