Search code examples
c#umbracoexamine

How to bring newer content at the top of search results using Umbraco Examine?


We have an website created in Umbraco CMS and I am developing a site search functionality. There is a section within the website called "news" where regular updates are published. While listing results from news section (among other pages), I want to bring latest content earlier on the results, e.g. if someone is searching for "exam results", I would like to bring matching news pages created in 2018 earlier than a page created in 2017 and so on. If there an way to boost (either query time or index time) so that latest pages are boosted up?

Below is my code I have written till now:

var page = 1;
var pageSize = 5;

if (Request.QueryString["q"] != null)
    searchQuery = Request.QueryString["q"];

if (Request.QueryString["page"] != null)
    Int32.TryParse(Request.QueryString["page"], out page);

ISearchResults searchResults = null;
BaseSearchProvider searcher = ExamineManager.Instance.SearchProviderCollection["ExternalSearcher"];

var headerFields = new[] { "contentTitle", "metaTags", "metaDescription", "nodeName" };
var contentFields = new[] { "contentDescription", "mainBody" };
var criteria = searcher.CreateSearchCriteria(IndexTypes.Content, BooleanOperation.Or);
var searchTerm = string.IsNullOrEmpty(Request["q"]) ? string.Empty : Request["q"];

if (searchTerm != string.Empty)
{
    searchTerm = searchTerm.MakeSearchQuerySafe();

    if (searchTerm.Length > 0)
    {
        searchTerm = searchTerm.Trim();
    }

    var examineQuery = criteria.GroupedOr(headerFields, searchTerm.Boost(100));
    examineQuery.Or().GroupedOr(contentFields, searchTerm.Boost(50));

    if (searchTerm.Contains(" "))
    {
        examineQuery.Or().GroupedOr(headerFields, searchTerm.RemoveStopWords().Split(' ').Select(x => x.MultipleCharacterWildcard().Value.Boost(10)).ToArray());
        examineQuery.Or().GroupedOr(contentFields, searchTerm.RemoveStopWords().Split(' ').Select(x => x.MultipleCharacterWildcard()).ToArray());
    }

    searchResults = searcher.Search(examineQuery.Compile(), maxResults: pageSize * page);
}

Solution

  • Since others might encounter the same issue and land on this question (through search), I am posting an answer to my own question.

    I wrote two event handlers as below:

    1. Whenever new content is published in Umbraco CMS, trigger an Examine index rebuild.
    2. When Examine indexer encounters the document type alias as "newsArticle", boost the document down relative to the last updated date.

    The event handler code is below:

    public class ExamineEvents : Umbraco.Core.ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            var indexer = (UmbracoContentIndexer)ExamineManager.Instance.IndexProviderCollection["ExternalIndexer"];
            indexer.DocumentWriting += Indexer_DocumentWriting;
            Umbraco.Core.Services.ContentService.Published += ContentService_Published;
        }
    
        private void ContentService_Published(Umbraco.Core.Publishing.IPublishingStrategy sender, Umbraco.Core.Events.PublishEventArgs<Umbraco.Core.Models.IContent> e)
        {
            ExamineManager.Instance.IndexProviderCollection["ExternalIndexer"].RebuildIndex();
        }
    
        private void Indexer_DocumentWriting(object sender, DocumentWritingEventArgs e)
        {
            //if it is a 'news article' doc type then > BOOST it DOWN - the older the article, the lower the boost value 
            if (e.Fields.ContainsKey("nodeTypeAlias") && e.Fields["nodeTypeAlias"] == "newsArticle")
            {
                float boostValue = 1f;
                const string umbDateField = "updateDate";
                if (e.Fields.ContainsKey(umbDateField))
                {
                    DateTime updateDate = DateTime.Parse(e.Fields[umbDateField]);
                    var daysInBetween = Math.Ceiling((DateTime.Now - updateDate).TotalDays + 1); // +1 to avoid 0 days
                    boostValue = (float) (1 / daysInBetween);
                }
                e.Document.SetBoost(boostValue);
            }
        }
    }