From a4f5db3e2e78fe23b346c6b8981dc91155f93a67 Mon Sep 17 00:00:00 2001 From: Abhinav Maurya Date: Fri, 30 Oct 2026 01:18:08 +0530 Subject: [PATCH 1/2] Python: Use storage_name for hybrid search fields and vector queries (#14684) --- .../connectors/azure_ai_search.py | 24 +++++++---- .../connectors/memory/test_azure_ai_search.py | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/python/semantic_kernel/connectors/azure_ai_search.py b/python/semantic_kernel/connectors/azure_ai_search.py index f01bef97a96f..c345ab8fda5e 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: + search_field = next( + ( + field.storage_name or field.name + for field in self.definition.fields + if field.name == options.additional_property_name + or field.storage_name == options.additional_property_name + ), + 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..5cbfc74ebb56 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,47 @@ 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( + "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"] + 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. additional_property_name using storage name directly + 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"] == ["body"] + 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): From 0450487f13dd830a63d61dd2b6a79840e073aa79 Mon Sep 17 00:00:00 2001 From: Abhinav Maurya Date: Fri, 30 Oct 2026 02:43:21 +0530 Subject: [PATCH 2/2] Prioritize declared property names over storage aliases in hybrid search --- .../connectors/azure_ai_search.py | 16 ++++++++-------- .../connectors/memory/test_azure_ai_search.py | 13 ++++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/python/semantic_kernel/connectors/azure_ai_search.py b/python/semantic_kernel/connectors/azure_ai_search.py index c345ab8fda5e..11433412e6d0 100644 --- a/python/semantic_kernel/connectors/azure_ai_search.py +++ b/python/semantic_kernel/connectors/azure_ai_search.py @@ -596,14 +596,14 @@ async def _inner_search( raise VectorStoreOperationException("No vector and/or keywords provided for search.") vector_field = self.definition.try_get_vector_field(options.vector_property_name) if options.additional_property_name is not None: - search_field = next( - ( - field.storage_name or field.name - for field in self.definition.fields - if field.name == options.additional_property_name - or field.storage_name == options.additional_property_name - ), - options.additional_property_name, + 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: 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 5cbfc74ebb56..041819d6a209 100644 --- a/python/tests/unit/connectors/memory/test_azure_ai_search.py +++ b/python/tests/unit/connectors/memory/test_azure_ai_search.py @@ -449,6 +449,7 @@ async def test_search_keyword_hybrid_search_with_storage_names( 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", @@ -465,7 +466,7 @@ async def test_search_keyword_hybrid_search_with_storage_names( # 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"] + 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 @@ -474,10 +475,16 @@ async def test_search_keyword_hybrid_search_with_storage_names( assert call[1]["search_fields"] == ["body"] assert call[1]["vector_queries"][0].fields == "vec" - # 3. additional_property_name using storage name directly + # 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"] == ["body"] + 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"