<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/" >

<channel>
	<title>Vadim Birkos &#8211; SaM Solutions</title>
	<atom:link href="https://sam-solutions.com/author/vadim-birkos/feed/" rel="self" type="application/rss+xml" />
	<link>https://sam-solutions.com</link>
	<description></description>
	<lastBuildDate>Tue, 02 Jun 2026 10:27:54 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>
	<item>
		<title>[TechSpeak] Removing Orphaned Renderings in Sitecore</title>
		<link>https://sam-solutions.com/blog/removing-orphaned-renderings-in-sitecore/</link>
					<comments>https://sam-solutions.com/blog/removing-orphaned-renderings-in-sitecore/#respond</comments>
		
		<dc:creator><![CDATA[Vadim Birkos]]></dc:creator>
		<pubDate>Tue, 18 Mar 2025 14:58:45 +0000</pubDate>
				<guid isPermaLink="false">https://sam-solutions.com/?post_type=article&#038;p=15729</guid>

					<description><![CDATA[Let&#8217;s take a break from AI news — today, I want to share a real-world challenge I encountered while working with Sitecore 10: orphaned renderings. Problem While working on a current content project in Sitecore 10, I encountered a frustrating issue: when removing a component that contains a placeholder, Sitecore deletes only the parent rendering [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Let&#8217;s take a break from AI news — today, I want to share a real-world challenge I encountered while working with Sitecore 10: orphaned renderings.</p>



<span id="more-15729"></span>




 
    
    <div class="editor-list-cta editor-list-cta_style_1 editor-list-cta_index_1">
        
    <div class="editor-list-cta__items">
                                    
                    			    				<style>
    					.editor-list-cta_index_1 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-button {
            				            				
            				            				        							    background: linear-gradient(to right, #a067e8, #527eff);
    							            				    					}
    				</style>
    			    			
    			    			    				<style>
    					.editor-list-cta_index_1 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left {
    					    padding-left: 10px;
    					}
    					
    					.editor-list-cta_index_1 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left:before {
    					    left: 0;
    					    top: 50%;
    					    width: 3px;
    					    content: '';
    					    position: absolute;
    					    margin-left: -10px;
    					    height: calc(100% + 10px);
    					    transform: translateY(-50%);

            				            					    							    background: linear-gradient(45deg, #527eff, #a067e8);
    							            				    					}
    					
    					@media (max-width: 475px) {
    					    .editor-list-cta_index_1 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left:before {
    					        width: 2px;
                                margin-left: -3.5px;
    					    }
    					}
    				</style>
    			                
                    			    			        
    			
                    			
                                
                <div class="editor-list-cta__item editor-list-cta__item_index_1">
                    <div class="editor-list-cta__item-inner">
            		                		                                <div class="editor-list-cta__item-left">
                                                                                                    <div class="editor-list-cta__item-title"><p><strong>Hire SaM Solutions&#8217; Sitecore team to perform a</strong> tailored audit of your Sitecore implementation and highlight points for improvement.</p>
</div>
                                                	        </div>
            	        
            	                    	            <div class="editor-list-cta__item-right">
            	                                    	                                	                                	            <div class="editor-list-cta__item-event">
                    	                <div class="editor-list-cta__item-button button button_style_1">
                                            <a class="button__inner" href="/services/portals-wcms/sitecore/?utm_source=blog&#038;utm_medium=post_ID_15729&#038;utm_campaign=cta_post_content_4050">                                                <div class="button__name">Our services</div>
                                            </a>                                        </div>
                                    </div>    
                                                                        
            	            </div>
            	                	        </div>
                </div>
                        </div>
    </div>
    



<h2 class="wp-block-heading">Problem</h2>



<p class="has-black-color has-text-color has-link-color wp-elements-2 wp-block-paragraph">While working on a current content project in Sitecore 10, I encountered a frustrating issue: when removing a component that contains a placeholder, Sitecore <strong>deletes only the parent rendering</strong> but <strong>leaves all nested child renderings </strong>behind in the layout field <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-black-color">(</mark><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>__Renderings</code></mark><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-black-color">)</mark>. As a result, orphaned components are created. They will<strong> never be rendered</strong> but still remain in the item’s layout, leading to unnecessary clutter, rendering issues, and potential performance degradation.</p>



<p class="wp-block-paragraph">When working with Sitecore’s dynamic placeholders, child components are often assigned within parent renderings. However, if a parent rendering is deleted, Sitecore does not automatically clean up the child renderings that were nested inside its placeholder.&nbsp;</p>



<p class="wp-block-paragraph">These orphaned components:</p>



<ul class="wp-block-list">
<li><strong>Remain stored </strong>in the layout field (<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>__Renderings</code></mark>).</li>



<li>Are <strong>not visible </strong>on the page, even though they still exist in the data structure.</li>



<li>Can cause <strong>broken layouts, unused placeholders, </strong>and<strong> excess rendering data</strong>.</li>
</ul>



<h2 class="wp-block-heading">Solution</h2>



<p class="wp-block-paragraph">So, how do we clean up these orphaned renderings? Let’s dive into the fix.</p>



<p class="wp-block-paragraph">As part of the solution, we implemented an event handler for <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>item:saved</code></mark> to validate whether a rendering with a nested placeholder was removed, potentially leaving orphaned renderings. If orphaned renderings are detected, they are removed, and the layout field is reserialized.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: csharp; title: ; quick-code: false; notranslate">
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Events;
using Sitecore.Layouts;

namespace YourNamespace.Sitecore.Events
{
    public class DeleteOrphanRenderingsHandler
    {
        private const string DefaultDeviceId = &quot;{FECA918C-40C4-4F99-8CF6-BE64DAF8FC32}&quot;; // Default Sitecore device ID
        private const string PlaceholderFormat = &quot;-{0}-&quot;; // Format for placeholders in Sitecore
        private const string LocalDataSourcePrefix = &quot;local:&quot;; // Prefix for local data sources

        public void OnItemSaved(object sender, EventArgs args)
        {
            if (!(Event.ExtractParameter(args, 0) is Item savedItem) ||
                !(Event.ExtractParameter(args, 1) is ItemChanges itemChanges) ||
                itemChanges.IsEmpty)
            {
                return;
            }

            foreach (var fieldId in new&#x5B;] { Sitecore.FieldIDs.LayoutField, Sitecore.FieldIDs.FinalLayoutField })
            {
                if (!itemChanges.FieldChanges.Contains(fieldId))
                    continue;

                // Important check: If Context.Item is null, local data sources may break (render empty)
                if (Context.Item == null)
                {
                    Context.Item = savedItem;
                }

                var updatedField = itemChanges.FieldChanges&#x5B;fieldId];
                var updatedFieldValue = updatedField.Value;
                var originalFieldValue = updatedField.OriginalValue;

                if (string.IsNullOrEmpty(originalFieldValue) || string.IsNullOrEmpty(updatedFieldValue))
                    continue;

                var originalLayout = LayoutDefinition.Parse(originalFieldValue);
                var updatedLayout = LayoutDefinition.Parse(updatedFieldValue);

                var deviceId = Context.Device?.ID.ToString() ?? DefaultDeviceId;
                var originalDevice = originalLayout.GetDevice(deviceId);
                var updatedDevice = updatedLayout.Devices.Cast&lt;DeviceDefinition&gt;()
                    .FirstOrDefault(x =&gt; x.ID == deviceId);

                if (updatedDevice == null || originalDevice == null)
                {
                    continue;
                }

                var updateLayoutField = (LayoutField)savedItem.Fields&#x5B;fieldId];
                
                var originalRenderings = originalDevice.Renderings.Cast&lt;RenderingDefinition&gt;().ToArray();
                var updatedRenderingsFromLayout = updateLayoutField.GetReferences(Context.Device).ToList();

                var removeResult = RemoveOrphanedRenderings(originalRenderings, updatedRenderingsFromLayout);
                if (!removeResult.wasUpdated) continue;

                var mappedRenderings = removeResult.updatedRenderings.Select(x =&gt;
                {
                    var dataSourcePath = x.Settings.DataSource;
                    return new RenderingDefinition
                    {
                        UniqueId = x.UniqueId,
                        Placeholder = x.Placeholder,
                        ItemID = x.RenderingID.ToString(),
                        Parameters = x.Settings.Parameters,
                        Datasource = ConvertToLocalDataSource(savedItem.Paths.FullPath, dataSourcePath)
                    };
                }).ToList();

                updatedDevice.Renderings = new ArrayList(mappedRenderings);
                
                savedItem.Editing.BeginEdit();
                // If we use LayoutField.SetFieldValue(), the layout will be missing for the item.
                savedItem.Fields&#x5B;fieldId].Value = updatedLayout.ToXml();
                
                // Prevent second item:save event
                savedItem.Editing.EndEdit(true);
            }
        }

        private static (bool wasUpdated, List&lt;RenderingReference&gt; updatedRenderings) RemoveOrphanedRenderings(
            RenderingDefinition&#x5B;] originalRenderings,
            List&lt;RenderingReference&gt; updatedRenderings
        )
        {
            var wasUpdated = false;
            var removedRenderings = originalRenderings
                .Where(orig =&gt; updatedRenderings.All(updated =&gt; updated.UniqueId != orig.UniqueId))
                .ToList();

            if (!removedRenderings.Any()) return (false, updatedRenderings);

            foreach (var removedRendering in removedRenderings)
            {
                var updatedRenderingsTemp = updatedRenderings
                    .RemoveWhere(x =&gt; x.Placeholder.Contains(string.Format(PlaceholderFormat, removedRendering.UniqueId))).ToList();

                if (updatedRenderingsTemp.Count != updatedRenderings.Count)
                {
                    wasUpdated = true;
                }

                updatedRenderings = updatedRenderingsTemp.ToList();
            }

            return (wasUpdated, updatedRenderings);
        }
        
        private static string ConvertToLocalDataSource(string savedItemFullPath, string absoluteDataSourcePath)
        {
            if (string.IsNullOrEmpty(savedItemFullPath) || string.IsNullOrEmpty(absoluteDataSourcePath))
                return absoluteDataSourcePath; 
        
            if (absoluteDataSourcePath.StartsWith(savedItemFullPath, StringComparison.OrdinalIgnoreCase))
            {
                var localPath = absoluteDataSourcePath.Substring(savedItemFullPath.Length).TrimStart(&#039;/&#039;);
                return $&quot;{LocalDataSourcePrefix}{localPath}&quot;;
            }
            return absoluteDataSourcePath;
        }
    }
}

</pre></div>


<p class="wp-block-paragraph">The configuration file should be placed under the <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>App_Config</code></mark> folder, following your project’s folder structure. If you are using the Helix architecture, I recommend placing it under <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>App_Config/Include/Foundation</code></mark></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: xml; title: ; quick-code: false; notranslate">
&lt;configuration xmlns:patch=&quot;http://www.sitecore.net/xmlconfig/&quot;&gt;
  &lt;sitecore&gt;
    &lt;events&gt;
      &lt;event name=&quot;item:saved&quot;&gt;
        &lt;handler type=&quot;YourNamespace.Sitecore.Events.DeleteOrphanRenderingsHandler, YourNamespace.Sitecore&quot;
                 method=&quot;OnItemSaved&quot; resolve=&quot;true&quot;/&gt;  
      &lt;/event&gt;
    &lt;/events&gt;
  &lt;/sitecore&gt;
&lt;/configuration&gt;

</pre></div>


<h2 class="wp-block-heading">Some Interesting Moments from the Code Above</h2>



<h3 class="wp-block-heading"><strong>Set Context.Item to the current saved item manually</strong></h3>



<p class="wp-block-paragraph">When handling Sitecore’s <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>item:saved</code></mark> event, <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>Context.Item</code></mark> is not automatically set to the saved item, even if <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>resolve="true"</code></mark> is specified in the configuration file. This leads to issues when resolving <strong>local data sources</strong> (e.g.,<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"> <code>local:/Data/RichTextDatasource</code></mark>) because Sitecore relies on <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>Context.Item</code></mark> for resolution.</p>



<p class="wp-block-paragraph">Without explicitly setting <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>Context.Item</code></mark>, the following problem occurs:</p>



<ul class="wp-block-list">
<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>updateLayoutField.GetReferences(Context.Device)</code></mark>; loads local data sources as empty strings (<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">&#8220;&#8221;</mark>).</li>



<li>All renderings that had local data sources will lose their assigned values.</li>



<li>This causes unexpected behavior and potential errors on the website.</li>
</ul>



<h3 class="wp-block-heading"><strong>Fix: manually set <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>Context.Item</code></mark></strong></h3>



<p class="wp-block-paragraph">To ensure correct resolution, manually set <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>Context.Item</code></mark> before processing:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: csharp; title: ; quick-code: false; notranslate">
    if (Context.Item == null)
    {
		    Context.Item = savedItem;
    }
</pre></div>


<h3 class="wp-block-heading"><strong>Using direct field set instead of LayoutField.SetFieldValue()</strong></h3>



<p class="wp-block-paragraph">Using <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>LayoutField.SetFieldValue()</code></mark> was found to <strong>remove the assigned layout</strong>, preventing the page from rendering properly.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: csharp; title: ; quick-code: false; notranslate">
savedItem.Editing.BeginEdit();
savedItem.Fields&#x5B;fieldId].Value = updatedLayout.ToXml();

// Prevent second item:save event
savedItem.Editing.EndEdit(true);
</pre></div>


<h3 class="wp-block-heading"><strong>ConvertToLocalDataSource function</strong></h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: csharp; title: ; quick-code: false; notranslate">
Datasource = ConvertToLocalDataSource(savedItem.Paths.FullPath, dataSourcePath)
</pre></div>


<p class="wp-block-paragraph">The <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>ConvertToLocalDataSource</code></mark> function was introduced to convert absolute data source paths to <strong>local</strong> relative paths <strong>when possible</strong>. This helps simplify the maintenance of data source items, especially when the <strong>parent item is renamed</strong>, ensuring that data sources remain correctly linked.</p>



<h3 class="wp-block-heading"><strong>Purpose of this function</strong></h3>



<ul class="wp-block-list">
<li>Converts absolute paths (e.g., <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>/sitecore/content/Home/Data/Item</code></mark>) to <strong>local</strong> paths (<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color"><code>local:Data/Item</code></mark>) when applicable.</li>



<li>Ensures that <strong>data sources remain correctly associated with their parent item</strong>.</li>



<li>Prevents issues where renaming a parent item breaks <strong>hardcoded absolute paths</strong>.</li>
</ul>



<p class="wp-block-paragraph">This approach helps keep data sources flexible and reduces the risk of broken references when items are moved or renamed in Sitecore.</p>



<p class="wp-block-paragraph">P. S.</p>



<p class="wp-block-paragraph">Thank you for reading! I’m planning to share more about the challenges I encounter in Sitecore development. Stay tuned for more insights!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sam-solutions.com/blog/removing-orphaned-renderings-in-sitecore/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Cognitive Search Experience in Sitecore 9.2: Pros and Cons</title>
		<link>https://sam-solutions.com/blog/azure-cognitive-search-in-sitecore/</link>
					<comments>https://sam-solutions.com/blog/azure-cognitive-search-in-sitecore/#respond</comments>
		
		<dc:creator><![CDATA[Vadim Birkos]]></dc:creator>
		<pubDate>Wed, 30 Nov 2022 14:26:28 +0000</pubDate>
				<guid isPermaLink="false">https://sam-solutions.de/?post_type=article&#038;p=429</guid>

					<description><![CDATA[Hey there! In this blog post, I want to share my development experience with Search in Sitecore 9.2. I have been working with Search in Sitecore for more than four years with different engines, including SOLR, Lucene and Azure Search (this is not to be confused with Azure ElasticSearch, because in version 9.2, Sitecore didn’t [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Hey there! In this blog post, I want to share my development experience with Search in Sitecore 9.2.</p>



<p class="wp-block-paragraph">I have been working with Search in Sitecore for more than four years with different engines, including SOLR, Lucene and Azure Search (this is not to be confused with Azure ElasticSearch, because in version 9.2, Sitecore didn’t have an SDK to work with this one).</p>



<span id="more-429"></span>



<p class="wp-block-paragraph">By this time, I’ve touched upon different scenarios of Search implementation:</p>



<ul class="wp-block-list">
<li>Supporting both Azure Search and SOLR at the same time</li>



<li>Integrating third-party tools to provide data for a custom index</li>



<li>Covering all CRUD functions for Custom Index crawler</li>



<li>Increasing performance for the custom Index full-rebuild logic</li>



<li>Implementing <a href="/services/software-engineering/cloud-software-development/">cloud solutions</a> for incremental index update</li>



<li>Creating a tool to manage search results on the website according to different criteria (dates, events, filters)</li>
</ul>



<p class="wp-block-paragraph">It was a long journey and tons of challenges, and I will be happy if some tips from this and my future blogs will save your time during planning or troubleshooting.</p>



<p class="wp-block-paragraph">The Search functionality is an important feature on many websites related to content management (literally, it means almost all Sitecore-based websites). And in my experience, indexing/search logic was sometimes really complicated and required a deep understanding of what you could do with the available tools set.</p>



<h2 class="wp-block-heading">What is the first step in all systems that requires Search?</h2>



<p class="wp-block-paragraph">The first and most important step is to collect as many search-related requirements as possible. Otherwise, it’s hard to design a schema and choose the Search engine if you don’t know how it will be used. To clarify versions, we started our project on Sitecore 9.2 because it was the latest one at that moment.</p>



<p class="wp-block-paragraph">The customer wanted to use Azure Cognitive Search on production, and this was the first issue we faced, because (as we learned later) the project turned out to be complicated in terms of the search business logic. For some reasons, we couldn’t pull together all the requirements on time. So eventually, we selected Azure Cognitive Search for all shared environments (Dev, QA, Staging and Production) and SOLR for local environments.</p>



<p class="wp-block-paragraph">It was a mistake for several reasons:</p>



<ol class="wp-block-list">
<li>It took additional time to support several Search Engines.</li>



<li>As mentioned above, the search functionality caused performance bottlenecks (will be described later).</li>



<li>Some bugs were reproduced only on Azure Search or SOLR.</li>



<li>Different limitations from both system took time to deal with.</li>



<li>Local testing/development.</li>



<li>Sitecore SDK for both works differently when using Search.</li>
</ol>



<p class="wp-block-paragraph">As you can see, it was a pretty bad idea trying to mix it all. But we were brave enough to try it.</p>



<h2 class="wp-block-heading">Azure Cognitive Search limitations</h2>



<p class="wp-block-paragraph">Here I will try to show all limitations that can affect your work during the project life.</p>



<ol class="wp-block-list">
<li><b>An Azure Cognitive Search index can only contain up to 1000 fields.</b> It means that all your fields + all fields that will be created (multi-language fields (1 field for every original field for one language), system fields) are limited by 1000. This is not much at all.</li>



<li><b>Joining queries such as .GroupJoin(), .SelfJoin(), and other operators that join queries, is not supported and results in an error.</b> For our case, it was mandatory to have Group operations on the Search Server side. While the Azure Search was exploited on the website, the Search response could take up to 15 seconds(!!!) because group operations were performed on the Server side instead of the Search engine.</li>



<li><b>Some weird behavior for equaling string operations. </b>It turned out that getting only equivalent results to the specific string is not supported on the Azure Search LINQ Query converter level. The reason is converting the <b>.Equals() </b>LINQ function into Azure search query.</li>
</ol>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1600" height="647" src="https://sam-solutions.com/wp-content/uploads/Azure-cognitive-search-min.png" alt="Azure Cognitive Search Experience in Sitecore" class="wp-image-3315"/></figure>



<p class="wp-block-paragraph">Here, if the expression is string and the trimmed expression length isn’t equal to 0, <i>this._queryBuilder.SearchQueryBuilder.Equal </i>will be used. Let’s take a look what this method contains inside.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1600" height="279" src="https://sam-solutions.com/wp-content/uploads/Azure-cognitive-search-1-min.png" alt="Azure cognitive search in Sitecore pros and cons" class="wp-image-3311"/></figure>
</div>


<p class="wp-block-paragraph">It contains typical <b>Contains() </b>function in the <b><i>{fieldName}:(“{value}”) </i></b>format.</p>



<p class="wp-block-paragraph">You can invent something with different types (e.g., double, integer) as a workaround. This is the first signal that should make you consider the migration to the different Search Engine.</p>



<p class="wp-block-paragraph">More limitations you can find <a href="https://doc.sitecore.com/xp/en/developers/91/platform-administration-and-architecture/sitecore-azure-search-overview.html" target="_blank" rel="nofollow noopener">here</a>.</p>



<h2 class="wp-block-heading">Azure Cognitive Search advantages</h2>



<p class="wp-block-paragraph">At the same time, Azure Cognitive Search has advantages too:</p>



<ol class="wp-block-list">
<li>It’s powered by Azure, meaning that you can integrate search into different Azure apps.</li>



<li>It has 99.9% SLA, which is important for the production environment.</li>



<li>It’s easy to configure auto-scale.</li>



<li>It works pretty good for simple Search solutions.</li>



<li>Due to the fact that it’s a SaaS solution, it’s pretty easy to set up.</li>
</ol>



<p class="wp-block-paragraph">But these advantages could not outperform challenges that we faced, and finally, we switched from Azure Cognitive Search to SOLR, which made our solution easier to support and increased performance multiple times.</p>



<h2 class="wp-block-heading">P.S.</h2>



<p class="wp-block-paragraph">In my following blog posts, I want to describe how we created the SOLR custom index, what issues we faced, and how we built the Full-Rebuild process. Stay tuned and try to avoid big challenges instead of fighting with them.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sam-solutions.com/blog/azure-cognitive-search-in-sitecore/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Sitecore Hackathon 2021: Our Hands-On Experience of Developing a Managed Synonyms Module</title>
		<link>https://sam-solutions.com/blog/managed-synonyms-module-sitecore-hackathon/</link>
					<comments>https://sam-solutions.com/blog/managed-synonyms-module-sitecore-hackathon/#respond</comments>
		
		<dc:creator><![CDATA[Vadim Birkos]]></dc:creator>
		<pubDate>Fri, 12 Mar 2021 13:18:18 +0000</pubDate>
				<guid isPermaLink="false">https://sam-solutions.de/?post_type=article&#038;p=547</guid>

					<description><![CDATA[On March 5, the SaM Solutions team represented by Vadzim Papko, Vitali Taleika and me, Vadim Birkos, took part in the annual online event Sitecore Hackathon. In this article, I’ll talk about our experience of participating in this event and the technical result we managed to achieve. Why Participate in Hackathon In 2020, I intended [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">On March 5, the SaM Solutions team represented by Vadzim Papko, Vitali Taleika and me, Vadim Birkos, took part in the annual online event Sitecore Hackathon.</p>



<p class="wp-block-paragraph">In this article, I’ll talk about our experience of participating in this event and the technical result we managed to achieve.</p>



<span id="more-547"></span>



<h2 class="wp-block-heading">Why Participate in Hackathon</h2>



<p class="wp-block-paragraph">In 2020, I intended to compete in Hackathon but things didn’t work out. Why? My colleagues and I had a brilliant idea (which we’re still working on so I can’t disclose the details) but we couldn’t gather together at the appointed time and implement it. I guess first attempts often result in failure.</p>



<p class="wp-block-paragraph">This year, when I saw the announcement for Sitecore Hackathon 2021, I decided to give it another go.</p>



<p class="wp-block-paragraph">Hackathon is a good challenge for every developer. If you take part in this event, you can:</p>



<ul class="wp-block-list">
<li>gain new experiences</li>



<li>get new ideas</li>



<li>network with like-minded people</li>



<li>get <a href="/industries/energy-software-development/">energy</a> and motivation for creative and productive work.</li>
</ul>



<p class="wp-block-paragraph">In fact, it was my team who really inspired me to participate. I was sure that together with my talented and creative colleagues Vitali Taleika and Vadzim Papko we would achieve great results.</p>




 
    
    <div class="editor-list-cta editor-list-cta_style_1 editor-list-cta_index_2">
        
    <div class="editor-list-cta__items">
                                    
                    			    				<style>
    					.editor-list-cta_index_2 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-button {
            				            				
            				            				        							    background: linear-gradient(to right, #a067e8, #527eff);
    							            				    					}
    				</style>
    			    			
    			    			    				<style>
    					.editor-list-cta_index_2 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left {
    					    padding-left: 10px;
    					}
    					
    					.editor-list-cta_index_2 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left:before {
    					    left: 0;
    					    top: 50%;
    					    width: 3px;
    					    content: '';
    					    position: absolute;
    					    margin-left: -10px;
    					    height: calc(100% + 10px);
    					    transform: translateY(-50%);

            				            					    							    background: linear-gradient(45deg, #527eff, #a067e8);
    							            				    					}
    					
    					@media (max-width: 475px) {
    					    .editor-list-cta_index_2 .editor-list-cta__item.editor-list-cta__item_index_1 .editor-list-cta__item-left:before {
    					        width: 2px;
                                margin-left: -3.5px;
    					    }
    					}
    				</style>
    			                
                    			    			        
    			
                    			
                                
                <div class="editor-list-cta__item editor-list-cta__item_index_1">
                    <div class="editor-list-cta__item-inner">
            		                		                                <div class="editor-list-cta__item-left">
                                                                                                    <div class="editor-list-cta__item-title"><p><strong>Launch your Sitecore development or modernization</strong> with SaM Solutions&#8217; skilled team, supervised and mentored by a four-time Sitecore MVP.</p>
</div>
                                                	        </div>
            	        
            	                    	            <div class="editor-list-cta__item-right">
            	                                    	                                	                                	            <div class="editor-list-cta__item-event">
                    	                <div class="editor-list-cta__item-button button button_style_1">
                                            <a class="button__inner" href="/services/portals-wcms/sitecore/?utm_source=blog&#038;utm_medium=post_ID_547&#038;utm_campaign=cta_post_content_4067">                                                <div class="button__name">Our services</div>
                                            </a>                                        </div>
                                    </div>    
                                                                        
            	            </div>
            	                	        </div>
                </div>
                        </div>
    </div>
    



<h2 class="wp-block-heading">Discussing Ideas</h2>



<p class="wp-block-paragraph">Since we didn’t know the Hackathon topics for this year, we could only discuss potential ideas that we were not sure would feature in the competition.</p>



<p class="wp-block-paragraph">We spent a lot of time discussing the potential solutions we could implement, and we had some great ideas, but all of them required weeks or months of intensive work to be completed. This was clearly not appropriate for the bounds of a 24-hour marathon.</p>



<p class="wp-block-paragraph">On March 5, we gathered together for the start of Hackathon (at 3 a.m. our local time) and learned that the following topics would feature:</p>



<ul class="wp-block-list">
<li>The best enhancement to the Sitecore Admin (XP) for Content Editors and Marketers</li>



<li>The best enhancement to SXA</li>



<li>Best use of SPE to help Content Authors and Marketers</li>



<li>Best use of Headless using JSS or <a href="/services/technologies/dot_net/">.NET</a></li>
</ul>



<p class="wp-block-paragraph">We were finally able to make a decision on which module to implement. Since all of us are experienced in Sitecore search functionality, Vitali Taleika suggested we work on improving the keyword synonym search for SOLR using Sitecore tools.</p>



<p class="wp-block-paragraph">After some discussion, we agreed that it would be a very useful module for the <a href="/services/portals-wcms/sitecore/">Sitecore platform</a>. We also thought that with our idea we could try to win in the category “The best enhancement to the Sitecore Admin (XP) for Content Editors and Marketers.”</p>



<h2 class="wp-block-heading">Implementation</h2>



<p class="wp-block-paragraph">As we ensured that our idea complied with all the requirements, we started to brainstorm how to implement it. The task was not hard because everyone in our team has a deep understanding of search functionality.</p>



<p class="wp-block-paragraph">We discovered that the SOLR engine utilized the “synonyms.txt” file to get the necessary data. But there was no way of managing this useful function from Sitecore. We also found Sitecore Pipelines responsible for synonym management in SOLR.</p>



<p class="wp-block-paragraph">By then, we had a plan on how to integrate a flexible synonym management approach in SOLR using Sitecore tools and make it manageable by the Content Author.</p>



<p class="wp-block-paragraph">We implemented our idea in the following way:</p>



<ul class="wp-block-list">
<li>A content author fills in Sitecore items with the necessary synonyms in the content tree.</li>



<li>All specified synonyms are migrated to SOLR with the help of a custom Ribbon button.</li>



<li>The search can now be performed using specified synonyms.</li>
</ul>



<p class="wp-block-paragraph">In Sitecore, Content Author has two types of items. The first type is root settings for SOLR that contain Core names that should be applied with synonyms. You can see the structure below:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1314" height="790" src="https://sam-solutions.com/wp-content/uploads/image4-min.png" alt="Managed synonyms" class="wp-image-6243"/></figure>
</div>


<p class="wp-block-paragraph">The second type contains synonyms that get published to the SOLR engine.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1109" height="793" src="https://sam-solutions.com/wp-content/uploads/image1-min.png" alt="MVP synonyms" class="wp-image-6246"/></figure>
</div>


<p class="wp-block-paragraph">Below you can see our custom Ribbon, which triggers the sync with Solr synonyms:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="758" height="693" src="https://sam-solutions.com/wp-content/uploads/image3-min.png" alt="Sync Managed Synonyms" class="wp-image-6248"/></figure>
</div>


<p class="wp-block-paragraph">When all synonyms are synchronized, we can show how the feature works. Let’s search for the word “superman”. We defined this word in the synonym content item, but search pages don’t contain it.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1380" height="840" src="https://sam-solutions.com/wp-content/uploads/image5-min.png" alt="Search" class="wp-image-6250"/></figure>
</div>


<p class="wp-block-paragraph">Here are two pages that contain a “superman” synonym. Let’s open the Contact page.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="1343" height="961" src="https://sam-solutions.com/wp-content/uploads/image2-min.png" alt="Contacts" class="wp-image-6252"/></figure>
</div>


<p class="wp-block-paragraph">Here you see the word “<a href="/services/mvp-development-services/">MVP</a>” but no “superman”. This is how the search-by-synonyms functionality works: a page may not contain the specified word but it will be in the search results if it contains a synonym for that word.</p>



<p class="wp-block-paragraph">You can find the source code and documentation on how this module can be used on your website in the <a href="https://github.com/Sitecore-Hackathon/2021-Free-Belarusians-Team" target="_blank" rel="noreferrer noopener nofollow">GitHub of our hackathon team</a>.</p>



<h2 class="wp-block-heading">Preparing for the Demo</h2>



<p class="wp-block-paragraph">The hardest task for us at this Hackathon was to prepare the demonstration website for our module.</p>



<p class="wp-block-paragraph">We spent half of the allocated time (9–11 hours) looking for a demonstration website with pre-filled content and implemented search functionality. That’s quite a long time, isn’t it?</p>



<p class="wp-block-paragraph">During this time, we could have created our own personalized website with enough content for any purpose using SXA. Unfortunately, that idea came to our minds too late, after we had already spent 6–7 hours searching for and installing available demonstration websites from Google.</p>



<p class="wp-block-paragraph">Imagine our disappointment when we realized that the website we had installed was working well but was not compatible with our module, because its searching approach was not the one we needed. This was a moment of heartbreak for us.</p>



<p class="wp-block-paragraph">I invite you to learn from our mistakes and create a website that will be appropriate for your purposes. And don’t hesitate to ask colleagues from the Sitecore Community in Slack or Twitter chats if you have questions that you can’t resolve yourself during the Hackathon.</p>



<h2 class="wp-block-heading">Tips for Future Hackathons</h2>



<p class="wp-block-paragraph">During this Hackathon, we made a number of mistakes that prevented us from performing better. But as they say, experience comes from bad experiences. Next year, we’ll use the following tips to avoid serious obstacles:</p>



<ol class="wp-block-list">
<li><b>Concentrate on what’s really important.</b> We were not concentrated enough and wasted a lot of time on issues that were not critical for our module.</li>



<li><b>Sleep at least 3–4 hours.</b> There is no need for you to stay awake for 24 hours. If you sleep a bit, you will reload and be more effective.</li>



<li><b>Prioritize the Hackathon task over other tasks.</b> You must put aside all your non-Hackathon plans to be fully involved in the process.</li>



<li><b>Don’t waste time searching for the demonstration site.</b> It’s more efficient to create your own demonstration website that will be relevant for your team and your idea.</li>



<li><b>Eliminate negative emotions, </b>only fun, and Rock’n’Roll 🙂</li>
</ol>



<h2 class="wp-block-heading">To Sum Up</h2>



<p class="wp-block-paragraph">It was really exciting for me to be a part of this event. I realized that the Sitecore Community is a big family passing positive energy to its members when they need it most.</p>



<p class="wp-block-paragraph">In addition to the module development, I had much fun networking with the community members via Twitter and Slack. Many warm and friendly people were ready to help us with any issues and ideas. This made me feel good.</p>



<p class="wp-block-paragraph">Undoubtedly, I will take part in Sitecore Hackathon next time and will do my best to perform better than this year.</p>



<p class="wp-block-paragraph">If you have any questions, contact me via Twitter or LinkedIn.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sam-solutions.com/blog/managed-synonyms-module-sitecore-hackathon/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
