Two checks: record counts match, and the same queries return mostly the same results on both systems. Run them before you switch, while the old database still answers.
For the second check, use stored vectors as queries, fetch the top 10 from each system, and count the shared IDs. Expect close to 10, not exactly 10. Investigate anything that mostly shares fewer than 7.
Check 1: Do the counts match?
Compare each collection's count in the source with the count after import:
| Source | Command |
|---|---|
| Chroma | collection.count() |
| TopK | topk sql "SELECT COUNT(*) FROM \"docs\"" |
A lower count almost always means the import stopped early. With TopK, rerun the import with --resume; upserts are idempotent, so running it again is safe. A higher count means records were written mid-export, so export again with writes paused.
Check 2: Do the same queries return the same results?
Take a sample of vectors already stored in the source, so you don't need an embedding model. Run each as a query on both systems and compare:
sample = chroma.get(limit=20, include=["embeddings"])
overlaps = []
for vec in sample["embeddings"]:
a = set(chroma.query(query_embeddings=[vec], n_results=10)["ids"][0])
b = {r["_id"] for r in topk_top10(vec)} # same metric, same sort
overlaps.append(len(a & b))
print(sum(overlaps) / len(overlaps), min(overlaps))
Why not expect exactly 10?
Both systems use approximate nearest neighbor indexes, which trade a little recall for speed. Two different indexes miss slightly different neighbors, so near-ties at positions 9 and 10 swap. That's normal. A consistently low overlap is not, and it usually has one of three causes:
| Symptom | Likely cause |
|---|---|
| Overlap near 0 | Wrong metric, or results sorted the wrong way |
| Low overlap on text queries | Queries embedded with a different model |
| Missing results on filtered queries | A filter mapped wrong, or a renamed field |
To judge quality rather than sameness, measure against ground truth. Which recall number actually matters explains why recall@10 alone can mislead, and how to benchmark vector search covers a fair setup.
When are you ready to switch?
When counts match and most queries share 7 or more of the top 10. Then switch writes first, because Chroma Cloud stops accepting writes on November 22, 2026, and reads after.
Let an agent run the checks
The TopK agent guide runs both checks for every collection and prints a table of counts and average overlap. Paste "Migrate my Chroma collections to TopK: docs.topk.io/migrate-chroma" into your coding agent. The human guide has the same checks as scripts. Want a second pair of eyes? Book a migration call.