Skip to content

Python: key Qdrant records by storage name when serializing and deserializing - #14762

Open
MOHAMMED WASIM KHAN (wasim-builds) wants to merge 1 commit into
microsoft:mainfrom
wasim-builds:fix/qdrant-storage-name-record-keys
Open

MOHAMMED WASIM KHAN (wasim-builds) wants to merge 1 commit into
microsoft:mainfrom
wasim-builds:fix/qdrant-storage-name-record-keys

Conversation

@wasim-builds

Copy link
Copy Markdown
Contributor

Motivation and Context

Closes #14754.

Dict and dataclass records reach the Qdrant serializer keyed by storage name and pydantic models keyed by field name. The serializer popped the key and vector fields by declared name, so dict and dataclass records with a storage_name on the key or vector field failed to upsert with a KeyError. Pydantic payloads were stored under the declared names, so filters on storage names did not match them. The deserializer returned the key and an unnamed vector under the declared names, which the data model step does not read for dict and dataclass records.

Description

  • Before building the PointStruct, _serialize_dicts_to_store_models renames declared names to storage names in the record when the storage name is not already present. Then it pops the key and vectors by storage name.
  • _deserialize_store_models_to_dicts returns the key and an unnamed vector under their storage names so the data model step can read them for dict, dataclass, and pydantic models.
  • Added comprehensive unit tests in tests/unit/connectors/memory/test_qdrant_local.py covering dict, dataclass, and pydantic models across named and unnamed vector configurations.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the SK Contribution Guidelines
  • All unit tests pass locally (33/33 tests passing)
  • Ruff check and format pass cleanly

…ializing (microsoft#14754)

Dict and dataclass records reach the Qdrant serializer keyed by storage name and pydantic models keyed by field name. The serializer popped the key and vector fields by declared name, so dict and dataclass records with a storage_name on the key or vector field failed to upsert with a KeyError. Pydantic payloads were stored under the declared names, so filters on storage names did not match them. The deserializer returned the key and an unnamed vector under the declared names, which the data model step does not read for dict and dataclass records.

Closes microsoft#14754

Co-authored-by: VANDRANKI <[email protected]>
Copilot AI balanced review requested due to automatic review settings November 9, 2026 16:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks correct. Here's what I checked.

  • Serialize. _storage_keyed (qdrant.py:443) copies the record, then renames declared names to storage names only when the storage name isn't already a key. After that the key is popped by key_field_storage_name and the vectors by storage name (qdrant.py:461-472). Dict and dataclass records already arrive keyed by storage name, so the KeyError from #14754 goes away. Pydantic payloads now land under storage names, which is what the lambda filter parser checks against (node.attr not in self.definition.storage_names, qdrant.py:425). The copy also means the caller's dicts are no longer mutated by pop, which main did.
  • Named vectors. vectors_config (qdrant.py:519) and the search using= (qdrant.py:298, :342) already use storage_name or name, so the dict built at :470 matches the collection's vector names.
  • Deserialize. The key and an unnamed vector come back under storage names (qdrant.py:482-493). A named-vector response is already a dict keyed by storage name. In _deserialize_dict_to_data_model (data/vector.py), dict and dataclass records read record.get(field.storage_name or field.name), and pydantic records rename storage_name to name when it's present. So all three record types read their key and vector back.
  • git merge-tree against main is clean.

I didn't run the tests.

Two small notes:

  • For pydantic models with a storage_name on a data field, the payload key changes from the declared name to the storage name. A collection with older points will then have both spellings, and a filter on the storage name won't match the older points. Reads still work for both. That may be worth a line in the release notes. The same applies to the Chroma change in #14763.
  • _storage_keyed here is the same function as the one in #14763's chroma.py. If both land, it could move to the base collection class so other connectors can use it.

Reviewed with help from Claude Code.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants