88
99
1010def get_body_from_response (response : ObjectApiResponse [Any ]) -> dict [str , Any ]:
11+ """Extract and validate the body from an Elasticsearch response.
12+
13+ Args:
14+ response: The Elasticsearch API response object.
15+
16+ Returns:
17+ The response body as a dictionary, or an empty dict if the body is missing or invalid.
18+ """
1119 if not (body := response .body ): # pyright: ignore[reportAny]
1220 return {}
1321
@@ -18,6 +26,14 @@ def get_body_from_response(response: ObjectApiResponse[Any]) -> dict[str, Any]:
1826
1927
2028def get_source_from_body (body : dict [str , Any ]) -> dict [str , Any ]:
29+ """Extract and validate the _source field from an Elasticsearch response body.
30+
31+ Args:
32+ body: The response body dictionary from Elasticsearch.
33+
34+ Returns:
35+ The _source field as a dictionary, or an empty dict if missing or invalid.
36+ """
2137 if not (source := body .get ("_source" )):
2238 return {}
2339
@@ -28,6 +44,14 @@ def get_source_from_body(body: dict[str, Any]) -> dict[str, Any]:
2844
2945
3046def get_aggregations_from_body (body : dict [str , Any ]) -> dict [str , Any ]:
47+ """Extract and validate the aggregations field from an Elasticsearch response body.
48+
49+ Args:
50+ body: The response body dictionary from Elasticsearch.
51+
52+ Returns:
53+ The aggregations field as a dictionary, or an empty dict if missing or invalid.
54+ """
3155 if not (aggregations := body .get ("aggregations" )):
3256 return {}
3357
@@ -38,6 +62,17 @@ def get_aggregations_from_body(body: dict[str, Any]) -> dict[str, Any]:
3862
3963
4064def get_hits_from_response (response : ObjectApiResponse [Any ]) -> list [dict [str , Any ]]:
65+ """Extract and validate the hits array from an Elasticsearch response.
66+
67+ This function navigates the nested structure of Elasticsearch responses to extract
68+ the hits.hits array which contains the actual search results.
69+
70+ Args:
71+ response: The Elasticsearch API response object.
72+
73+ Returns:
74+ A list of hit dictionaries from the response, or an empty list if the hits are missing or invalid.
75+ """
4176 if not (body := response .body ): # pyright: ignore[reportAny]
4277 return []
4378
@@ -66,6 +101,21 @@ def get_hits_from_response(response: ObjectApiResponse[Any]) -> list[dict[str, A
66101
67102
68103def get_fields_from_hit (hit : dict [str , Any ]) -> dict [str , list [Any ]]:
104+ """Extract and validate the fields from an Elasticsearch hit.
105+
106+ Elasticsearch can return stored fields via the "fields" key in each hit.
107+ This function validates that the fields object exists and conforms to the
108+ expected structure (a dict mapping field names to lists of values).
109+
110+ Args:
111+ hit: A single hit dictionary from an Elasticsearch response.
112+
113+ Returns:
114+ The fields dictionary from the hit, or an empty dict if missing.
115+
116+ Raises:
117+ TypeError: If the fields structure is invalid (not a dict or contains non-list values).
118+ """
69119 if not (fields := hit .get ("fields" )):
70120 return {}
71121
@@ -81,6 +131,18 @@ def get_fields_from_hit(hit: dict[str, Any]) -> dict[str, list[Any]]:
81131
82132
83133def get_field_from_hit (hit : dict [str , Any ], field : str ) -> list [Any ]:
134+ """Extract a specific field value from an Elasticsearch hit.
135+
136+ Args:
137+ hit: A single hit dictionary from an Elasticsearch response.
138+ field: The name of the field to extract.
139+
140+ Returns:
141+ The field value as a list, or an empty list if the fields object is missing.
142+
143+ Raises:
144+ TypeError: If the specified field is not present in the hit.
145+ """
84146 if not (fields := get_fields_from_hit (hit = hit )):
85147 return []
86148
@@ -92,6 +154,19 @@ def get_field_from_hit(hit: dict[str, Any], field: str) -> list[Any]:
92154
93155
94156def get_values_from_field_in_hit (hit : dict [str , Any ], field : str , value_type : type [T ]) -> list [T ]:
157+ """Extract and type-check field values from an Elasticsearch hit.
158+
159+ Args:
160+ hit: A single hit dictionary from an Elasticsearch response.
161+ field: The name of the field to extract.
162+ value_type: The expected type of values in the field list.
163+
164+ Returns:
165+ A list of values of the specified type.
166+
167+ Raises:
168+ TypeError: If the field is missing or contains values of the wrong type.
169+ """
95170 if not (value := get_field_from_hit (hit = hit , field = field )):
96171 msg = f"Field { field } is not in hit { hit } "
97172 raise TypeError (msg )
@@ -104,6 +179,19 @@ def get_values_from_field_in_hit(hit: dict[str, Any], field: str, value_type: ty
104179
105180
106181def get_first_value_from_field_in_hit (hit : dict [str , Any ], field : str , value_type : type [T ]) -> T :
182+ """Extract and validate a single-value field from an Elasticsearch hit.
183+
184+ Args:
185+ hit: A single hit dictionary from an Elasticsearch response.
186+ field: The name of the field to extract.
187+ value_type: The expected type of the value.
188+
189+ Returns:
190+ The single value from the field.
191+
192+ Raises:
193+ TypeError: If the field doesn't contain exactly one value, or if the value type is incorrect.
194+ """
107195 values : list [T ] = get_values_from_field_in_hit (hit = hit , field = field , value_type = value_type )
108196 if len (values ) != 1 :
109197 msg : str = f"Field { field } in hit { hit } is not a single value"
@@ -112,6 +200,19 @@ def get_first_value_from_field_in_hit(hit: dict[str, Any], field: str, value_typ
112200
113201
114202def managed_entry_to_document (collection : str , key : str , managed_entry : ManagedEntry ) -> dict [str , Any ]:
203+ """Convert a ManagedEntry to an Elasticsearch document format.
204+
205+ This function serializes a ManagedEntry to a document suitable for storage in Elasticsearch,
206+ including collection, key, and value fields, plus optional timestamp metadata.
207+
208+ Args:
209+ collection: The collection name to include in the document.
210+ key: The key to include in the document.
211+ managed_entry: The ManagedEntry to serialize.
212+
213+ Returns:
214+ An Elasticsearch document dictionary ready for indexing.
215+ """
115216 document : dict [str , Any ] = {"collection" : collection , "key" : key , "value" : managed_entry .to_json (include_metadata = False )}
116217
117218 if managed_entry .created_at :
@@ -123,4 +224,14 @@ def managed_entry_to_document(collection: str, key: str, managed_entry: ManagedE
123224
124225
125226def new_bulk_action (action : str , index : str , document_id : str ) -> dict [str , Any ]:
227+ """Create a bulk action descriptor for Elasticsearch bulk operations.
228+
229+ Args:
230+ action: The bulk action type (e.g., "index", "delete", "update").
231+ index: The Elasticsearch index name.
232+ document_id: The document ID.
233+
234+ Returns:
235+ A bulk action dictionary formatted for Elasticsearch's bulk API.
236+ """
126237 return {action : {"_index" : index , "_id" : document_id }}
0 commit comments