migrateCollection
Copies a collection into another one and moves an alias onto it, without taking reads down.
Every team running a vector database eventually changes embedding model, and a collection's vector size is fixed at creation. The way out is known and nobody enjoys writing it: create a second collection with the new configuration, re-embed and copy the points, verify, swap an alias so readers move in one step, keep the old collection until you are sure. Kdrant already had every call this needs; what it did not have was the procedure, so each user rebuilt it, and the parts that are easy to get wrong were the parts they got wrong.
val report = qdrant.migrateCollection(
from = "docs-768",
to = "docs-1536",
alias = "docs",
createTarget = { vector { size = 1536; distance = Distance.COSINE } },
checkpoints = FileMigrationCheckpointStore(Path("/var/lib/migrations")),
) { record ->
PointStruct(record.id, VectorData.Dense(embed(record.payload!!["text"]!!.text())), record.payload)
}Readers go through the alias throughout and never see a gap: the alias points at the old collection until the last moment and at the new one after it, and Qdrant applies the change in one atomic request.
Two properties, and why each is here
It resumes. A copy of ten million points will be interrupted, and starting over is not an answer. After every batch Qdrant acknowledges, the id reached goes to checkpoints; the next run reads it and continues. Nothing is dropped, because the checkpoint is written after the write rather than before it, and nothing is duplicated, because a point is written under the id it already had.
The alias swap is gated on a verification that ran. Counts have to match, and a sample of queries has to return the same neighbours from both collections above a stated recall. If the check fails the alias does not move and MigrationVerificationFailed is thrown with the numbers in it.
What it does not do
It does not stop writes to the source. A point written to the source behind the cursor — with an id the copy has already passed — is not picked up, and the count check will notice. Migrate from a source that is being read rather than written, or run the migration twice: the second pass over an already-copied collection is cheap and catches whatever the first one raced.
Parameters
the collection to read.
the collection to write. Created by createTarget when that is given.
moved onto to once the verification passes. null performs the copy and the check and leaves the alias alone, which is how you rehearse one.
the configuration for to. null expects it to exist already.
points per read page and per upsert.
where progress is remembered. The default survives nothing; see MigrationCheckpointStore.
the key progress is stored under. The default is derived from the two collection names, which is right unless you run the same pair twice for different reasons.
how hard to check before moving the alias.
what to write for each source point. The default copies it unchanged, which is a re-shard or a config change rather than a re-embedding; return null to drop a point.
Throws
if the copy does not check out. The alias is untouched.