Qdrant metadata v2 backfill
Use this runbook when a collection contains pre-v2 point payload metadata and the application has already been upgraded to write schema v2 metadata for new points.
The backfill only sets the top-level Qdrant payload key metadata to its schema
v2 shape. It does not delete points, recreate collections, drop old indexes,
or remove old payload fields outside metadata.
What changes
The migration transforms legacy metadata groups into the v2 public contract:
metadata.processing.source_filename→metadata.source.filename, withextensionandmime_typeinferred from the filename when possible.metadata.parsing.page_count→metadata.document.page_countas an integer.metadata.processing.page_number,metadata.processing.page_label, and the point payloadchunk_index→metadata.location.*.metadata.classification.categories/status→metadata.classification.categories/status; legacycategory_modelanderrorare intentionally not exposed in public v2 metadata.- OCR/native extraction signals from
metadata.ocr,extraction_method, andocr_used→metadata.quality.text_extraction,ocr_used, and structured warnings when OCR or mixed extraction is meaningful. - Non-empty
metadata.parsing.title/authorand source-created date fields →metadata.extracted.*; empty title/author noise is dropped. metadata.customand other arbitrary non-system JSON metadata are preserved undermetadata.custom.
Already-normalized v2 metadata is idempotent: repeated dry-runs or apply runs should report no additional updates after the first successful apply.
1. Dry-run first
Dry-run is the default and performs metadata-only scans with
with_payload=True and with_vectors=False.
uv run python scripts/backfill_qdrant_metadata_v2.py \
--qdrant-url "$QDRANT_URL" \
--collection-name "$COLLECTION_NAME"
For a smaller canary scan:
uv run python scripts/backfill_qdrant_metadata_v2.py --limit 100
Dry-run output from the 2026-06-11 guide run:
Metadata v2 backfill mode: dry-run
Collection: documents
Checked: 35
Would update: 0
Updated: 0
Skipped: 35
Errors: 0
No Qdrant writes were performed. Re-run with --apply to update metadata payloads.
Expected summary fields:
checked: points read from Qdrant.would_update: points whosemetadatawould change.updated: points written; always0in dry-run mode.skipped: points with no metadata or already-normalized metadata.errors: points or update batches that failed transformation/write handling.
2. Apply explicitly
Only run apply after the dry-run count is expected and the application version that reads/writes metadata v2 is deployed.
uv run python scripts/backfill_qdrant_metadata_v2.py --apply
The script writes batches with Qdrant SetPayloadOperation, setting only the
metadata key for selected point IDs. By default it waits for Qdrant write
completion. Use --no-wait only during a controlled maintenance window where
deferred write acknowledgement is acceptable.
3. Verify after apply
Run the dry-run again:
uv run python scripts/backfill_qdrant_metadata_v2.py
Success criteria:
errorsis0.would_updateis0or only reflects new legacy writes from an older worker still running during the migration.- Search and document-list endpoints continue to return results with v2 metadata
paths such as
metadata.source.filename,metadata.location.page_number, andmetadata.quality.ocr_used.
Qdrant index implications
Issue #71 moved filter paths to v2 payload keys. This backfill updates payload values so those v2 indexes can match older points. It does not rebuild or delete indexes.
If the collection was created before v2 indexes existed, run the collection setup or verification path that creates the current payload indexes before relying on filters over v2 fields. Keeping old v1 indexes temporarily is safe; they can be removed later only after:
- all workers and API instances write v2 metadata,
- this backfill reports no pending updates,
- no dashboards, scripts, or saved filters query old paths such as
page_number,ocr_used, orextraction_method, and - a rollback plan exists for restoring old indexes if an external consumer was missed.
Do not combine old path removal with this backfill. Treat index cleanup as a separate maintenance task so data transformation and query/index cleanup can be verified independently.