Upsert by id
Use collection.upsert() when re-ingesting. If the id exists, the row is updated; if not, it is inserted. This is the difference between an idempotent ingest pipeline and one that explodes the second time it runs.
Soft-delete by tombstone, hard-delete by id
For audit-friendly systems, mark deleted chunks with metadata {'deleted': True} and exclude them at query time. For production cleanup, collection.delete(ids=[...]) physically removes them.
Re-embedding strategy
Switching embedding models requires rebuilding every vector. Two patterns:
- Side-by-side collection. Build
docs-bge-m3-v2in parallel withdocs-bge-m3-v1. Switch the read pointer when ready. Delete v1 a week later. - In-place rebuild. Iterate ids, re-embed, upsert. Acceptable on small collections; risky on large because you lose query consistency mid-rebuild.
Always favor side-by-side. The rollback path is just "point back to v1".