Skip to content

Commit a2ac24f

Browse files
algolia-botFluf22
andcommitted
feat(csharp): add transformation helpers for object indexing with a transformation (#5452) (generated) [skip ci]
Co-authored-by: Thomas Raffray <Fluf22@users.noreply.github.com>
1 parent 74eccc4 commit a2ac24f

File tree

12 files changed

+351
-39
lines changed

12 files changed

+351
-39
lines changed

clients/algoliasearch-client-csharp/algoliasearch/Clients/IngestionClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Algolia.Search.Clients;
1818
/// <summary>
1919
/// Represents a collection of functions to interact with the API endpoints
2020
/// </summary>
21-
public interface IIngestionClient
21+
public partial interface IIngestionClient
2222
{
2323
/// <summary>
2424
/// Creates a new authentication resource.

clients/algoliasearch-client-csharp/algoliasearch/Clients/SearchClient.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Net.Http;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Algolia.Search.Clients;
910
using Algolia.Search.Http;
1011
using Algolia.Search.Models.Search;
1112
using Algolia.Search.Transport;
@@ -2568,6 +2569,7 @@ public partial class SearchClient : ISearchClient
25682569
{
25692570
internal HttpTransport _transport;
25702571
private readonly ILogger<SearchClient> _logger;
2572+
private IIngestionClient _ingestionTransporter;
25712573

25722574
/// <summary>
25732575
/// Create a new Search client for the given appID and apiKey.
@@ -2640,6 +2642,43 @@ public void SetClientApiKey(string apiKey)
26402642
_transport._algoliaConfig.SetClientApiKey(apiKey);
26412643
}
26422644

2645+
/// <summary>
2646+
/// Sets the region of the transformation pipeline. This is required to be called
2647+
/// if you wish to leverage the transformation pipeline (via the *WithTransformation methods).
2648+
/// </summary>
2649+
/// <param name="region">The region ("us" or "eu")</param>
2650+
/// <param name="factory">Logger factory</param>
2651+
public void SetTransformationRegion(string region, ILoggerFactory factory = null)
2652+
{
2653+
if (string.IsNullOrWhiteSpace(region))
2654+
{
2655+
throw new ArgumentException(
2656+
"`region` must be provided when leveraging the transformation pipeline"
2657+
);
2658+
}
2659+
2660+
if (
2661+
string.IsNullOrWhiteSpace(_transport._algoliaConfig.AppId)
2662+
|| string.IsNullOrWhiteSpace(_transport._algoliaConfig.ApiKey)
2663+
)
2664+
{
2665+
throw new ArgumentException("AppId and ApiKey are required for transformation pipeline");
2666+
}
2667+
2668+
_ingestionTransporter = new IngestionClient(
2669+
new IngestionConfig(_transport._algoliaConfig.AppId, _transport._algoliaConfig.ApiKey, region)
2670+
{
2671+
DefaultHeaders = _transport._algoliaConfig.DefaultHeaders,
2672+
ConnectTimeout = _transport._algoliaConfig.ConnectTimeout,
2673+
ReadTimeout = _transport._algoliaConfig.ReadTimeout,
2674+
WriteTimeout = _transport._algoliaConfig.WriteTimeout,
2675+
Compression = _transport._algoliaConfig.Compression,
2676+
CustomHosts = _transport._algoliaConfig.CustomHosts,
2677+
},
2678+
factory
2679+
);
2680+
}
2681+
26432682
/// <inheritdoc />
26442683
public async Task<AddApiKeyResponse> AddApiKeyAsync(
26452684
ApiKey apiKey,

clients/algoliasearch-client-csharp/algoliasearch/Utils/IngestionClientExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ public async Task<List<WatchResponse>> ChunkedPushAsync(
118118
);
119119
}
120120

121-
await RetryHelper.RetryUntil(
121+
await RetryHelper
122+
.RetryUntil(
122123
async () =>
123124
{
124125
try

clients/algoliasearch-client-csharp/algoliasearch/Utils/RetryHelper.cs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,35 @@ public static class RetryHelper
2727
/// <returns>The result of the function if the validation function returns true</returns>
2828
/// <exception cref="AlgoliaException">Thrown if the maximum number of retries is reached</exception>
2929
public static async Task<T> RetryUntil<T>(
30-
Func<Task<T>> func,
31-
Func<T, bool> validate,
32-
int maxRetries = DefaultMaxRetries,
33-
Func<int, int> timeout = null,
34-
CancellationToken ct = default
35-
)
36-
{
37-
timeout ??= NextDelay;
30+
Func<Task<T>> func,
31+
Func<T, bool> validate,
32+
int maxRetries = DefaultMaxRetries,
33+
Func<int, int> timeout = null,
34+
CancellationToken ct = default
35+
)
36+
{
37+
timeout ??= NextDelay;
3838

39-
var retryCount = 0;
40-
while (retryCount < maxRetries)
39+
var retryCount = 0;
40+
while (retryCount < maxRetries)
41+
{
42+
var resp = await func().ConfigureAwait(false);
43+
if (validate(resp))
4144
{
42-
var resp = await func().ConfigureAwait(false);
43-
if (validate(resp))
44-
{
45-
return resp;
46-
}
47-
48-
await Task.Delay(timeout(retryCount), ct).ConfigureAwait(false);
49-
retryCount++;
45+
return resp;
5046
}
5147

52-
throw new AlgoliaException(
53-
"The maximum number of retries exceeded. (" + (retryCount + 1) + "/" + maxRetries + ")"
54-
);
48+
await Task.Delay(timeout(retryCount), ct).ConfigureAwait(false);
49+
retryCount++;
5550
}
5651

57-
private static int NextDelay(int retryCount)
58-
{
59-
return Math.Min(retryCount * 200, 5000);
60-
}
52+
throw new AlgoliaException(
53+
"The maximum number of retries exceeded. (" + (retryCount + 1) + "/" + maxRetries + ")"
54+
);
55+
}
56+
57+
private static int NextDelay(int retryCount)
58+
{
59+
return Math.Min(retryCount * 200, 5000);
60+
}
6161
}

clients/algoliasearch-client-csharp/algoliasearch/Utils/SearchClientExtensions.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,8 @@ public async Task<GetTaskResponse> WaitForTaskAsync(
536536
RequestOptions requestOptions = null,
537537
CancellationToken ct = default
538538
) =>
539-
await RetryHelper.RetryUntil(
539+
await RetryHelper
540+
.RetryUntil(
540541
async () => await GetTaskAsync(indexName, taskId, requestOptions, ct),
541542
resp => resp.Status == Models.Search.TaskStatus.Published,
542543
maxRetries,
@@ -566,7 +567,8 @@ public async Task<GetTaskResponse> WaitForAppTaskAsync(
566567
RequestOptions requestOptions = null,
567568
CancellationToken ct = default
568569
) =>
569-
await RetryHelper.RetryUntil(
570+
await RetryHelper
571+
.RetryUntil(
570572
async () => await GetAppTaskAsync(taskId, requestOptions, ct),
571573
resp => resp.Status == Models.Search.TaskStatus.Published,
572574
maxRetries,
@@ -603,7 +605,8 @@ public async Task<GetApiKeyResponse> WaitForApiKeyAsync(
603605
throw new AlgoliaException("`ApiKey` is required when waiting for an `update` operation.");
604606
}
605607

606-
return await RetryHelper.RetryUntil(
608+
return await RetryHelper
609+
.RetryUntil(
607610
() => GetApiKeyAsync(key, requestOptions, ct),
608611
resp =>
609612
{

docs/bundled/ingestion.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2728,9 +2728,10 @@
27282728
"Records"
27292729
],
27302730
"x-available-languages": [
2731-
"javascript",
2731+
"csharp",
27322732
"go",
27332733
"java",
2734+
"javascript",
27342735
"php",
27352736
"python"
27362737
],

docs/bundled/search-snippets.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@
199199
"call partialUpdateObjects with createIfNotExists=true": "var response = await client.PartialUpdateObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"1\" }, { \"name\", \"Adam\" } },\n new Dictionary<string, string> { { \"objectID\", \"2\" }, { \"name\", \"Benoit\" } },\n },\n true\n);",
200200
"call partialUpdateObjects with createIfNotExists=false": "var response = await client.PartialUpdateObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"3\" }, { \"name\", \"Cyril\" } },\n new Dictionary<string, string> { { \"objectID\", \"4\" }, { \"name\", \"David\" } },\n },\n false\n);"
201201
},
202+
"partialUpdateObjectsWithTransformation": {
203+
"default": "var response = await client.PartialUpdateObjectsWithTransformationAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"1\" }, { \"name\", \"Adam\" } },\n new Dictionary<string, string> { { \"objectID\", \"2\" }, { \"name\", \"Benoit\" } },\n },\n true,\n true\n);"
204+
},
202205
"removeUserId": {
203206
"default": "var response = await client.RemoveUserIdAsync(\"uniqueID\");"
204207
},
@@ -207,6 +210,9 @@
207210
"call replaceAllObjects with partial scopes": "var response = await client.ReplaceAllObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"1\" }, { \"name\", \"Adam\" } },\n new Dictionary<string, string> { { \"objectID\", \"2\" }, { \"name\", \"Benoit\" } },\n },\n 77,\n new List<ScopeType> { Enum.Parse<ScopeType>(\"Settings\"), Enum.Parse<ScopeType>(\"Synonyms\") }\n);",
208211
"replaceAllObjects should cleanup on failure": "var response = await client.ReplaceAllObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"fine\" }, { \"body\", \"small obj\" } },\n new Dictionary<string, string>\n {\n { \"objectID\", \"toolarge\" },\n { \"body\", \"something bigger than 10KB\" },\n },\n }\n);"
209212
},
213+
"replaceAllObjectsWithTransformation": {
214+
"default": "var response = await client.ReplaceAllObjectsWithTransformationAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"1\" }, { \"name\", \"Adam\" } },\n new Dictionary<string, string> { { \"objectID\", \"2\" }, { \"name\", \"Benoit\" } },\n new Dictionary<string, string> { { \"objectID\", \"3\" }, { \"name\", \"Cyril\" } },\n new Dictionary<string, string> { { \"objectID\", \"4\" }, { \"name\", \"David\" } },\n new Dictionary<string, string> { { \"objectID\", \"5\" }, { \"name\", \"Eva\" } },\n new Dictionary<string, string> { { \"objectID\", \"6\" }, { \"name\", \"Fiona\" } },\n new Dictionary<string, string> { { \"objectID\", \"7\" }, { \"name\", \"Gael\" } },\n new Dictionary<string, string> { { \"objectID\", \"8\" }, { \"name\", \"Hugo\" } },\n new Dictionary<string, string> { { \"objectID\", \"9\" }, { \"name\", \"Igor\" } },\n new Dictionary<string, string> { { \"objectID\", \"10\" }, { \"name\", \"Julia\" } },\n },\n 3\n);"
215+
},
210216
"replaceSources": {
211217
"default": "var response = await client.ReplaceSourcesAsync(\n new List<Source>\n {\n new Source { VarSource = \"theSource\", Description = \"theDescription\" },\n }\n);"
212218
},
@@ -222,6 +228,9 @@
222228
"saveObjectsPlaylist": "var response = await client.SaveObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string>\n {\n { \"objectID\", \"1\" },\n { \"visibility\", \"public\" },\n { \"name\", \"Hot 100 Billboard Charts\" },\n { \"playlistId\", \"d3e8e8f3-0a4f-4b7d-9b6b-7e8f4e8e3a0f\" },\n { \"createdAt\", \"1500240452\" },\n },\n }\n);",
223229
"saveObjectsPublicUser": "var response = await client.SaveObjectsAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string>\n {\n { \"objectID\", \"1\" },\n { \"visibility\", \"public\" },\n { \"name\", \"Hot 100 Billboard Charts\" },\n { \"playlistId\", \"d3e8e8f3-0a4f-4b7d-9b6b-7e8f4e8e3a0f\" },\n { \"createdAt\", \"1500240452\" },\n },\n },\n false,\n 1000,\n new RequestOptionBuilder().AddExtraHeader(\"X-Algolia-User-ID\", \"*\").Build()\n);"
224230
},
231+
"saveObjectsWithTransformation": {
232+
"default": "var response = await client.SaveObjectsWithTransformationAsync(\n \"<YOUR_INDEX_NAME>\",\n new List<Object>\n {\n new Dictionary<string, string> { { \"objectID\", \"1\" }, { \"name\", \"Adam\" } },\n new Dictionary<string, string> { { \"objectID\", \"2\" }, { \"name\", \"Benoit\" } },\n },\n true\n);"
233+
},
225234
"saveRule": {
226235
"saveRule with minimal parameters": "var response = await client.SaveRuleAsync(\n \"<YOUR_INDEX_NAME>\",\n \"id1\",\n new Rule\n {\n ObjectID = \"id1\",\n Conditions = new List<Condition>\n {\n new Condition { Pattern = \"apple\", Anchoring = Enum.Parse<Anchoring>(\"Contains\") },\n },\n Consequence = new Consequence\n {\n Params = new ConsequenceParams { Filters = \"brand:xiaomi\" },\n },\n }\n);",
227236
"saveRule with all parameters": "var response = await client.SaveRuleAsync(\n \"<YOUR_INDEX_NAME>\",\n \"id1\",\n new Rule\n {\n ObjectID = \"id1\",\n Conditions = new List<Condition>\n {\n new Condition\n {\n Pattern = \"apple\",\n Anchoring = Enum.Parse<Anchoring>(\"Contains\"),\n Alternatives = false,\n Context = \"search\",\n },\n },\n Consequence = new Consequence\n {\n Params = new ConsequenceParams\n {\n Filters = \"brand:apple\",\n Query = new ConsequenceQuery(\n new ConsequenceQueryObject\n {\n Remove = new List<string> { \"algolia\" },\n Edits = new List<Edit>\n {\n new Edit\n {\n Type = Enum.Parse<EditType>(\"Remove\"),\n Delete = \"abc\",\n Insert = \"cde\",\n },\n new Edit\n {\n Type = Enum.Parse<EditType>(\"Replace\"),\n Delete = \"abc\",\n Insert = \"cde\",\n },\n },\n }\n ),\n },\n Hide = new List<ConsequenceHide> { new ConsequenceHide { ObjectID = \"321\" } },\n FilterPromotes = false,\n UserData = new Dictionary<string, string> { { \"algolia\", \"aloglia\" } },\n Promote = new List<Promote>\n {\n new Promote(new PromoteObjectID { ObjectID = \"abc\", Position = 3 }),\n new Promote(\n new PromoteObjectIDs\n {\n ObjectIDs = new List<string> { \"abc\", \"def\" },\n Position = 1,\n }\n ),\n },\n },\n Description = \"test\",\n Enabled = true,\n Validity = new List<TimeRange>\n {\n new TimeRange { From = 1656670273L, Until = 1656670277L },\n },\n },\n true\n);",

docs/bundled/search.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4253,9 +4253,10 @@
42534253
"Records"
42544254
],
42554255
"x-available-languages": [
4256-
"javascript",
4256+
"csharp",
42574257
"go",
42584258
"java",
4259+
"javascript",
42594260
"php",
42604261
"python"
42614262
],
@@ -4499,16 +4500,17 @@
44994500
"/saveObjectsWithTransformation": {
45004501
"get": {
45014502
"x-helper": true,
4503+
"tags": [
4504+
"Records"
4505+
],
45024506
"x-available-languages": [
4507+
"csharp",
45034508
"go",
45044509
"java",
45054510
"javascript",
45064511
"php",
45074512
"python"
45084513
],
4509-
"tags": [
4510-
"Records"
4511-
],
45124514
"operationId": "saveObjectsWithTransformation",
45134515
"summary": "Save objects to an Algolia index by leveraging the Transformation pipeline setup using the Push connector (https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/connectors/push)",
45144516
"description": "Helper: Similar to the `saveObjects` 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 have been passed to the client instantiation method.\n",
@@ -4782,6 +4784,7 @@
47824784
"post": {
47834785
"x-helper": true,
47844786
"x-available-languages": [
4787+
"csharp",
47854788
"go",
47864789
"java",
47874790
"javascript",

docs/snippets/csharp/src/Search.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,6 +2394,34 @@ public async Task SnippetForSearchClientPartialUpdateObjects1()
23942394
// SEPARATOR<
23952395
}
23962396

2397+
/// <summary>
2398+
/// Snippet for the PartialUpdateObjectsWithTransformation method.
2399+
///
2400+
/// call partialUpdateObjectsWithTransformation with createIfNotExists=true
2401+
/// </summary>
2402+
public async Task SnippetForSearchClientPartialUpdateObjectsWithTransformation()
2403+
{
2404+
// >SEPARATOR partialUpdateObjectsWithTransformation default
2405+
// Initialize the client
2406+
var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
2407+
2408+
// Call the API
2409+
var response = await client.PartialUpdateObjectsWithTransformationAsync(
2410+
"<YOUR_INDEX_NAME>",
2411+
new List<Object>
2412+
{
2413+
new Dictionary<string, string> { { "objectID", "1" }, { "name", "Adam" } },
2414+
new Dictionary<string, string> { { "objectID", "2" }, { "name", "Benoit" } },
2415+
},
2416+
true,
2417+
true
2418+
);
2419+
// >LOG
2420+
// print the response
2421+
Console.WriteLine(response);
2422+
// SEPARATOR<
2423+
}
2424+
23972425
/// <summary>
23982426
/// Snippet for the RemoveUserId method.
23992427
///
@@ -2506,6 +2534,41 @@ public async Task SnippetForSearchClientReplaceAllObjects2()
25062534
// SEPARATOR<
25072535
}
25082536

2537+
/// <summary>
2538+
/// Snippet for the ReplaceAllObjectsWithTransformation method.
2539+
///
2540+
/// call replaceAllObjectsWithTransformation without error
2541+
/// </summary>
2542+
public async Task SnippetForSearchClientReplaceAllObjectsWithTransformation()
2543+
{
2544+
// >SEPARATOR replaceAllObjectsWithTransformation default
2545+
// Initialize the client
2546+
var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
2547+
2548+
// Call the API
2549+
var response = await client.ReplaceAllObjectsWithTransformationAsync(
2550+
"<YOUR_INDEX_NAME>",
2551+
new List<Object>
2552+
{
2553+
new Dictionary<string, string> { { "objectID", "1" }, { "name", "Adam" } },
2554+
new Dictionary<string, string> { { "objectID", "2" }, { "name", "Benoit" } },
2555+
new Dictionary<string, string> { { "objectID", "3" }, { "name", "Cyril" } },
2556+
new Dictionary<string, string> { { "objectID", "4" }, { "name", "David" } },
2557+
new Dictionary<string, string> { { "objectID", "5" }, { "name", "Eva" } },
2558+
new Dictionary<string, string> { { "objectID", "6" }, { "name", "Fiona" } },
2559+
new Dictionary<string, string> { { "objectID", "7" }, { "name", "Gael" } },
2560+
new Dictionary<string, string> { { "objectID", "8" }, { "name", "Hugo" } },
2561+
new Dictionary<string, string> { { "objectID", "9" }, { "name", "Igor" } },
2562+
new Dictionary<string, string> { { "objectID", "10" }, { "name", "Julia" } },
2563+
},
2564+
3
2565+
);
2566+
// >LOG
2567+
// print the response
2568+
Console.WriteLine(response);
2569+
// SEPARATOR<
2570+
}
2571+
25092572
/// <summary>
25102573
/// Snippet for the ReplaceSources method.
25112574
///
@@ -2696,6 +2759,33 @@ public async Task SnippetForSearchClientSaveObjects3()
26962759
// SEPARATOR<
26972760
}
26982761

2762+
/// <summary>
2763+
/// Snippet for the SaveObjectsWithTransformation method.
2764+
///
2765+
/// call saveObjectsWithTransformation without error
2766+
/// </summary>
2767+
public async Task SnippetForSearchClientSaveObjectsWithTransformation()
2768+
{
2769+
// >SEPARATOR saveObjectsWithTransformation default
2770+
// Initialize the client
2771+
var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
2772+
2773+
// Call the API
2774+
var response = await client.SaveObjectsWithTransformationAsync(
2775+
"<YOUR_INDEX_NAME>",
2776+
new List<Object>
2777+
{
2778+
new Dictionary<string, string> { { "objectID", "1" }, { "name", "Adam" } },
2779+
new Dictionary<string, string> { { "objectID", "2" }, { "name", "Benoit" } },
2780+
},
2781+
true
2782+
);
2783+
// >LOG
2784+
// print the response
2785+
Console.WriteLine(response);
2786+
// SEPARATOR<
2787+
}
2788+
26992789
/// <summary>
27002790
/// Snippet for the SaveRule method.
27012791
///

specs/bundled/ingestion.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1860,9 +1860,10 @@ paths:
18601860
tags:
18611861
- ingestion
18621862
x-available-languages:
1863-
- javascript
1863+
- csharp
18641864
- go
18651865
- java
1866+
- javascript
18661867
- php
18671868
- python
18681869
operationId: chunkedPush

0 commit comments

Comments
 (0)