Skip to content

Commit 71ebbb4

Browse files
[Search] Include Issue Cleanup starting 2026-08-01-preview release (#48767)
* Fix issue in python Collection * Fix serialization perf regression * Include issue in changelog * Address copilot comments * Revert generated skill documentation edits Co-authored-by: efrainretana <141282336+efrainretana@users.noreply.github.com> * Update API surface * Fix accidental change --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent fc72d1a commit 71ebbb4

22 files changed

Lines changed: 474 additions & 66 deletions

sdk/search/azure-search-documents/.github/skills/azure-search-documents/scripts/apply_generator_workarounds.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class Replacement:
9292
(
9393
"class SearchIndexerKnowledgeStoreTableProjectionSelector(TypedDict, total=False): "
9494
"# pylint: disable=name-too-long\n"
95-
""" \"\"\"Description for what data to store in Azure Tables.
95+
""" \"\"\"Description for what data to store in Azure Tables.
9696
9797
:ivar referenceKeyName: Name of reference key to different projection.
9898
:vartype referenceKeyName: str
@@ -209,4 +209,4 @@ def main() -> int:
209209

210210

211211
if __name__ == "__main__":
212-
raise SystemExit(main())
212+
raise SystemExit(main())

sdk/search/azure-search-documents/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070

7171
### Bugs Fixed
7272

73+
- Improved large document upload and merge performance by avoiding redundant serialization. #46860
74+
- Made `SearchFieldDataType.Collection` visible to static type checkers. #47929
7375
- Normalized `SearchIndexClient.update_knowledge_source_file` and its asynchronous equivalent to
7476
use the name-first signature `(name, file_id, body)`, consistent with the other File knowledge
7577
source operations.

sdk/search/azure-search-documents/api.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,25 @@ namespace azure.search.documents
2323

2424
def add_delete_actions(
2525
self,
26-
*documents: Union[List[Dict], List[List[Dict]]],
26+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
2727
**kwargs: Any
2828
) -> List[IndexAction]: ...
2929

3030
def add_merge_actions(
3131
self,
32-
*documents: Union[List[Dict], List[List[Dict]]],
32+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
3333
**kwargs: Any
3434
) -> List[IndexAction]: ...
3535

3636
def add_merge_or_upload_actions(
3737
self,
38-
*documents: Union[List[Dict], List[List[Dict]]],
38+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
3939
**kwargs: Any
4040
) -> List[IndexAction]: ...
4141

4242
def add_upload_actions(
4343
self,
44-
*documents: Union[List[Dict], List[List[Dict]]],
44+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
4545
**kwargs: Any
4646
) -> List[IndexAction]: ...
4747

@@ -13750,25 +13750,25 @@ namespace azure.search.documents.models
1375013750

1375113751
def add_delete_actions(
1375213752
self,
13753-
*documents: Union[List[Dict], List[List[Dict]]],
13753+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
1375413754
**kwargs: Any
1375513755
) -> List[IndexAction]: ...
1375613756

1375713757
def add_merge_actions(
1375813758
self,
13759-
*documents: Union[List[Dict], List[List[Dict]]],
13759+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
1376013760
**kwargs: Any
1376113761
) -> List[IndexAction]: ...
1376213762

1376313763
def add_merge_or_upload_actions(
1376413764
self,
13765-
*documents: Union[List[Dict], List[List[Dict]]],
13765+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
1376613766
**kwargs: Any
1376713767
) -> List[IndexAction]: ...
1376813768

1376913769
def add_upload_actions(
1377013770
self,
13771-
*documents: Union[List[Dict], List[List[Dict]]],
13771+
*documents: Union[Dict[str, Any], List[Dict[str, Any]]],
1377213772
**kwargs: Any
1377313773
) -> List[IndexAction]: ...
1377413774

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
apiMdSha256: 084e314c75fadd638286314c23b600310d948b8748f479b39b840db568f7b0f4
1+
apiMdSha256: 036650e18f0e3316265c4dd74ee48e52fc99ea2167f7302d443f7974c4099501
22
parserVersion: 0.3.31
33
pythonVersion: 3.12.1

sdk/search/azure-search-documents/azure/search/documents/indexes/models/_patch.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
)
2020

2121
if TYPE_CHECKING:
22+
from enum import Enum
23+
2224
from ._models import (
2325
AzureActiveDirectoryApplicationCredentials,
2426
DataChangeDetectionPolicy,
@@ -170,19 +172,52 @@ def _collection_helper(typ: Any) -> str:
170172

171173
# Re-export SearchFieldDataType with Collection method
172174
# The Collection method is added at runtime via monkey-patching
173-
SearchFieldDataType = _SearchFieldDataType
174-
SearchFieldDataType.Collection = staticmethod(_collection_helper) # type: ignore[attr-defined]
175+
if TYPE_CHECKING:
175176

176-
# Backward-compatible aliases (old camelCase names -> new UPPER_CASE names)
177-
SearchFieldDataType.String = SearchFieldDataType.STRING # type: ignore[attr-defined]
178-
SearchFieldDataType.Int32 = SearchFieldDataType.INT32 # type: ignore[attr-defined]
179-
SearchFieldDataType.Int64 = SearchFieldDataType.INT64 # type: ignore[attr-defined]
180-
SearchFieldDataType.Single = SearchFieldDataType.SINGLE # type: ignore[attr-defined]
181-
SearchFieldDataType.Double = SearchFieldDataType.DOUBLE # type: ignore[attr-defined]
182-
SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN # type: ignore[attr-defined]
183-
SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET # type: ignore[attr-defined]
184-
SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT # type: ignore[attr-defined]
185-
SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX # type: ignore[attr-defined]
177+
# pylint: disable=enum-must-inherit-case-insensitive-enum-meta,enum-must-be-uppercase
178+
class SearchFieldDataType(str, Enum):
179+
STRING = "Edm.String"
180+
INT32 = "Edm.Int32"
181+
INT64 = "Edm.Int64"
182+
DOUBLE = "Edm.Double"
183+
BOOLEAN = "Edm.Boolean"
184+
DATE_TIME_OFFSET = "Edm.DateTimeOffset"
185+
GEOGRAPHY_POINT = "Edm.GeographyPoint"
186+
COMPLEX = "Edm.ComplexType"
187+
SINGLE = "Edm.Single"
188+
HALF = "Edm.Half"
189+
INT16 = "Edm.Int16"
190+
S_BYTE = "Edm.SByte"
191+
BYTE = "Edm.Byte"
192+
String = "Edm.String"
193+
Int32 = "Edm.Int32"
194+
Int64 = "Edm.Int64"
195+
Single = "Edm.Single"
196+
Double = "Edm.Double"
197+
Boolean = "Edm.Boolean"
198+
DateTimeOffset = "Edm.DateTimeOffset"
199+
GeographyPoint = "Edm.GeographyPoint"
200+
ComplexType = "Edm.ComplexType"
201+
202+
@staticmethod
203+
def Collection(typ: Union[str, "SearchFieldDataType"]) -> str:
204+
return _collection_helper(typ)
205+
206+
# pylint: enable=enum-must-inherit-case-insensitive-enum-meta,enum-must-be-uppercase
207+
208+
else:
209+
SearchFieldDataType = _SearchFieldDataType
210+
SearchFieldDataType.Collection = staticmethod(_collection_helper) # type: ignore[attr-defined]
211+
# Backward-compatible aliases (old camelCase names -> new UPPER_CASE names)
212+
SearchFieldDataType.String = SearchFieldDataType.STRING # type: ignore[attr-defined]
213+
SearchFieldDataType.Int32 = SearchFieldDataType.INT32 # type: ignore[attr-defined]
214+
SearchFieldDataType.Int64 = SearchFieldDataType.INT64 # type: ignore[attr-defined]
215+
SearchFieldDataType.Single = SearchFieldDataType.SINGLE # type: ignore[attr-defined]
216+
SearchFieldDataType.Double = SearchFieldDataType.DOUBLE # type: ignore[attr-defined]
217+
SearchFieldDataType.Boolean = SearchFieldDataType.BOOLEAN # type: ignore[attr-defined]
218+
SearchFieldDataType.DateTimeOffset = SearchFieldDataType.DATE_TIME_OFFSET # type: ignore[attr-defined]
219+
SearchFieldDataType.GeographyPoint = SearchFieldDataType.GEOGRAPHY_POINT # type: ignore[attr-defined]
220+
SearchFieldDataType.ComplexType = SearchFieldDataType.COMPLEX # type: ignore[attr-defined]
186221

187222

188223
def Collection(typ: Any) -> str:
@@ -257,7 +292,7 @@ def SimpleField(
257292
:rtype: SearchField
258293
"""
259294
# If type is an enum, get its value; otherwise use it as-is
260-
field_type = type.value if hasattr(type, "value") else type
295+
field_type = type.value if isinstance(type, SearchFieldDataType) else type
261296
result: Dict[str, Any] = {
262297
"name": name,
263298
"type": field_type,

sdk/search/azure-search-documents/azure/search/documents/models/_patch.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,47 @@
99
Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize
1010
"""
1111

12+
from enum import Enum
1213
from typing import Any, Dict, List, Tuple, Union, cast, Optional
1314
from azure.core.exceptions import HttpResponseError
1415

16+
from .._utils.model_base import SdkJSONEncoder
1517
from ._enums import IndexActionType
1618
from ._models import IndexAction
1719
from ._models import IndexDocumentsBatch as IndexDocumentsBatchGenerated
1820

21+
_ENUM_ENCODER_MARKER = "_azure_search_documents_handles_enum"
1922

20-
def _flatten_args(args: Tuple[Union[List[Dict[Any, Any]], List[List[Dict[Any, Any]]]], ...]) -> List[Dict]:
23+
24+
def _patch_sdk_json_encoder() -> None:
25+
default = SdkJSONEncoder.default
26+
if getattr(default, _ENUM_ENCODER_MARKER, False):
27+
return
28+
29+
def default_with_enum(self: SdkJSONEncoder, value: Any) -> Any:
30+
if isinstance(value, Enum):
31+
return value.value
32+
return default(self, value)
33+
34+
setattr(default_with_enum, _ENUM_ENCODER_MARKER, True)
35+
setattr(SdkJSONEncoder, "default", default_with_enum)
36+
37+
38+
def _flatten_args(args: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], ...]) -> List[Dict[str, Any]]:
2139
"""Flatten variadic arguments into a single list of documents.
2240
2341
Supports both:
2442
- add_upload_actions([doc1, doc2]) # single list
2543
- add_upload_actions(doc1, doc2) # multiple args
2644
27-
:param args: Variadic arguments containing documents or lists of documents.
28-
:type args: Tuple[Union[List[Dict[Any, Any]], List[List[Dict[Any, Any]]]], ...]
45+
:param args: Variadic arguments containing documents or a list of documents.
46+
:type args: tuple[dict[str, Any] or list[dict[str, Any]], ...]
2947
:return: A flattened list of document dictionaries.
3048
:rtype: List[Dict]
3149
"""
3250
if len(args) == 1 and isinstance(args[0], (list, tuple)):
33-
return cast(List[Dict], args[0])
34-
return cast(List[Dict], args)
51+
return list(cast(List[Dict[str, Any]], args[0]))
52+
return list(cast(Tuple[Dict[str, Any], ...], args))
3553

3654

3755
class RequestEntityTooLargeError(HttpResponseError):
@@ -53,7 +71,9 @@ def __init__(self, *, actions: Optional[List[IndexAction]] = None) -> None:
5371
def __repr__(self) -> str:
5472
return "<IndexDocumentsBatch [{} actions]>".format(len(self.actions) if self.actions else 0)[:1024]
5573

56-
def add_upload_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **kwargs: Any) -> List[IndexAction]:
74+
def add_upload_actions(
75+
self, *documents: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs: Any
76+
) -> List[IndexAction]:
5777
# pylint: disable=unused-argument
5878
"""Add documents to upload to the Azure search index.
5979
@@ -69,7 +89,9 @@ def add_upload_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **
6989
"""
7090
return self._extend_batch(_flatten_args(documents), IndexActionType.UPLOAD)
7191

