public async Task <TResult> UpdateItemAsync <TResult>(string indexName, IDictionary <string, object> item, Func <TResult> onSuccess, Func <string, TResult> onMissingField, Func <string, TResult> onInvalidPropertyValue, Func <Exception, TResult> onFailure) { var indexClient = searchClient.Indexes.GetClient(indexName); if (default(SearchIndexClient) == indexClient) { throw new InvalidOperationException("Index does not exist: " + indexName); } var doc = new Microsoft.Azure.Search.Models.Document(); foreach (var itemKvp in item) { doc.Add(itemKvp.Key, itemKvp.Value); } try { var batch = IndexBatch.Upload(doc.AsEnumerable()); await indexClient.Documents.IndexAsync(batch); return(onSuccess()); } catch (IndexBatchException ex) { return(onFailure(ex)); } catch (Microsoft.Rest.Azure.CloudException ex) { if (ex.Response.StatusCode == HttpStatusCode.BadRequest) { var match = System.Text.RegularExpressions.Regex.Match(ex.Response.Content, "The property '(\\w+)' does not exist on type"); if (match.Success && match.Groups.Count >= 2) { return(onMissingField(match.Groups[1].Value)); } //return onInvalidPropertyValue(null); } return(onFailure(ex)); } catch (Exception ex) { return(onFailure(ex)); } }
protected override async Task <IEnumerable <IDocument> > ExecuteConfigAsync(IDocument input, IExecutionContext context, IMetadata values) { string searchServiceName = values.GetString(SearchServiceName) ?? throw new ExecutionException("Invalid search service name"); string indexName = values.GetString(IndexName) ?? throw new ExecutionException("Invalid search index name"); string apiKey = values.GetString(ApiKey) ?? throw new ExecutionException("Invalid search API key"); IList <Field> fields = values.GetList <Field>(Fields)?.ToList() ?? throw new ExecutionException("Invalid search fields"); SearchServiceClient client = new SearchServiceClient(searchServiceName, new SearchCredentials(apiKey)); // Delete the index if it currently exists (recreating is the easiest way to update it) CorsOptions corsOptions = null; if (await client.Indexes.ExistsAsync(indexName, null, context.CancellationToken)) { // Get the CORS options because we'll need to recreate those Microsoft.Azure.Search.Models.Index existingIndex = await client.Indexes.GetAsync(indexName, null, context.CancellationToken); corsOptions = existingIndex.CorsOptions; // Delete the existing index context.LogDebug($"Deleting existing search index {indexName}"); await client.Indexes.DeleteAsync(indexName, null, null, context.CancellationToken); } // Create the index Microsoft.Azure.Search.Models.Index index = new Microsoft.Azure.Search.Models.Index { Name = indexName, Fields = fields, CorsOptions = corsOptions }; context.LogDebug($"Creating search index {indexName}"); await client.Indexes.CreateAsync(index, null, context.CancellationToken); // Upload the documents to the search index in batches context.LogDebug($"Uploading {context.Inputs.Length} documents to search index {indexName}..."); ISearchIndexClient indexClient = client.Indexes.GetClient(indexName); int start = 0; do { // Create the dynamic search documents and batch IndexAction <Microsoft.Azure.Search.Models.Document>[] indexActions = context.Inputs .Skip(start) .Take(BatchSize) .Select(doc => { Microsoft.Azure.Search.Models.Document searchDocument = new Microsoft.Azure.Search.Models.Document(); foreach (Field field in fields) { if (doc.ContainsKey(field.Name)) { searchDocument[field.Name] = doc.Get(field.Name); } } return(IndexAction.Upload(searchDocument)); }) .ToArray(); IndexBatch <Microsoft.Azure.Search.Models.Document> indexBatch = IndexBatch.New(indexActions); // Upload the batch with exponential retry for failures await Policy .Handle <IndexBatchException>() .WaitAndRetryAsync( 5, attempt => { context.LogWarning($"Failure while uploading batch {(start / BatchSize) + 1}, retry number {attempt}"); return(TimeSpan.FromSeconds(Math.Pow(2, attempt))); }, (ex, _) => indexBatch = ((IndexBatchException)ex).FindFailedActionsToRetry(indexBatch, fields.Single(x => x.IsKey == true).Name)) .ExecuteAsync(async ct => await indexClient.Documents.IndexAsync(indexBatch, null, ct), context.CancellationToken); context.LogDebug($"Uploaded {start + indexActions.Length} documents to search index {indexName}"); start += 1000; }while (start < context.Inputs.Length); return(context.Inputs); }