Skip to content

Vector store filters compare a value with a property of another type differently on each store #1781

Description

@edwinyyyu

What happens

Measured on main at ad8ff24 against SQLiteVectorStore and SQLiteVecVectorStore (identical results, "SQLite" below), QdrantVectorStore on Qdrant 1.17.0 (REST and gRPC identical), and MilvusVectorStore on Milvus 2.6.24 and Milvus Lite 3.2.1 (identical unless noted). d_* properties are declared in indexed_properties_schema; undeclared properties give the same results unless noted.

Datetimes and strings (DT is 2024-06-15T12:00:00Z):

filter records SQLite Qdrant Milvus
d_dt = '2024-06-15T12:00:00Z' (a string) datetime DT none DT none
d_dt = '2024-06-15T12:00:00+00:00' (a string) datetime DT, and DT at +05:00 none none both
d_str = DT (a datetime) string '2024-06-15T12:00:00+00:00' none match match
d_str <= DT strings '2', '', and the one above none the ISO string all three, by text order

Bools against numbers, and ordering on bools and strings:

filter records SQLite Qdrant Milvus
d_int < True ints -1, 0, 1, 2, and 3 ArgumentError -1 and 0 2.6.24: none; Lite: -1 and 0
d_bool < True True, False ArgumentError none False
d_str < 'a' strings byte order TypeError from the store byte order

Ints against floats:

filter records SQLite Qdrant Milvus
d_float = 2 (an int) float 2.0 none none match
d_int = 2.0 (a float) int 2 none match match
d_float < 2 (an int) floats 1.5, 0.0, and -0.0 none all three all three
d_int < 1.5 ints -1, 0, and 1 none -1 and 0 all three
d_int = 1.5 int 1 none match none

On Qdrant the last two hold only for a declared int property; an undeclared one compares by value.

In, whose values are declared list[int] | list[str]:

filter SQLite Qdrant Milvus
d_int in [2.0, 1.5] none ValidationError int 2
d_bool in [True] the True records ValidationError the True records
d_str in [DT] none ValidationError the ISO string
d_int in [1, '2'] int 1 ValidationError 2.6.24: int 1; Lite: refused, "list elements must be of compatible types"
d_int in ['2', 1] none ValidationError same as above

Why

  • SQLite: properties are stored type-tagged (packages/server/src/memmachine_server/common/properties_json.py:33-63) and every leaf is gated on the filter value's type tag (packages/server/src/memmachine_server/common/filter/sql_filter_util.py:181-187), so an int never matches a float. An ordering on a bool builds column < True, which SQLAlchemy refuses. In takes its type gate and column cast from its first member (:163-178), so the answer depends on member order.
  • Qdrant: payload values are stored as given (packages/server/src/memmachine_server/common/vector_store/qdrant_vector_store.py:253-269). Int, bool, and string equality use MatchValue, float equality a Range(gte=v, lte=v), and datetime equality a DatetimeRange (:112-184); Qdrant applies the DatetimeRange to string payloads that parse as datetimes, and a string MatchValue matches a datetime payload's stored text. A bool passes the store's OrderedValue check (:132) and orders as a number, a string ordering raises in the store (:132-137), and In becomes MatchAny (:186-194), whose model accepts only an all-int or all-string list. On the integer payload index created for a declared int property (:535-543, :750-754), a fractional bound behaves as truncated.
  • Milvus: every property is an untyped dynamic JSON field, datetimes as UTC ISO text (packages/server/src/memmachine_server/common/vector_store/milvus_vector_store.py:92-96, :177-198), and filter literals are rendered the same way (:76-84), so Milvus's own JSON comparison decides: datetimes and strings compare as text, ints and floats by value, and bools as False < True. Milvus 2.6.24 and Milvus Lite disagree on bools against numbers and on mixed In lists.

Expected

Every filter returns the same records on every store: a value is compared only with properties of its own type, ordering is defined only on numbers and datetimes, and an In list holds values of one type.

Fix

#1628 makes a store filter only on the properties it declares, a value of another type than its key declares matching nothing. #1616 closes the filter union and allows ordering only on numbers and datetimes. #1670 and #1702 take user properties out of the vector store. A filter type check is being added on top of #1616.

🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.

Activity

  1. added theissue type on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions