Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions python/semantic_kernel/connectors/sql_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from semantic_kernel.connectors.ai.embedding_generator_base import EmbeddingGeneratorBase
from semantic_kernel.data.vector import (
DISTANCE_FUNCTION_DIRECTION_HELPER,
DistanceFunction,
GetFilteredRecordOptions,
IndexKind,
Expand Down Expand Up @@ -1150,8 +1149,10 @@ def _build_search_query(
f"Distance function '{vector_field.distance_function}' is not supported for field '{vector_field.name}'"
)
distance_function = DISTANCE_FUNCTION_MAP[vector_field.distance_function]
asc: bool = True
asc = DISTANCE_FUNCTION_DIRECTION_HELPER[vector_field.distance_function](0, 1)
# VECTOR_DISTANCE returns a distance for every supported metric (the dot
# metric is the negative dot product), so the nearest rows are the
# smallest values.
asc = True

command.query.append(
f", VECTOR_DISTANCE('{distance_function}', {QueryBuilder.escape_identifier(vector_field.storage_name or vector_field.name)}, CAST(? AS VECTOR({vector_field.dimensions}))) as {SCORE_FIELD_NAME}\n", # noqa: E501
Expand Down
58 changes: 55 additions & 3 deletions python/tests/unit/connectors/memory/test_sql_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,16 @@ def test_build_delete_query(self):
assert cmd.parameters[0] == "test"
assert str_cmd == "DELETE FROM [dbo].[Test] WHERE [id] IN (?) ;"

def test_build_search_query(self):
@mark.parametrize(
("distance_function", "metric"),
[
(DistanceFunction.COSINE_DISTANCE, "cosine"),
(DistanceFunction.EUCLIDEAN_DISTANCE, "euclidean"),
(DistanceFunction.DOT_PROD, "dot"),
(DistanceFunction.DEFAULT, "cosine"),
],
)
def test_build_search_query(self, distance_function, metric):
schema = "dbo"
table = "Test"
key_field = VectorStoreField("key", name="id", type="str")
Expand All @@ -300,7 +309,7 @@ def test_build_search_query(self):
name="embedding",
type="float",
dimensions=5,
distance_function=DistanceFunction.COSINE_DISTANCE,
distance_function=distance_function,
),
]
vector = [0.1, 0.2, 0.3, 0.4, 0.5]
Expand All @@ -312,7 +321,7 @@ def test_build_search_query(self):
assert cmd.parameters[0] == json.dumps(vector)
str_cmd = str(cmd)
assert (
str_cmd == "SELECT [id], [name], [age], VECTOR_DISTANCE('cosine', [embedding], CAST(? AS VECTOR(5))) as "
str_cmd == f"SELECT [id], [name], [age], VECTOR_DISTANCE('{metric}', [embedding], CAST(? AS VECTOR(5))) as "
"_vector_distance_value\n FROM [dbo].[Test] \nORDER BY "
"_vector_distance_value ASC\nOFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;"
)
Expand Down Expand Up @@ -573,6 +582,49 @@ class MockRow:
(json.dumps(vector), "test"),
)

async def test_search_with_default_distance_function(
self,
sql_server_unit_test_env,
mock_connection,
):
@vectorstoremodel
@dataclass
class DefaultDistanceRecord:
id: Annotated[str, VectorStoreField("key")]
content: Annotated[str, VectorStoreField("data")]
vector: Annotated[list[float], VectorStoreField("vector", dimensions=5)] = None

mock_cursor = MagicMock()
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
collection = SqlServerCollection(
collection_name="test",
record_type=DefaultDistanceRecord,
connection=mock_connection,
)
assert collection.definition.vector_fields[0].distance_function == DistanceFunction.DEFAULT
vector = [0.1, 0.2, 0.3, 0.4, 0.5]

@dataclass
class MockRow:
id: str
content: str
_vector_distance_value: float

mock_cursor.description = [["id"], ["content"], ["_vector_distance_value"]]
mock_cursor.__iter__.return_value = [MockRow("1", "test", 0.1)]
search_result = await collection.search(vector=vector)
records = [record async for record in search_result.results]
assert records[0].record.id == "1"
assert records[0].score == 0.1
mock_cursor.execute.assert_called_with(
(
"SELECT [id], [content], VECTOR_DISTANCE('cosine', [vector], CAST(? AS VECTOR(5))) as "
"_vector_distance_value\n FROM [dbo].[test] \nORDER BY _vector_distance_value ASC\n"
"OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;"
),
(json.dumps(vector),),
)

async def test_ensure_collection_exists(
self,
sql_server_unit_test_env,
Expand Down
Loading