Skip to main content

Optimizely

Integrating Optimizely CMS with Azure AI Search – A Game-Changer for Site Search

Programming Background With Person Working With Codes Computer

Want to elevate your Optimizely PaaS CMS site’s search capabilities? Azure AI Search could be just the tool you need! In this blog, I’ll discuss how to connect your CMS with Microsoft’s advanced AI-driven search platform to create fast, smart search experiences that surpass regular keyword searches. Optimizely Azure Ai Search

What is Azure AI Search?

Azure AI Search is Microsoft’s cloud-based search service powered by AI. It enables you to index, search, and analyze extensive amounts of content utilizing full-text searches, faceted navigation, and machine-learning features (such as language comprehension and semantic search).

Why it’s great

  • Super fast and scalable search experiences.
  • Built-in AI for enhanced relevance.
  • Smooth integration with other Azure services.

In short: it’s a smart search made user-friendly.

Advantages of Integrating with Optimizely CMS

Before we get into the benefits, let’s take a moment to consider how Azure AI Search compares to Optimizely’s native search functionalities. Optimizely Search (which relies on Lucene or Find/Search & Navigation) works well for straightforward keyword searches and basic filters, and it’s closely tied to the CMS. However, it doesn’t offer the advanced AI features, scalability, or flexibility that Azure provides right off the bat. Azure AI Search enriches the search experience with functionalities like semantic search, cognitive enhancements, and external data indexing, making it perfect for enterprise-level sites with intricate search requirements.

Here’s why merging these two solutions is beneficial:

  • Improved search experiences with AI-based relevance.
  • Scalable and dependable – allow Azure to manage the heavy lifting.
  • Customized content indexing from your CMS using APIs or jobs.
  • Advanced options such as filtering, faceting, auto-complete, and more.

Get Started with Azure AI Search

To set up Azure AI Search, just follow these steps:

  1. Log in to the Azure Portal and look for AI Search.
  2. Click ‘Create’ to configure the following:
    • Name
    • Resource Group
    • Pricing Tier (including a free tier!)
    • Region

Once created, make sure to note down the Search Service Name and Admin API Key – you’ll need these to send and retrieve

Custom Scheduled Job to Sync Updated Content with Azure AI Search Using ServiceAPI

