diff --git a/python/semantic_kernel/connectors/postgres.py b/python/semantic_kernel/connectors/postgres.py index 5a7e66c15880..26e28950f5f7 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..3ba00c15456f 100644 --- a/python/tests/unit/connectors/memory/test_postgres_store.py +++ b/python/tests/unit/connectors/memory/test_postgres_store.py @@ -17,7 +17,7 @@ PostgresSettings, PostgresStore, ) -from semantic_kernel.data.vector import DistanceFunction, IndexKind, VectorStoreField, vectorstoremodel +from semantic_kernel.data.vector import DistanceFunction, IndexKind, VectorSearchOptions, VectorStoreField, vectorstoremodel @fixture(scope="function") @@ -322,6 +322,41 @@ 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() + + 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() + + 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