diff --git a/python/semantic_kernel/connectors/azure_ai_search.py b/python/semantic_kernel/connectors/azure_ai_search.py index f01bef97a96f..11433412e6d0 100644 --- a/python/semantic_kernel/connectors/azure_ai_search.py +++ b/python/semantic_kernel/connectors/azure_ai_search.py @@ -595,15 +595,23 @@ async def _inner_search( if values is None: raise VectorStoreOperationException("No vector and/or keywords provided for search.") vector_field = self.definition.try_get_vector_field(options.vector_property_name) - search_args["search_fields"] = ( - [options.additional_property_name] - if options.additional_property_name is not None - else [ - field.name + if options.additional_property_name is not None: + target_field = next( + (field for field in self.definition.fields if field.name == options.additional_property_name), + None, + ) + search_field = ( + target_field.storage_name or target_field.name + if target_field is not None + else options.additional_property_name + ) + search_args["search_fields"] = [search_field] + else: + search_args["search_fields"] = [ + field.storage_name or field.name for field in self.definition.fields if field.field_type == FieldTypes.DATA and field.is_full_text_indexed ] - ) if not search_args["search_fields"]: raise VectorStoreOperationException("No searchable fields found for hybrid search.") search_args["search_text"] = values @@ -613,14 +621,14 @@ async def _inner_search( search_args["vector_queries"] = [ VectorizedQuery( vector=vector, # type: ignore - fields=vector_field.name if vector_field else None, + fields=vector_field.storage_name or vector_field.name if vector_field else None, ) ] else: search_args["vector_queries"] = [ VectorizableTextQuery( text=values, - fields=vector_field.name if vector_field else None, + fields=vector_field.storage_name or vector_field.name if vector_field else None, ) ] try: diff --git a/python/tests/unit/connectors/memory/test_azure_ai_search.py b/python/tests/unit/connectors/memory/test_azure_ai_search.py index d5a8ba2111e0..041819d6a209 100644 --- a/python/tests/unit/connectors/memory/test_azure_ai_search.py +++ b/python/tests/unit/connectors/memory/test_azure_ai_search.py @@ -440,6 +440,54 @@ async def test_search_keyword_hybrid_search(collection, mock_search, include_vec assert call[1]["vector_queries"][0].fields == "vector" +async def test_search_keyword_hybrid_search_with_storage_names( + azure_ai_search_unit_test_env, mock_search, dimensions, index_kind, distance_function, vector_property_type +): + from semantic_kernel.data.vector import VectorStoreCollectionDefinition, VectorStoreField + + storage_definition = VectorStoreCollectionDefinition( + fields=[ + VectorStoreField("key", name="id", storage_name="doc_id", type="str"), + VectorStoreField("data", name="content", storage_name="body", type="str", is_full_text_indexed=True), + VectorStoreField("data", name="body", storage_name="summary", type="str", is_full_text_indexed=True), + VectorStoreField( + "vector", + name="vector", + storage_name="vec", + dimensions=dimensions, + index_kind=index_kind, + distance_function=distance_function, + type=vector_property_type, + ), + ] + ) + col = AzureAISearchCollection(record_type=dict, definition=storage_definition) + + # 1. Default search fields and vector fields use storage names + await col.hybrid_search(values="query", vector=[0.1, 0.2, 0.3]) + call = mock_search.call_args_list[-1] + assert call[1]["search_fields"] == ["body", "summary"] + assert call[1]["vector_queries"][0].fields == "vec" + + # 2. additional_property_name resolves declared property name to storage name + await col.hybrid_search(values="query", vector=[0.1, 0.2, 0.3], additional_property_name="content") + call = mock_search.call_args_list[-1] + assert call[1]["search_fields"] == ["body"] + assert call[1]["vector_queries"][0].fields == "vec" + + # 3. Declared property name takes precedence over earlier storage alias + await col.hybrid_search(values="query", vector=[0.1, 0.2, 0.3], additional_property_name="body") + call = mock_search.call_args_list[-1] + assert call[1]["search_fields"] == ["summary"] + assert call[1]["vector_queries"][0].fields == "vec" + + # 4. Fallback to direct storage name when no declared property matches + await col.hybrid_search(values="query", vector=[0.1, 0.2, 0.3], additional_property_name="summary") + call = mock_search.call_args_list[-1] + assert call[1]["search_fields"] == ["summary"] + assert call[1]["vector_queries"][0].fields == "vec" + + @mark.parametrize("filter, result", filter_lambda_list("ai_search")) def test_lambda_filter(collection, filter, result): if isinstance(result, type) and issubclass(result, Exception):