From 5fcbf0e8081c3b4434e93e4c0826ea2a28a1cd18 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Sun, 23 Aug 2026 22:53:42 +0800 Subject: [PATCH 1/3] fix: preserve Postgres vector search filter SQL --- python/semantic_kernel/connectors/postgres.py | 6 +++-- .../connectors/memory/test_postgres_store.py | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/python/semantic_kernel/connectors/postgres.py b/python/semantic_kernel/connectors/postgres.py index 5a7e66c15880..24ee3b40f320 100644 --- a/python/semantic_kernel/connectors/postgres.py +++ b/python/semantic_kernel/connectors/postgres.py @@ -795,9 +795,11 @@ def _construct_vector_query( if where_clauses := self._build_filter(options.filter): # type: ignore query += ( - sql.SQL("WHERE {clause}").format(clause=sql.SQL(" AND ").join(where_clauses)) + sql.SQL("WHERE {clause}").format( + clause=sql.SQL(" AND ").join(sql.SQL(clause) for clause in where_clauses) + ) if isinstance(where_clauses, list) - else sql.SQL("WHERE {clause}").format(clause=where_clauses) + else sql.SQL("WHERE {clause}").format(clause=sql.SQL(where_clauses)) ) query += sql.SQL(" ORDER BY {dist_col} LIMIT {limit}").format( diff --git a/python/tests/unit/connectors/memory/test_postgres_store.py b/python/tests/unit/connectors/memory/test_postgres_store.py index 8f417cf7036b..4fa2a4e4b395 100644 --- a/python/tests/unit/connectors/memory/test_postgres_store.py +++ b/python/tests/unit/connectors/memory/test_postgres_store.py @@ -18,6 +18,7 @@ PostgresStore, ) from semantic_kernel.data.vector import DistanceFunction, IndexKind, VectorStoreField, vectorstoremodel +from semantic_kernel.data.vector import VectorSearchOptions @fixture(scope="function") @@ -322,6 +323,31 @@ def model_post_init(self, context: Any) -> None: assert statement_str == expected_statement +async def test_vector_search_filter_is_emitted_as_sql(vector_store: PostgresStore) -> None: + @vectorstoremodel + @dataclass + class FilterDataModel: + id: Annotated[int, VectorStoreField("key")] + name: Annotated[str, VectorStoreField("data")] + embedding: Annotated[ + list[float], + VectorStoreField( + "vector", + index_kind=IndexKind.HNSW, + dimensions=3, + distance_function=DistanceFunction.COSINE_DISTANCE, + type="float", + ), + ] + + collection = vector_store.get_collection(collection_name="test_collection", record_type=FilterDataModel) + options = VectorSearchOptions(filter="lambda x: x.name == 'test'") + + query, _, _ = collection._construct_vector_query([1.0, 2.0, 3.0], options) + + assert 'WHERE "name" = \'test\'' in query.as_string() + + async def test_model_post_init_conflicting_distance_column_name(vector_store: PostgresStore) -> None: @vectorstoremodel @dataclass From df76fec067fd71e5093d5dab31c91b671a6f9c5c Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Sun, 23 Aug 2026 23:04:20 +0800 Subject: [PATCH 2/3] fix: address Postgres filter review findings --- python/semantic_kernel/connectors/postgres.py | 4 ++-- .../unit/connectors/memory/test_postgres_store.py | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/semantic_kernel/connectors/postgres.py b/python/semantic_kernel/connectors/postgres.py index 24ee3b40f320..26e28950f5f7 100644 --- a/python/semantic_kernel/connectors/postgres.py +++ b/python/semantic_kernel/connectors/postgres.py @@ -795,11 +795,11 @@ def _construct_vector_query( if where_clauses := self._build_filter(options.filter): # type: ignore query += ( - sql.SQL("WHERE {clause}").format( + sql.SQL(" WHERE {clause}").format( clause=sql.SQL(" AND ").join(sql.SQL(clause) for clause in where_clauses) ) if isinstance(where_clauses, list) - else sql.SQL("WHERE {clause}").format(clause=sql.SQL(where_clauses)) + else sql.SQL(" WHERE {clause}").format(clause=sql.SQL(where_clauses)) ) query += sql.SQL(" ORDER BY {dist_col} LIMIT {limit}").format( diff --git a/python/tests/unit/connectors/memory/test_postgres_store.py b/python/tests/unit/connectors/memory/test_postgres_store.py index 4fa2a4e4b395..8d38b7a0b2ff 100644 --- a/python/tests/unit/connectors/memory/test_postgres_store.py +++ b/python/tests/unit/connectors/memory/test_postgres_store.py @@ -17,8 +17,7 @@ PostgresSettings, PostgresStore, ) -from semantic_kernel.data.vector import DistanceFunction, IndexKind, VectorStoreField, vectorstoremodel -from semantic_kernel.data.vector import VectorSearchOptions +from semantic_kernel.data.vector import DistanceFunction, IndexKind, VectorSearchOptions, VectorStoreField, vectorstoremodel @fixture(scope="function") @@ -345,7 +344,12 @@ class FilterDataModel: query, _, _ = collection._construct_vector_query([1.0, 2.0, 3.0], options) - assert 'WHERE "name" = \'test\'' in query.as_string() + assert ' WHERE "name" = \'test\'' in query.as_string() + + options = VectorSearchOptions(filter=["lambda x: x.name == 'test'", "lambda x: x.id > 0"]) + query, _, _ = collection._construct_vector_query([1.0, 2.0, 3.0], options) + + assert ' WHERE "name" = \'test\' AND "id" > 0' in query.as_string() async def test_model_post_init_conflicting_distance_column_name(vector_store: PostgresStore) -> None: From d20abe9d4777fb95fff84838dda55c4e31bfdadd Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Sun, 23 Aug 2026 23:06:26 +0800 Subject: [PATCH 3/3] test: cover callable Postgres filters --- python/tests/unit/connectors/memory/test_postgres_store.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/tests/unit/connectors/memory/test_postgres_store.py b/python/tests/unit/connectors/memory/test_postgres_store.py index 8d38b7a0b2ff..3ba00c15456f 100644 --- a/python/tests/unit/connectors/memory/test_postgres_store.py +++ b/python/tests/unit/connectors/memory/test_postgres_store.py @@ -351,6 +351,11 @@ class FilterDataModel: assert ' WHERE "name" = \'test\' AND "id" > 0' in query.as_string() + options = VectorSearchOptions(filter=lambda x: x.name == "test") + query, _, _ = collection._construct_vector_query([1.0, 2.0, 3.0], options) + + assert ' WHERE "name" = \'test\'' in query.as_string() + async def test_model_post_init_conflicting_distance_column_name(vector_store: PostgresStore) -> None: @vectorstoremodel