Spaces:
Sleeping
Sleeping
| """ | |
| Déduplication de la base ChromaDB | |
| ================================= | |
| Supprime les chunks redondants : quand une même paire (source, chunk_index) | |
| existe plusieurs fois (= fichier ingéré plusieurs fois), on garde 1 exemplaire | |
| et on supprime les autres. Lecture/écriture locale, AUCUN appel Azure. | |
| Usage : ./.venv/Scripts/python.exe dedup_store.py | |
| """ | |
| import os | |
| from pathlib import Path | |
| from collections import defaultdict | |
| os.environ.setdefault("ANONYMIZED_TELEMETRY", "False") | |
| import chromadb | |
| from chromadb.config import Settings | |
| PROJECT_ROOT = Path(__file__).parent | |
| DATA_DIR = Path("/data") if Path("/data").is_dir() else PROJECT_ROOT / "data" | |
| COLLECTION_NAME = "rag_documents" | |
| client = chromadb.PersistentClient(path=str(DATA_DIR / "chroma_db"), settings=Settings(anonymized_telemetry=False)) | |
| col = client.get_or_create_collection(name=COLLECTION_NAME, metadata={"hnsw:space": "cosine"}) | |
| total = col.count() | |
| print(f"Total chunks avant : {total}") | |
| # (source, chunk_index) -> liste d'ids | |
| pair_to_ids = defaultdict(list) | |
| off, step = 0, 2000 | |
| while off < total: | |
| got = col.get(include=["metadatas"], limit=step, offset=off) | |
| for _id, m in zip(got["ids"], got["metadatas"]): | |
| pair_to_ids[(m.get("source"), m.get("chunk_index"))].append(_id) | |
| off += step | |
| # pour chaque paire en double, on garde le 1er id, on supprime les autres | |
| to_delete = [] | |
| affected = defaultdict(int) | |
| for (src, idx), ids in pair_to_ids.items(): | |
| if len(ids) > 1: | |
| extra = ids[1:] # on garde ids[0] | |
| to_delete.extend(extra) | |
| affected[src] += len(extra) | |
| print(f"Chunks redondants à supprimer : {len(to_delete)}") | |
| print("Mémoires concernés :") | |
| for src, n in sorted(affected.items(), key=lambda x: -x[1]): | |
| print(f" -{n} {src}") | |
| if not to_delete: | |
| print("Aucun doublon. Rien à faire.") | |
| else: | |
| # suppression par lots (limite SQLite sur le nombre de variables) | |
| B = 500 | |
| for i in range(0, len(to_delete), B): | |
| col.delete(ids=to_delete[i:i + B]) | |
| print(f" supprimé {min(i + B, len(to_delete))}/{len(to_delete)}") | |
| print(f"Total chunks après : {col.count()}") | |