By utilizing the Optimizely ServiceAPI, we can effectively get updated content and synchronize it with Azure AI Search. This process avoids the need to re-index the entire site, which helps boost performance.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[ScheduledPlugIn(DisplayName = "Sync Updated Content to Azure Search")]
publicclass AzureSearchJob : ScheduledJobBase
{
privatereadonly HttpClient _httpClient;
privatereadonlystring _serviceApiBaseUrl = "https://yourwebsite.com/episerverapi/content/";
publicAzureSearchJob()
{
_httpClient = newHttpClient();
IsStoppable = true;
}
publicoverridestringExecute()
{
// Step 1: Get content updated in the last 24 hours
var yesterday = DateTime.UtcNow.AddDays(-1).ToString("o");
var contentApiUrl = $"{_serviceApiBaseUrl}?updatedAfter={Uri.EscapeDataString(yesterday)}";
var response = _httpClient.GetAsync(contentApiUrl).Result;
if(!response.IsSuccessStatusCode)
return"Failed to fetch updated content from ServiceAPI.";
var contentJson = response.Content.ReadAsStringAsync().Result;
var documents = JsonSerializer.Deserialize<JsonElement>(contentJson).EnumerateArray()
.Select(content =>new Dictionary<string, object>
{
["id"] = content.GetProperty("ContentGuid").ToString(),
["name"] = content.GetProperty("Name").GetString(),
["content"] = content.GetProperty("ContentLink").GetRawText(),
["type"] = content.GetProperty("ContentTypeName").GetString()
}).ToList();
// Step 2: Push to Azure AI Search
var json = JsonSerializer.Serialize(new{value = documents });
var request = newHttpRequestMessage(HttpMethod.Post, "https://servicename.search.windows.net/indexes/<index-name>/docs/index?api-version=2021-04-30-Preview")
{
Content = newStringContent(json, Encoding.UTF8, "application/json")
};
request.Headers.Add("api-key", "<your-admin-key>");
var result = _httpClient.SendAsync(request).Result;
return result.IsSuccessStatusCode ? "Success" : "Failed to index in Azure Search.";
}
}
[ScheduledPlugIn(DisplayName = "Sync Updated Content to Azure Search")] public class AzureSearchJob : ScheduledJobBase { private readonly HttpClient _httpClient; private readonly string _serviceApiBaseUrl = "https://yourwebsite.com/episerverapi/content/"; public AzureSearchJob() { _httpClient = new HttpClient(); IsStoppable = true; } public override string Execute() { // Step 1: Get content updated in the last 24 hours var yesterday = DateTime.UtcNow.AddDays(-1).ToString("o"); var contentApiUrl = $"{_serviceApiBaseUrl}?updatedAfter={Uri.EscapeDataString(yesterday)}"; var response = _httpClient.GetAsync(contentApiUrl).Result; if (!response.IsSuccessStatusCode) return "Failed to fetch updated content from ServiceAPI."; var contentJson = response.Content.ReadAsStringAsync().Result; var documents = JsonSerializer.Deserialize<JsonElement>(contentJson).EnumerateArray() .Select(content => new Dictionary<string, object> { ["id"] = content.GetProperty("ContentGuid").ToString(), ["name"] = content.GetProperty("Name").GetString(), ["content"] = content.GetProperty("ContentLink").GetRawText(), ["type"] = content.GetProperty("ContentTypeName").GetString() }).ToList(); // Step 2: Push to Azure AI Search var json = JsonSerializer.Serialize(new { value = documents }); var request = new HttpRequestMessage(HttpMethod.Post, "https://servicename.search.windows.net/indexes/<index-name>/docs/index?api-version=2021-04-30-Preview") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; request.Headers.Add("api-key", "<your-admin-key>"); var result = _httpClient.SendAsync(request).Result; return result.IsSuccessStatusCode ? "Success" : "Failed to index in Azure Search."; } }
[ScheduledPlugIn(DisplayName = "Sync Updated Content to Azure Search")]
public class AzureSearchJob : ScheduledJobBase
{
    private readonly HttpClient _httpClient;
    private readonly string _serviceApiBaseUrl = "https://yourwebsite.com/episerverapi/content/";

    public AzureSearchJob()
    {
        _httpClient = new HttpClient();
        IsStoppable = true;
    }

    public override string Execute()
    {
        // Step 1: Get content updated in the last 24 hours
        var yesterday = DateTime.UtcNow.AddDays(-1).ToString("o");
        var contentApiUrl = $"{_serviceApiBaseUrl}?updatedAfter={Uri.EscapeDataString(yesterday)}";

        var response = _httpClient.GetAsync(contentApiUrl).Result;
        if (!response.IsSuccessStatusCode)
            return "Failed to fetch updated content from ServiceAPI.";

        var contentJson = response.Content.ReadAsStringAsync().Result;
        var documents = JsonSerializer.Deserialize<JsonElement>(contentJson).EnumerateArray()
            .Select(content => new Dictionary<string, object>
            {
                ["id"] = content.GetProperty("ContentGuid").ToString(),
                ["name"] = content.GetProperty("Name").GetString(),
                ["content"] = content.GetProperty("ContentLink").GetRawText(),
                ["type"] = content.GetProperty("ContentTypeName").GetString()
            }).ToList();

        // Step 2: Push to Azure AI Search
        var json = JsonSerializer.Serialize(new { value = documents });
        var request = new HttpRequestMessage(HttpMethod.Post, "https://servicename.search.windows.net/indexes/<index-name>/docs/index?api-version=2021-04-30-Preview")
        {
            Content = new StringContent(json, Encoding.UTF8, "application/json")
        };
        request.Headers.Add("api-key", "<your-admin-key>");

        var result = _httpClient.SendAsync(request).Result;
        return result.IsSuccessStatusCode ? "Success" : "Failed to index in Azure Search.";
    }
}

You can filter and transform the ServiceAPI response further to match your index schema.

Custom Page Type and Controller/View to Query Azure Search

Create a new page type to serve as a Search Results page.

Search Page Type

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[ContentType(DisplayName = "Search Results Page", GUID = "3C918F3E-D82B-480B-9FD8-A3A1DA3ECB1B", Description = "Search using Azure Search")]
publicclass AzureSearchPage : PageData
{
[Display(Name = "Search Placeholder")]
publicvirtualstring PlaceholderText {get; set; }
}
[ContentType(DisplayName = "Search Results Page", GUID = "3C918F3E-D82B-480B-9FD8-A3A1DA3ECB1B", Description = "Search using Azure Search")] public class AzureSearchPage : PageData { [Display(Name = "Search Placeholder")] public virtual string PlaceholderText { get; set; } }
[ContentType(DisplayName = "Search Results Page", GUID = "3C918F3E-D82B-480B-9FD8-A3A1DA3ECB1B", Description = "Search using Azure Search")]
public class AzureSearchPage : PageData
{
    [Display(Name = "Search Placeholder")]
    public virtual string PlaceholderText { get; set; }
}

Page Controller

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
publicclass AzureSearchPageController : PageController<AzureSearchPage>
{
public ActionResult Index(AzureSearchPage currentPage, string q = "")
{
var results = new List<string>();
if(!string.IsNullOrEmpty(q))
{
var url = $"https://<search-service>.search.windows.net/indexes/<index-name>/docs?api-version=2021-04-30-Preview&search={q}";
usingvar client = newHttpClient();
client.DefaultRequestHeaders.Add("api-key", "<your-query-key>");
var response = client.GetStringAsync(url).Result;
var doc = JsonDocument.Parse(response);
results = doc.RootElement.GetProperty("value")
.EnumerateArray()
.Select(x => x.GetProperty("name").GetString())
.ToList();
}
ViewBag.Results = results;
ViewBag.Query = q;
returnView(currentPage);
}
}
public class AzureSearchPageController : PageController<AzureSearchPage> { public ActionResult Index(AzureSearchPage currentPage, string q = "") { var results = new List<string>(); if (!string.IsNullOrEmpty(q)) { var url = $"https://<search-service>.search.windows.net/indexes/<index-name>/docs?api-version=2021-04-30-Preview&search={q}"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("api-key", "<your-query-key>"); var response = client.GetStringAsync(url).Result; var doc = JsonDocument.Parse(response); results = doc.RootElement.GetProperty("value") .EnumerateArray() .Select(x => x.GetProperty("name").GetString()) .ToList(); } ViewBag.Results = results; ViewBag.Query = q; return View(currentPage); } }
public class AzureSearchPageController : PageController<AzureSearchPage>
{
    public ActionResult Index(AzureSearchPage currentPage, string q = "")
    {
        var results = new List<string>();

        if (!string.IsNullOrEmpty(q))
        {
            var url = $"https://<search-service>.search.windows.net/indexes/<index-name>/docs?api-version=2021-04-30-Preview&search={q}";
            using var client = new HttpClient();
            client.DefaultRequestHeaders.Add("api-key", "<your-query-key>");
            var response = client.GetStringAsync(url).Result;

            var doc = JsonDocument.Parse(response);
            results = doc.RootElement.GetProperty("value")
                .EnumerateArray()
                .Select(x => x.GetProperty("name").GetString())
                .ToList();
        }

        ViewBag.Results = results;
        ViewBag.Query = q;
        return View(currentPage);
    }
}

Search Page View

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@model AzureSearchPage
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Search Results</h1>
<form method="get">
<input type="text" name="q"value="@ViewBag.Query" placeholder="@Model.PlaceholderText" />
<button type="submit">Search</button>
</form>
<ul>
@foreach(var result in ViewBag.Resultsas List<string>)
{
<li>@result</li>
}
</ul>
@model AzureSearchPage @{ Layout = "~/Views/Shared/_Layout.cshtml"; } <h1>Search Results</h1> <form method="get"> <input type="text" name="q" value="@ViewBag.Query" placeholder="@Model.PlaceholderText" /> <button type="submit">Search</button> </form> <ul> @foreach (var result in ViewBag.Results as List<string>) { <li>@result</li> } </ul>
@model AzureSearchPage
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Search Results</h1>
<form method="get">
    <input type="text" name="q" value="@ViewBag.Query" placeholder="@Model.PlaceholderText" />
    <button type="submit">Search</button>
</form>

<ul>
@foreach (var result in ViewBag.Results as List<string>)
{
    <li>@result</li>
}
</ul>

Optimizely CMS / Azure AI Search Advanced Use Cases

  • Semantic Search: Let Azure understand intent, not just keywords.
  • Auto-complete & Suggestions: Hook into search-as-you-type features.
  • Faceted Navigation: Create filters by category, tags, etc.
  • AI Enrichment: Use Azure’s skillsets to extract metadata, and analyse images, or OCR PDFs.
  • Multilingual Search: Azure supports search across multiple languages out of the box.

Summary

Integrating Azure AI Search with Optimizely CMS can truly take your site search from basic to brilliant. With a bit of setup and some clean code, you’re empowering users with fast, smart, and scalable content discovery.

This blog is also published here

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Naveed Ul-Haq

Naveed is a UK-based technical architect and technology evangelist passionate about crafting scalable digital experiences. As an Optimizely MVP and Subject Matter Expert in Content Cloud and Commerce Cloud, he specializes in building robust .NET-based CMS and eCommerce solutions. His technical toolkit includes .NET Core, DevOps practices, and cloud computing—particularly within Microsoft Azure. Naveed holds multiple certifications: Certified Software Architect, Optimizely Content Cloud Developer, Optimizely Commerce Cloud Developer, Optimizely B2B Commerce Developer, and Microsoft Certified: Azure Developer Associate. Outside of work, Naveed enjoys spending quality time with his family and diving into a good book.

More from this Author

Categories
Follow Us