This commit is contained in:
nose
2025-12-23 16:36:39 -08:00
parent 16316bb3fd
commit 8bf04c6b71
25 changed files with 3165 additions and 234 deletions

View File

@@ -1392,7 +1392,12 @@ class API_folder_store:
return 0
def delete_file(self, file_path: Path) -> bool:
"""Delete a file from the database by path. Cascades to metadata, tags, notes, etc."""
"""Delete a file from the database by path.
Cascades to metadata, tags, notes, etc, and also cleans up relationship
backlinks in other files so no file retains dangling references to the
deleted hash.
"""
try:
str_path = str(file_path.resolve())
cursor = self.connection.cursor()
@@ -1405,6 +1410,67 @@ class API_folder_store:
return False
file_hash = row[0]
# Remove backlinks from other files that reference this hash.
try:
target_hash = str(file_hash or "").strip().lower()
backlinks = self.find_files_pointing_to_hash(target_hash)
by_src: Dict[str, set[str]] = {}
for b in backlinks:
src = str((b or {}).get("hash") or "").strip().lower()
rt = str((b or {}).get("type") or "").strip()
if not src or src == target_hash or not rt:
continue
by_src.setdefault(src, set()).add(rt)
for src_hash, rel_types in by_src.items():
meta = self.get_metadata(src_hash) or {}
rels = meta.get("relationships") if isinstance(meta, dict) else None
if not isinstance(rels, dict) or not rels:
continue
changed = False
for rt in rel_types:
key_to_edit = None
for k in list(rels.keys()):
if str(k).lower() == str(rt).lower():
key_to_edit = str(k)
break
if not key_to_edit:
continue
bucket = rels.get(key_to_edit)
if not isinstance(bucket, list) or not bucket:
continue
new_bucket = [h for h in bucket if str(h or "").strip().lower() != target_hash]
if len(new_bucket) == len(bucket):
continue
changed = True
if new_bucket:
rels[key_to_edit] = new_bucket
else:
try:
del rels[key_to_edit]
except Exception:
rels[key_to_edit] = []
if changed:
cursor.execute(
"""
INSERT INTO metadata (hash, relationships)
VALUES (?, ?)
ON CONFLICT(hash) DO UPDATE SET
relationships = excluded.relationships,
time_modified = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP
""",
(src_hash, json.dumps(rels if rels else {})),
)
except Exception:
# Best-effort cleanup; deletion should still proceed.
pass
# Delete the file entry (cascades to metadata, tags, notes, etc via foreign keys)
cursor.execute("DELETE FROM files WHERE file_path = ?", (str_path,))