5 min read
Building Search Solutions with Azure Cognitive Search
Azure Cognitive Search provides cloud search capabilities with built-in AI enrichment. Whether you are building e-commerce search, document search, or knowledge mining solutions, Cognitive Search offers the features you need. Here is how to get started.
Creating a Search Service
# Create a search service
az search service create \
--name search-myapp-2020 \
--resource-group rg-search \
--location australiaeast \
--sku Standard \
--partition-count 1 \
--replica-count 1
# Get the admin key
az search admin-key show \
--service-name search-myapp-2020 \
--resource-group rg-search
Setting Up with .NET SDK
dotnet add package Azure.Search.Documents
Creating an Index
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
public class SearchIndexManager
{
private readonly SearchIndexClient _indexClient;
public SearchIndexManager(string endpoint, string adminKey)
{
var credential = new AzureKeyCredential(adminKey);
_indexClient = new SearchIndexClient(new Uri(endpoint), credential);
}
public async Task CreateProductIndexAsync()
{
var index = new SearchIndex("products")
{
Fields =
{
new SimpleField("id", SearchFieldDataType.String) { IsKey = true },
new SearchableField("name") { IsFilterable = true, IsSortable = true },
new SearchableField("description") { AnalyzerName = LexicalAnalyzerName.EnMicrosoft },
new SearchableField("category") { IsFilterable = true, IsFacetable = true },
new SearchableField("brand") { IsFilterable = true, IsFacetable = true },
new SimpleField("price", SearchFieldDataType.Double) { IsFilterable = true, IsSortable = true, IsFacetable = true },
new SimpleField("rating", SearchFieldDataType.Double) { IsFilterable = true, IsSortable = true },
new SimpleField("inStock", SearchFieldDataType.Boolean) { IsFilterable = true },
new SearchableField("tags", collection: true) { IsFilterable = true, IsFacetable = true },
new SimpleField("lastUpdated", SearchFieldDataType.DateTimeOffset) { IsFilterable = true, IsSortable = true }
},
Suggesters =
{
new SearchSuggester("sg", "name", "category", "brand")
},
ScoringProfiles =
{
new ScoringProfile("boost-rating")
{
FunctionAggregation = ScoringFunctionAggregation.Sum,
Functions =
{
new MagnitudeScoringFunction(
fieldName: "rating",
boost: 2,
parameters: new MagnitudeScoringParameters(0, 5)
{
ShouldBoostBeyondRangeByConstant = true
})
}
}
}
};
await _indexClient.CreateOrUpdateIndexAsync(index);
}
}
Indexing Documents
public class ProductIndexer
{
private readonly SearchClient _searchClient;
public ProductIndexer(string endpoint, string adminKey, string indexName)
{
var credential = new AzureKeyCredential(adminKey);
_searchClient = new SearchClient(new Uri(endpoint), indexName, credential);
}
public async Task IndexProductsAsync(IEnumerable<Product> products)
{
var documents = products.Select(p => new
{
id = p.Id,
name = p.Name,
description = p.Description,
category = p.Category,
brand = p.Brand,
price = p.Price,
rating = p.Rating,
inStock = p.InStock,
tags = p.Tags,
lastUpdated = p.LastUpdated
});
var batch = IndexDocumentsBatch.Upload(documents);
var result = await _searchClient.IndexDocumentsAsync(batch);
Console.WriteLine($"Indexed {result.Value.Results.Count} documents");
foreach (var r in result.Value.Results.Where(r => !r.Succeeded))
{
Console.WriteLine($"Failed to index document {r.Key}: {r.ErrorMessage}");
}
}
public async Task DeleteProductAsync(string productId)
{
var batch = IndexDocumentsBatch.Delete("id", new[] { productId });
await _searchClient.IndexDocumentsAsync(batch);
}
}
Searching Documents
public class ProductSearchService
{
private readonly SearchClient _searchClient;
public ProductSearchService(string endpoint, string queryKey, string indexName)
{
var credential = new AzureKeyCredential(queryKey);
_searchClient = new SearchClient(new Uri(endpoint), indexName, credential);
}
public async Task<SearchResult> SearchProductsAsync(SearchRequest request)
{
var options = new SearchOptions
{
Filter = BuildFilter(request),
OrderBy = { request.SortBy ?? "search.score() desc" },
Skip = (request.Page - 1) * request.PageSize,
Size = request.PageSize,
IncludeTotalCount = true,
Facets = { "category,count:10", "brand,count:10", "price,values:50|100|200|500" },
HighlightFields = { "name", "description" },
ScoringProfile = "boost-rating"
};
// Select specific fields
options.Select.Add("id");
options.Select.Add("name");
options.Select.Add("description");
options.Select.Add("price");
options.Select.Add("rating");
options.Select.Add("category");
var response = await _searchClient.SearchAsync<ProductDocument>(request.Query, options);
var result = new SearchResult
{
TotalCount = response.Value.TotalCount ?? 0,
Facets = ExtractFacets(response.Value.Facets),
Products = new List<ProductSearchResult>()
};
await foreach (var searchResult in response.Value.GetResultsAsync())
{
result.Products.Add(new ProductSearchResult
{
Id = searchResult.Document.Id,
Name = searchResult.Document.Name,
Description = searchResult.Document.Description,
Price = searchResult.Document.Price,
Rating = searchResult.Document.Rating,
Score = searchResult.Score ?? 0,
Highlights = searchResult.Highlights
});
}
return result;
}
private string BuildFilter(SearchRequest request)
{
var filters = new List<string>();
if (!string.IsNullOrEmpty(request.Category))
filters.Add($"category eq '{request.Category}'");
if (!string.IsNullOrEmpty(request.Brand))
filters.Add($"brand eq '{request.Brand}'");
if (request.MinPrice.HasValue)
filters.Add($"price ge {request.MinPrice}");
if (request.MaxPrice.HasValue)
filters.Add($"price le {request.MaxPrice}");
if (request.InStockOnly)
filters.Add("inStock eq true");
return filters.Any() ? string.Join(" and ", filters) : null;
}
}
Autocomplete and Suggestions
public async Task<List<string>> GetSuggestionsAsync(string searchText)
{
var options = new SuggestOptions
{
UseFuzzyMatching = true,
Size = 5
};
var response = await _searchClient.SuggestAsync<ProductDocument>(searchText, "sg", options);
return response.Value.Results.Select(r => r.Text).ToList();
}
public async Task<List<string>> GetAutocompleteAsync(string searchText)
{
var options = new AutocompleteOptions
{
Mode = AutocompleteMode.OneTermWithContext,
Size = 5
};
var response = await _searchClient.AutocompleteAsync(searchText, "sg", options);
return response.Value.Results.Select(r => r.Text).ToList();
}
AI Enrichment with Skillsets
Create a skillset for cognitive enrichment:
public async Task CreateSkillsetAsync()
{
var skillset = new SearchIndexerSkillset("document-skillset", new List<SearchIndexerSkill>
{
new OcrSkill(
inputs: new[] { new InputFieldMappingEntry("image") { Source = "/document/normalized_images/*" } },
outputs: new[] { new OutputFieldMappingEntry("text") { TargetName = "ocrText" } })
{
DefaultLanguageCode = OcrSkillLanguage.En
},
new KeyPhraseExtractionSkill(
inputs: new[] { new InputFieldMappingEntry("text") { Source = "/document/content" } },
outputs: new[] { new OutputFieldMappingEntry("keyPhrases") { TargetName = "keyPhrases" } })
{
DefaultLanguageCode = KeyPhraseExtractionSkillLanguage.En
},
new EntityRecognitionSkill(
inputs: new[] { new InputFieldMappingEntry("text") { Source = "/document/content" } },
outputs: new[] { new OutputFieldMappingEntry("organizations") { TargetName = "organizations" } })
{
Categories = { EntityCategory.Organization, EntityCategory.Person, EntityCategory.Location }
}
});
var indexerClient = new SearchIndexerClient(new Uri(endpoint), credential);
await indexerClient.CreateOrUpdateSkillsetAsync(skillset);
}
Azure Cognitive Search provides powerful search capabilities that can transform how users find and discover content in your applications.