72-
def add_delete_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **kwargs: Any) -> List[IndexAction]:
92+
def add_delete_actions(
93+
self, *documents: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs: Any
94+
) -> List[IndexAction]:
7395
# pylint: disable=unused-argument
7496
"""Add documents to delete from the Azure search index.
7597
@@ -90,7 +112,9 @@ def add_delete_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **
90112
"""
91113
return self._extend_batch(_flatten_args(documents), IndexActionType.DELETE)
92114

93-
def add_merge_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **kwargs: Any) -> List[IndexAction]:
115+
def add_merge_actions(
116+
self, *documents: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs: Any
117+
) -> List[IndexAction]:
94118
# pylint: disable=unused-argument
95119
"""Add documents to merge in to existing documents in the Azure search
96120
index.
@@ -109,7 +133,7 @@ def add_merge_actions(self, *documents: Union[List[Dict], List[List[Dict]]], **k
109133
return self._extend_batch(_flatten_args(documents), IndexActionType.MERGE)
110134

111135
def add_merge_or_upload_actions(
112-
self, *documents: Union[List[Dict], List[List[Dict]]], **kwargs: Any
136+
self, *documents: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs: Any
113137
) -> List[IndexAction]:
114138
# pylint: disable=unused-argument
115139
"""Add documents to merge in to existing documents in the Azure search
@@ -175,7 +199,7 @@ def enqueue_actions(self, new_actions: Union[IndexAction, List[IndexAction]], **
175199
else:
176200
self._actions.extend(new_actions)
177201

178-
def _extend_batch(self, documents: List[Dict], action_type: str) -> List[IndexAction]:
202+
def _extend_batch(self, documents: List[Dict[str, Any]], action_type: str) -> List[IndexAction]:
179203
"""Internal helper to extend the batch with new actions.
180204
181205
:param documents: The documents to add
@@ -190,9 +214,8 @@ def _extend_batch(self, documents: List[Dict], action_type: str) -> List[IndexAc
190214

191215
new_actions = []
192216
for doc in documents:
193-
action_dict = {"@search.action": action_type}
194-
action_dict.update(doc)
195-
action = IndexAction(action_dict)
217+
action = IndexAction(action_type=action_type)
218+
action.update(doc)
196219
new_actions.append(action)
197220

198221
self._actions.extend(new_actions)
@@ -215,3 +238,5 @@ def patch_sdk():
215238
you can't accomplish using the techniques described in
216239
https://aka.ms/azsdk/python/dpcodegen/python/customize
217240
"""
241+
242+
_patch_sdk_json_encoder()

sdk/search/azure-search-documents/samples/sample_knowledge_base_crud.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def update_knowledge_base():
7373
# [START update_knowledge_base]
7474
from azure.core.credentials import AzureKeyCredential
7575
from azure.search.documents.indexes import SearchIndexClient
76+
7677
index_client = SearchIndexClient(service_endpoint, AzureKeyCredential(key))
7778
knowledge_base = index_client.get_knowledge_base(knowledge_base_name)
7879
knowledge_base.tags = {"environment": "sample", "owner": "retrieval-team"}

sdk/search/azure-search-documents/samples/sample_knowledge_base_crud_async.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ async def update_knowledge_base_async():
7676
# [START update_knowledge_base_async]
7777
from azure.core.credentials import AzureKeyCredential
7878
from azure.search.documents.indexes.aio import SearchIndexClient
79+
7980
index_client = SearchIndexClient(service_endpoint, AzureKeyCredential(key))
8081
async with index_client:
8182
knowledge_base = await index_client.get_knowledge_base(knowledge_base_name)

sdk/search/azure-search-documents/samples/sample_knowledge_source_crud.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,7 @@ def create_private_blob_knowledge_source(): # pylint: disable=too-many-locals
215215
assert indexer_client.get_data_source_connection(data_source_name).name == data_source_name
216216
assert indexer_client.get_indexer_status(indexer_name).status is not None
217217
generated_index = index_client.get_index(generated_index_name)
218-
analyzers = {
219-
str(field.analyzer_name)
220-
for field in generated_index.fields
221-
if field.analyzer_name is not None
222-
}
218+
analyzers = {str(field.analyzer_name) for field in generated_index.fields if field.analyzer_name is not None}
223219
assert os.environ["AZURE_SEARCH_EXPECTED_ANALYZER"] in analyzers
224220
assert os.environ["AZURE_SEARCH_EXPECTED_FALLBACK_ANALYZER"] in analyzers
225221
print(f"Verified generated resources and analyzers: {sorted(analyzers)}")

sdk/search/azure-search-documents/samples/sample_knowledge_source_crud_async.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,7 @@ async def create_private_blob_knowledge_source_async(): # pylint: disable=too-m
223223
assert (await indexer_client.get_indexer_status(indexer_name)).status is not None
224224
generated_index = await index_client.get_index(generated_index_name)
225225
analyzers = {
226-
str(field.analyzer_name)
227-
for field in generated_index.fields
228-
if field.analyzer_name is not None
226+
str(field.analyzer_name) for field in generated_index.fields if field.analyzer_name is not None
229227
}
230228
assert os.environ["AZURE_SEARCH_EXPECTED_ANALYZER"] in analyzers
231229
assert os.environ["AZURE_SEARCH_EXPECTED_FALLBACK_ANALYZER"] in analyzers

0 commit comments

Comments
 (0)