GaetanoParente commited on
Commit
d2bdec9
·
verified ·
1 Parent(s): 4c8ac6c

Update src/graph/graph_loader.py

Browse files
Files changed (1) hide show
  1. src/graph/graph_loader.py +20 -13
src/graph/graph_loader.py CHANGED
@@ -7,26 +7,26 @@ from dotenv import load_dotenv
7
  load_dotenv()
8
 
9
  class KnowledgeGraphPersister:
10
-
11
  def __init__(self):
12
  """
13
- Inizializza il driver Neo4j usando le variabili d'ambiente per sicurezza.
14
  """
15
- uri = os.getenv("NEO4J_URI", "")
16
  user = os.getenv("NEO4J_USER", "neo4j")
17
- password = os.getenv("NEO4J_PASSWORD", "")
18
 
19
  try:
20
  self.driver = GraphDatabase.driver(uri, auth=(user, password))
21
  self.driver.verify_connectivity()
22
  print(f"✅ Connesso a Neo4j ({uri}).")
 
23
  # Creazione indici all'avvio (Fondamentale per la velocità dei MERGE)
24
  self._create_constraints()
 
25
  except Exception as e:
26
  print(f"❌ Errore critico connessione Neo4j: {e}")
27
  self.driver = None
28
 
29
-
30
  def close(self):
31
  if self.driver:
32
  self.driver.close()
@@ -43,6 +43,7 @@ class KnowledgeGraphPersister:
43
  session.run(query)
44
  print("⚡ Vincoli/Indici Neo4j verificati.")
45
  except Exception as e:
 
46
  print(f"⚠️ Warning creazione indici: {e}")
47
 
48
  def sanitize_name(self, name):
@@ -51,15 +52,25 @@ class KnowledgeGraphPersister:
51
  """
52
  if not name: return "Unknown"
53
  # Rimuove spazi extra e normalizza.
54
- # Nota: In produzione usare slugify o urllib.parse.quote per URI robusti
55
  return name.strip().replace(" ", "_").replace("'", "").replace('"', "")
56
 
57
  def sanitize_predicate(self, pred):
58
  """
59
- Pulisce il predicato per evitare Cypher Injection, dato che non può essere parametrizzato.
 
 
 
60
  """
61
- # Accetta solo caratteri alfanumerici e underscore. Upper case per convenzione Neo4j.
 
 
 
 
 
 
62
  clean = "".join(x for x in pred if x.isalnum() or x == "_")
 
 
63
  return clean.upper() if clean else "RELATED_TO"
64
 
65
  def save_triples(self, triples):
@@ -73,7 +84,6 @@ class KnowledgeGraphPersister:
73
  print(f"💾 Preparazione Batch di {len(triples)} triple...")
74
 
75
  # 1. Raggruppamento per Predicato
76
- # Struttura: { "LOCATED_IN": [ {subj:..., obj:..., ...}, ... ], "HAS_TYPE": [...] }
77
  batched_by_pred = defaultdict(list)
78
 
79
  for t in triples:
@@ -84,7 +94,7 @@ class KnowledgeGraphPersister:
84
  "subj_label": t.subject,
85
  "obj_uri": self.sanitize_name(t.object),
86
  "obj_label": t.object,
87
- "conf": float(t.confidence), # Assicura float nativo
88
  "src": t.source or "unknown"
89
  }
90
  batched_by_pred[safe_pred].append(item)
@@ -104,10 +114,7 @@ class KnowledgeGraphPersister:
104
  def _unwind_write_tx(tx, predicate, batch_data):
105
  """
106
  Usa UNWIND per inserire migliaia di righe in un colpo solo.
107
- Molto più performante su rete cloud.
108
  """
109
- # La query è dinamica SOLO sul tipo di relazione (sanitizzato prima),
110
- # tutto il resto passa come parametro lista ($batch).
111
  query = (
112
  f"UNWIND $batch AS row "
113
  f"MERGE (s:Resource {{uri: row.subj_uri}}) "
 
7
  load_dotenv()
8
 
9
  class KnowledgeGraphPersister:
 
10
  def __init__(self):
11
  """
12
+ Inizializza il driver Neo4j e crea i vincoli necessari per le performance.
13
  """
14
+ uri = os.getenv("NEO4J_URI", "neo4j+s://748d6c94.databases.neo4j.io")
15
  user = os.getenv("NEO4J_USER", "neo4j")
16
+ password = os.getenv("NEO4J_PASSWORD", "t1bT1DiXwDOGMYfX89qR20loSN8FXurB3Dfg8bPQcTI")
17
 
18
  try:
19
  self.driver = GraphDatabase.driver(uri, auth=(user, password))
20
  self.driver.verify_connectivity()
21
  print(f"✅ Connesso a Neo4j ({uri}).")
22
+
23
  # Creazione indici all'avvio (Fondamentale per la velocità dei MERGE)
24
  self._create_constraints()
25
+
26
  except Exception as e:
27
  print(f"❌ Errore critico connessione Neo4j: {e}")
28
  self.driver = None
29
 
 
30
  def close(self):
31
  if self.driver:
32
  self.driver.close()
 
43
  session.run(query)
44
  print("⚡ Vincoli/Indici Neo4j verificati.")
45
  except Exception as e:
46
+ # Spesso fallisce se l'utente non ha permessi admin o se esiste già con nome diverso
47
  print(f"⚠️ Warning creazione indici: {e}")
48
 
49
  def sanitize_name(self, name):
 
52
  """
53
  if not name: return "Unknown"
54
  # Rimuove spazi extra e normalizza.
 
55
  return name.strip().replace(" ", "_").replace("'", "").replace('"', "")
56
 
57
  def sanitize_predicate(self, pred):
58
  """
59
+ Pulisce il predicato per evitare Cypher Injection.
60
+ FIX: Gestisce meglio i separatori (:, -, spazio) sostituendoli con underscore
61
+ per evitare predicati illeggibili come XCHEHASOBJECT.
62
+ Es. xche:has_object -> XCHE_HAS_OBJECT
63
  """
64
+ if not pred: return "RELATED_TO"
65
+
66
+ # 1. Normalizzazione preliminare dei separatori comuni
67
+ # Sostituisce i due punti dei namespace e trattini con underscore
68
+ pred = pred.replace(":", "_").replace("-", "_").replace(" ", "_")
69
+
70
+ # 2. Rimozione caratteri non sicuri (mantiene solo alfanumerici e underscore)
71
  clean = "".join(x for x in pred if x.isalnum() or x == "_")
72
+
73
+ # 3. Conversione in uppercase (convenzione Neo4j per Relationships)
74
  return clean.upper() if clean else "RELATED_TO"
75
 
76
  def save_triples(self, triples):
 
84
  print(f"💾 Preparazione Batch di {len(triples)} triple...")
85
 
86
  # 1. Raggruppamento per Predicato
 
87
  batched_by_pred = defaultdict(list)
88
 
89
  for t in triples:
 
94
  "subj_label": t.subject,
95
  "obj_uri": self.sanitize_name(t.object),
96
  "obj_label": t.object,
97
+ "conf": float(t.confidence),
98
  "src": t.source or "unknown"
99
  }
100
  batched_by_pred[safe_pred].append(item)
 
114
  def _unwind_write_tx(tx, predicate, batch_data):
115
  """
116
  Usa UNWIND per inserire migliaia di righe in un colpo solo.
 
117
  """
 
 
118
  query = (
119
  f"UNWIND $batch AS row "
120
  f"MERGE (s:Resource {{uri: row.subj_uri}}) "