<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Rajveer Singh</title><link href="http://world.optimizely.com" /><updated>2025-08-04T06:22:37.0000000Z</updated><id>https://world.optimizely.com/blogs/rajveer-singh/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Understanding Query Execution in Optimizely Search &amp; Navigation</title><link href="https://world.optimizely.com/blogs/rajveer-singh/dates/2025/8/understanding-query-execution-in-optimizely-search--navigation/" /><id>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;A Real-Life Example of How Search Works Behind the Scenes&lt;/h3&gt;
&lt;p&gt;In today&amp;rsquo;s digital landscape, delivering accurate and relevant search results is critical to user experience. Optimizely Search &amp;amp; Navigation (formerly Episerver Find) offers a powerful search engine that enhances queries using features like &lt;strong&gt;synonyms&lt;/strong&gt;, &lt;strong&gt;fuzzy matching&lt;/strong&gt;, &lt;strong&gt;boosting&lt;/strong&gt;, and &lt;strong&gt;best bets&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s walk through how a search query is executed using a real-life example.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Real-Life Scenario&lt;/h2&gt;
&lt;p&gt;Imagine a user visits an e-commerce site and searches for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&quot;iphon&quot;&lt;/strong&gt; (a typo for &quot;iPhone&quot;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Despite the typo, the user expects to see relevant results like &quot;iPhone 15 Pro&quot;, &quot;Apple iPhone Case&quot;, etc. Here&#39;s how Optimizely processes this query:&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Query Execution Flow&lt;/h2&gt;
&lt;h3&gt;1️⃣ &lt;strong&gt;Synonym Expansion&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before the query hits the search engine, Optimizely checks for synonyms. If &quot;iPhone&quot; has synonyms like &quot;smartphone&quot; or &quot;mobile&quot;, the query is expanded:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Query becomes:&lt;/strong&gt; &lt;code&gt;&quot;iphon OR iPhone OR smartphone OR mobile&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This increases the chances of matching relevant content.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2️⃣ &lt;strong&gt;Fuzzy Matching&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Next, fuzzy logic is applied to handle typos. The term &lt;code&gt;&quot;iphon&quot;&lt;/code&gt; is recognized as a close match to &lt;code&gt;&quot;iPhone&quot;&lt;/code&gt; using Levenshtein distance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Match found:&lt;/strong&gt; &lt;code&gt;&quot;iPhone&quot;&lt;/code&gt; despite the typo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;3️⃣ &lt;strong&gt;Document Retrieval&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The search engine now retrieves all documents that match the expanded and fuzzy-corrected query terms.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Results include:&lt;/strong&gt; iPhone 15 Pro, iPhone Cases, Apple Accessories, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;4️⃣ &lt;strong&gt;Boosting&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Boosting adjusts the relevance score of results. For example, if Apple products are boosted:&lt;/p&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-CSharp&quot;&gt;.BoostMatching(x =&amp;gt; x.Brand, &quot;Apple&quot;, 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!--ScriptorEndFragment--&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Apple products appear higher&lt;/strong&gt; in the results than others.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;5️⃣ &lt;strong&gt;Best Bets&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If the admin has configured a best bet for the term &quot;iPhone&quot;, such as the &quot;iPhone 15 Pro&quot; product page, it is promoted to the top of the results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Best Bet Result:&lt;/strong&gt; iPhone 15 Pro appears first, regardless of score.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;6️⃣ &lt;strong&gt;Final Results&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The final result list is assembled, combining all the above enhancements and User sees accurate, relevant, and prioritized results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Synonym-expanded matches&lt;/li&gt;
&lt;li&gt;Fuzzy-corrected terms&lt;/li&gt;
&lt;li&gt;Boosted relevance scores&lt;/li&gt;
&lt;li&gt;Best bet promotions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;This layered approach ensures that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users get results even with typos.&lt;/li&gt;
&lt;li&gt;Related terms are intelligently matched.&lt;/li&gt;
&lt;li&gt;Important content is prioritized.&lt;/li&gt;
&lt;li&gt;Admins can manually promote key pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Summary Diagram&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;User Query &amp;rarr; Synonym Expansion &amp;rarr; Fuzzy Matching &amp;rarr; Document Retrieval &amp;rarr; Boosting &amp;rarr; Best Bets &amp;rarr; Final Results
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2025-08-04T06:22:37.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Environment-specific badges in the Optimizely CMS </title><link href="https://world.optimizely.com/blogs/rajveer-singh/dates/2025/7/environment-specific-badges-in-the-optimizely-cms-/" /><id>&lt;p&gt;&lt;img src=&quot;/link/d8254a55289b4471880079eb203cccf8.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Step 1: Add Environment Info to the UI&lt;/h3&gt;
&lt;p&gt;Create a custom &lt;code&gt;UIDescriptor&lt;/code&gt; and a &lt;code&gt;ComponentDefinition&lt;/code&gt; to inject a badge into the CMS UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;csharp language-csharp&quot;&gt;using EPiServer.Shell;
using EPiServer.Shell.ViewComposition;

[UIDescriptorRegistration]
public class EnvironmentBadgeUIDescriptor : UIDescriptor
{
    public EnvironmentBadgeUIDescriptor()
        : base(&quot;environment-badge&quot;)
    {
        DisabledViews = new string[] { };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;Step 2: Add a Client Resource&lt;/h3&gt;
&lt;p&gt;Create a JavaScript file that will render the badge in the CMS UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;javascript language-javascript&quot;&gt;define([
    &quot;dojo/_base/declare&quot;,
    &quot;epi/_Module&quot;,
    &quot;dojo/dom-construct&quot;,
    &quot;dojo/domReady!&quot;
], function (declare, _Module, domConstruct) {
    return declare([_Module], {
        initialize: function () {
            this.inherited(arguments);

            var env = window.cmsEnvironment || &quot;Production&quot;;
            var color = {
                Development: &quot;#007bff&quot;,
                Staging: &quot;#ffc107&quot;,
                Production: &quot;#28a745&quot;
            }[env] || &quot;#6c757d&quot;;

            var badge = domConstruct.create(&quot;div&quot;, {
                innerHTML: env,
                style: `
                    position: fixed;
                    top: 10px;
                    right: 10px;
                    background-color: ${color};
                    color: white;
                    padding: 5px 10px;
                    font-weight: bold;
                    border-radius: 4px;
                    z-index: 9999;
                `
            }, document.body);
        }
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;Step 3: Register the Module in &lt;code&gt;module.config&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;xml language-xml&quot;&gt;&amp;lt;module&amp;gt;
  &amp;lt;clientModule initializer=&quot;environment-badge&quot;&amp;gt;
    &amp;lt;requiredResources&amp;gt;
      &amp;lt;add name=&quot;epi.shell&quot; /&amp;gt;
    &amp;lt;/requiredResources&amp;gt;
  &amp;lt;/clientModule&amp;gt;
&amp;lt;/module&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3&gt;Step 4: Set the Environment in Razor or Layout&lt;/h3&gt;
&lt;p&gt;In your &lt;code&gt;_Layout.cshtml&lt;/code&gt; or a shared Razor view:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;csharp language-csharp&quot;&gt;&amp;lt;script&amp;gt;
    window.cmsEnvironment = &#39;@Environment.GetEnvironmentVariable(&quot;ASPNETCORE_ENVIRONMENT&quot;)&#39;;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;When editors log into the CMS, they&amp;rsquo;ll see a badge in the top-right corner showing the current environment, color-coded for clarity.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2025-07-01T07:09:44.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Boosting by published date with Relevance</title><link href="https://world.optimizely.com/blogs/rajveer-singh/dates/2025/7/boosting-by-published-date-with-relevance/" /><id>&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; To ensure that the latest published or last updated content is ranked higher in your search results, you can modify your query to include a&amp;nbsp;&lt;strong&gt;boost based on recency&lt;/strong&gt;. This typically involves boosting content based on a date field such as&amp;nbsp;&lt;code&gt;PublishedDate&lt;/code&gt;&amp;nbsp;or&amp;nbsp;&lt;code&gt;LastUpdated&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Here&amp;rsquo;s how you can update your query to prioritize recent content while still considering relevance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;csharp language-csharp&quot;&gt;var result = query.ApplyBestBets()
    .BoostMatching(x =&amp;gt; ((IContent)x).SearchTitle().MatchCaseInsensitive(contentFilter.SearchQuery), 1.5)
    .OrderByDescending(x =&amp;gt; ((IContent)x).LastUpdated) // Boost by recency
    .Take(pageSize)
    .Skip((pageNo - 1) * pageSize)
    .StaticallyCacheFor(TimeSpan.FromSeconds(CacheSpan))
    .GetResult(hitSpec);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Explanation:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OrderByDescending(x =&amp;gt; ((IContent)x).LastUpdated)&lt;/code&gt; ensures that the most recently updated content appears first.&lt;/li&gt;
&lt;li&gt;If you want to balance &lt;strong&gt;relevance and recency&lt;/strong&gt;, you might consider a &lt;strong&gt;custom scoring function&lt;/strong&gt; or a &lt;strong&gt;composite boost&lt;/strong&gt; that combines both.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;------------&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Also, we can implementing a more advanced scoring strategy that balances relevance and freshness as below:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;To implement a &lt;strong&gt;balanced scoring strategy&lt;/strong&gt; that considers both &lt;strong&gt;relevance&lt;/strong&gt; and &lt;strong&gt;recency&lt;/strong&gt;, you can use a &lt;strong&gt;custom boost function&lt;/strong&gt;. This approach allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Boost content that matches the search query (relevance).&lt;/li&gt;
&lt;li&gt;Boost content that is recently published or updated (recency).&lt;/li&gt;
&lt;li&gt;Combine both into a composite score.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s how you can update your query using a &lt;strong&gt;custom boost expression&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;csharp language-csharp&quot;&gt;var result = query.ApplyBestBets()
    .BoostMatching(x =&amp;gt; ((IContent)x).SearchTitle().MatchCaseInsensitive(contentFilter.SearchQuery), 1.5)
    .OrderByDescending(x =&amp;gt; 
        ((IContent)x).SearchTitle().MatchCaseInsensitive(contentFilter.SearchQuery) ? 1 : 0 + 
        GetRecencyScore(((IContent)x).LastUpdated)
    )
    .Take(pageSize)
    .Skip((pageNo - 1) * pageSize)
    .StaticallyCacheFor(TimeSpan.FromSeconds(CacheSpan))
    .GetResult(hitSpec);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;You&amp;rsquo;ll need to define &lt;code&gt;GetRecencyScore&lt;/code&gt; like this:&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;csharp language-csharp&quot;&gt;private double GetRecencyScore(DateTime lastUpdated)
{
    var daysOld = (DateTime.UtcNow - lastUpdated).TotalDays;

    // Example scoring: newer content gets higher score
    if (daysOld &amp;lt;= 1) return 1.0;
    if (daysOld &amp;lt;= 7) return 0.8;
    if (daysOld &amp;lt;= 30) return 0.5;
    if (daysOld &amp;lt;= 90) return 0.3;
    return 0.1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Why this works:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;It gives a &lt;strong&gt;higher score to recent content&lt;/strong&gt;, but still considers &lt;strong&gt;query match&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You can &lt;strong&gt;tune the weights&lt;/strong&gt; in &lt;code&gt;GetRecencyScore&lt;/code&gt; to fit your needs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</id><updated>2025-07-01T07:04:27.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>