# Trouve-KA — migration de l'index de recherche vers le mapping kNN # Author: Simon-Pierre Boucher # Contact: contact@spboucher.ai """Migre l'index vers le mapping v2 (champ embedding kNN). `index.knn` est un réglage statique OpenSearch : impossible de l'activer sur un index existant. Procédure (idempotente) : 1. crawl en pause (les workers respectent trouveka:crawler:paused); 2. création de -v2 avec le mapping kNN; 3. _reindex serveur → -v2; 4. suppression de l'ancien index, alias → -v2 (le code continue de parler à sans changement); 5. reprise du crawl. Usage : python -m scripts.migrate-search-index.migrate (ou via docker compose run) """ import asyncio from opensearchpy import AsyncOpenSearch from trouveka.config import get_settings from trouveka.queue import Coordination from trouveka.search_core import INDEX_SETTINGS async def main() -> None: s = get_settings() client = AsyncOpenSearch(hosts=[s.search_url], timeout=600) coord = Coordination(s.redis_url) index, v2 = s.search_index, f"{s.search_index}-v2" try: if await client.indices.exists_alias(name=index): print(f"✓ {index} est déjà un alias — migration déjà faite") return if not await client.indices.exists(index=index): await client.indices.create(index=v2, body=INDEX_SETTINGS) await client.indices.put_alias(index=v2, name=index) print(f"✓ installation neuve : {v2} créé avec alias {index}") return print("→ pause du crawl") await coord.pause_crawler() await asyncio.sleep(5) # laisser les indexations en vol se terminer if not await client.indices.exists(index=v2): await client.indices.create(index=v2, body=INDEX_SETTINGS) print(f"→ {v2} créé") print(f"→ _reindex {index} → {v2} (serveur, peut prendre quelques minutes)") result = await client.reindex( body={"source": {"index": index}, "dest": {"index": v2}}, params={"wait_for_completion": "true", "refresh": "true"}, ) print(f"→ réindexé : {result.get('total')} docs, {result.get('took')} ms") old_count = (await client.count(index=index))["count"] new_count = (await client.count(index=v2))["count"] print(f"→ comptes : ancien={old_count} nouveau={new_count}") if new_count < old_count * 0.99: raise RuntimeError("perte de documents détectée, on ne bascule pas") await client.indices.delete(index=index) await client.indices.put_alias(index=v2, name=index) print(f"✓ alias {index} → {v2} en place") finally: await coord.resume_crawler() print("→ crawl repris") await coord.close() await client.close() if __name__ == "__main__": asyncio.run(main())