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));
            }
        }
Beispiel #2
0
        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);
        }