Skip to content

Commit 2c903cf

Browse files
authored
feat(python): add bridge to transformation on search (#4898)
1 parent bcadf3a commit 2c903cf

File tree

6 files changed

+106
-3
lines changed

6 files changed

+106
-3
lines changed

playground/python/app/search.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from os import environ
22

33
from algoliasearch.search.client import SearchClientSync
4+
from algoliasearch.search.config import SearchConfig
45
from algoliasearch.search import __version__
56
from dotenv import load_dotenv
67

@@ -27,3 +28,29 @@ def main():
2728
client.close()
2829

2930
print("client closed")
31+
32+
print("with transformations")
33+
34+
config = SearchConfig(
35+
environ.get("ALGOLIA_APPLICATION_ID"), environ.get("ALGOLIA_ADMIN_KEY")
36+
)
37+
38+
config.with_transformation("eu")
39+
40+
print("region in playground")
41+
print(config.region)
42+
43+
client = SearchClientSync.create_with_config(config)
44+
client.add_user_agent("playground search with ingestion")
45+
46+
print("user_agent", client._config._user_agent.get())
47+
48+
try:
49+
resp = client.save_objects_with_transformation(
50+
"foo", [{"objectID": "bar"}], wait_for_tasks=True
51+
)
52+
print(resp)
53+
finally:
54+
client.close()
55+
56+
print("client closed")

playground/python/poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

playground/python/requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
argcomplete==3.6.2
2+
click==8.2.1
3+
packaging==25.0
4+
pipx==1.7.1
5+
platformdirs==4.3.8
6+
userpath==1.9.2

templates/python/api.mustache

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ from algoliasearch.search.models.operation_type import OperationType
99
from algoliasearch.search.models.replace_all_objects_response import ReplaceAllObjectsResponse
1010
from algoliasearch.search.models.scope_type import ScopeType
1111
from algoliasearch.search.models.secured_api_key_restrictions import SecuredApiKeyRestrictions
12+
13+
from algoliasearch.ingestion.models.watch_response import WatchResponse
14+
from algoliasearch.ingestion.config import IngestionConfig
15+
from algoliasearch.ingestion.client import (IngestionClient, IngestionClientSync)
1216
{{/isSearchClient}}
1317

1418
{{#operations}}{{#operation}}{{#imports}}
@@ -37,15 +41,22 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
3741
"""
3842

3943
_transporter: Transporter{{#isSyncClient}}Sync{{/isSyncClient}}
44+
{{#isSearchClient}}
45+
_ingestion_transporter: Optional[IngestionClient{{#isSyncClient}}Sync{{/isSyncClient}}]
46+
{{/isSearchClient}}
4047
_config: BaseConfig
4148
_request_options: RequestOptions
4249

4350
def __init__(self, app_id: Optional[str] = None, api_key: Optional[str] = None, {{#hasRegionalHost}}region: {{#fallbackToAliasHost}}Optional[str] = None{{/fallbackToAliasHost}}{{^fallbackToAliasHost}}str = ""{{/fallbackToAliasHost}}, {{/hasRegionalHost}}transporter: Optional[Transporter{{#isSyncClient}}Sync{{/isSyncClient}}] = None, config: Optional[{{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}] = None) -> None:
4451
if transporter is not None and config is None:
4552
config = {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}(transporter.config.app_id, transporter.config.api_key{{#hasRegionalHost}}, region{{/hasRegionalHost}})
46-
47-
if config is None:
53+
elif config is None:
4854
config = {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}(app_id, api_key{{#hasRegionalHost}}, region{{/hasRegionalHost}})
55+
{{#isSearchClient}}
56+
elif config.region is not None:
57+
self._ingestion_transporter = IngestionClient{{#isSyncClient}}Sync{{/isSyncClient}}.create_with_config(IngestionConfig(config.app_id, config.api_key, config.region))
58+
{{/isSearchClient}}
59+
4960
self._config = config
5061
self._request_options = RequestOptions(config)
5162

templates/python/config.mustache

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ class {{#lambda.pascalcase}}{{client}}{{/lambda.pascalcase}}Config(BaseConfig):
7777
{{/hostWithAppID}}
7878
)
7979
{{/hasRegionalHost}}
80+
81+
{{#isSearchClient}}
82+
self.region = None
83+
84+
def with_transformation(self, region: str = ""):
85+
"This method is required to be called with the appropriate region of your Algolia application if you wish to leverage the *_with_transformation methods."
86+
self.region = region
87+
{{/isSearchClient}}

templates/python/search_helpers.mustache

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,31 @@
292292
"""
293293
return {{^isSyncClient}}await {{/isSyncClient}}self.chunked_batch(index_name=index_name, objects=objects, action=Action.ADDOBJECT, wait_for_tasks=wait_for_tasks, batch_size=batch_size, request_options=request_options)
294294
295+
{{^isSyncClient}}async {{/isSyncClient}}def save_objects_with_transformation(
296+
self,
297+
index_name: str,
298+
objects: List[Dict[str, Any]],
299+
wait_for_tasks: bool = False,
300+
batch_size: int = 1000,
301+
request_options: Optional[Union[dict, RequestOptions]] = None,
302+
) -> WatchResponse:
303+
"""
304+
Helper: Similar to the `save_objects` method but requires a Push connector (https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/connectors/push/) to be created first, in order to transform records before indexing them to Algolia. The `region` must've been passed to the client's config at instantiation.
305+
"""
306+
if self._ingestion_transporter is None:
307+
raise ValueError("`region` must be provided at client instantiation before calling this method.")
308+
309+
return {{^isSyncClient}}await {{/isSyncClient}}self._ingestion_transporter.push(
310+
index_name=index_name,
311+
push_task_payload={
312+
"action": Action.ADDOBJECT,
313+
"records": objects,
314+
},
315+
watch=wait_for_tasks,
316+
request_options=request_options,
317+
)
318+
319+
295320
{{^isSyncClient}}async {{/isSyncClient}}def delete_objects(
296321
self,
297322
index_name: str,
@@ -319,6 +344,32 @@
319344
"""
320345
return {{^isSyncClient}}await {{/isSyncClient}}self.chunked_batch(index_name=index_name, objects=objects, action=Action.PARTIALUPDATEOBJECT if create_if_not_exists else Action.PARTIALUPDATEOBJECTNOCREATE, wait_for_tasks=wait_for_tasks, batch_size=batch_size, request_options=request_options)
321346
347+
{{^isSyncClient}}async {{/isSyncClient}}def partial_update_objects_with_transformation(
348+
self,
349+
index_name: str,
350+
objects: List[Dict[str, Any]],
351+
create_if_not_exists: bool = False,
352+
wait_for_tasks: bool = False,
353+
batch_size: int = 1000,
354+
request_options: Optional[Union[dict, RequestOptions]] = None,
355+
) -> WatchResponse:
356+
"""
357+
Helper: Similar to the `partial_update_objects` method but requires a Push connector (https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/connectors/push/) to be created first, in order to transform records before indexing them to Algolia. The `region` must've been passed to the client instantiation method.
358+
"""
359+
if self._ingestion_transporter is None:
360+
raise ValueError("`region` must be provided at client instantiation before calling this method.")
361+
362+
return {{^isSyncClient}}await {{/isSyncClient}}self._ingestion_transporter.push(
363+
index_name=index_name,
364+
push_task_payload={
365+
"action": Action.PARTIALUPDATEOBJECT if create_if_not_exists else Action.PARTIALUPDATEOBJECTNOCREATE,
366+
"records": objects,
367+
},
368+
watch=wait_for_tasks,
369+
request_options=request_options,
370+
)
371+
372+
322373
{{^isSyncClient}}async {{/isSyncClient}}def chunked_batch(
323374
self,
324375
index_name: str,

0 commit comments

Comments
 (0)