Skip to content

Negated vector store filters drop records without the property on the SQLite and Milvus stores #1780

Description

@edwinyyyu

What happens

Measured on main at ad8ff24 with nine records in one collection: five carry an integer property d_int (2, 1, -1, 0, and 3) and four do not carry it. Count of records each filter returns:

filter SQLiteVectorStore, SQLiteVecVectorStore MilvusVectorStore (Milvus 2.6.24 and Milvus Lite 3.2.1) QdrantVectorStore (Qdrant 1.17.0, REST and gRPC)
d_int = 2 1 1 1
not (d_int = 2) 4 4 8
d_int != 2 4 4 8

not (d_int = 2) on the SQLite stores and Milvus returns only the four records holding another value and drops the four without the property, so a filter and its negation together miss records. Qdrant returns all eight. The same split holds for string properties and for properties not in indexed_properties_schema. not (d_int in [1, 2]) drops the four on the SQLite stores and Milvus 2.6.24 and keeps them on Qdrant and Milvus Lite.

Why

  • The SQL stores compile a leaf to and_(type_check, column <op> value) (packages/server/src/memmachine_server/common/filter/sql_filter_util.py:183-187) and Not to ~inner (:229-230). For a record without the property the leaf is SQL NULL, NOT NULL is NULL, and the row is filtered out. != compiles to column != value, which is NULL there as well.
  • The Milvus store renders Not as Milvus not (...) and != as Milvus != over the property's dynamic field (packages/server/src/memmachine_server/common/vector_store/milvus_vector_store.py:116-119, :132-136); neither matches an entity that lacks the field.
  • The Qdrant store renders both != and Not as must_not (packages/server/src/memmachine_server/common/vector_store/qdrant_vector_store.py:96-99, :150-157), which admits points without the field.

Expected

Negation is the complement on every store: not (f) matches exactly the records f does not match, records without the property included, and x != v matches the same records as not (x = v).

Fix

#1616 makes negation the complement on every backend: != parses as Not(Equals), and the SQL compilers render Not as NOT COALESCE(x, FALSE). For Milvus it builds on #1736, which pushes negation down to the conditions and admits entities lacking the property.

🤖 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