Constant score query for Sitecore.ContentSearch.Linq

69 views Asked by At

I am developing a query for global search to one of my client's Sitecore project. The requirement is to search in 2 ways:

  1. For exact match with the search key with title (1st priority);
  2. Split the search key into individual search keywords and check if each search keyword exists in the title (2nd priority).

I used PredicateBuilder from Sitecore.ContentSearch.Linq and wrote the below code, I used Boost() method to set the priority and it is working fine:

     private Expression<Func<GlobalSearchResultModel, bool>> GetSearchResultsPredicate(string keyword)
    {
        var finalPredicate = PredicateBuilder.True<GlobalSearchResultModel>();
        var searchKeywordPredicate = PredicateBuilder.False<GlobalSearchResultModel>();
        var splittedKeywordPredicate = PredicateBuilder.True<GlobalSearchResultModel>();
        
        if (!string.IsNullOrEmpty(keyword))
        {
            var searchKeyword = keyword;
            var splittedKeywords = searchKeyword.Split(' ').Take(10).ToList();
            ID homeTemplateId = Templates.GlobalSearch.HomeTemplateId;
            ID announcementListingId = Templates.GlobalSearch.AnnouncementListingId;
            finalPredicate = finalPredicate.And(x => (x.Paths.Contains(homeTemplateId)
                                                    || x.Paths.Contains(announcementListingId)
                                                    )
                                                    && x.LatestVerion && !x.ExcludeFromSearch
                                                    && !(String.IsNullOrEmpty(x.Title) && String.IsNullOrEmpty(x.AnnouncementTitle)));

            searchKeywordPredicate = searchKeywordPredicate.Or(x => x.Title.Equals(searchKeyword).Boost(5f));

            foreach (var splittedKeyword in splittedKeywords)
            {
                splittedKeywordPredicate = splittedKeywordPredicate.And(x => x.Title.Contains(splittedKeyword).Boost(3f));
            }

            
            finalPredicate = finalPredicate.And(searchKeywordPredicate.Or(splittedKeywordPredicate));
        }
        return finalPredicate;
    }

Retrieving result using following

 var result = context.GetQueryable<GlobalSearchResultModel>()
                                .Where(GetSearchResultsPredicate(searchKey))
                                .ToList();

Now I need to order them by a date field on boosting. Like the items are returned from Boost(5f) will descending order by UpdatedDate of them. But after apply Boost even for the same boosted item I am having different score, which is expected. I need a constant score for the same boost value so that I can order by UpdatedDate after ordering by score. I have seen in Solr query ^= keyword is use for constant scoring. Is there any way to apply it using Sitecore.ContentSearch.Linq? I want to achieve it without doing any memory operation.

1

There are 1 answers

2
Anna Gevel On

I'm afraid it is not possible to achieve this with Sitecore.ContentSearch.Linq and a single request to Solr.

I must say that complex logic that combines boosting and custom ordering is usually confusing and difficult to understand for both website users and content editors. But if you do want to implement this scenario, the best approach is to split it into two Solr queries:

  1. Get your exact match results and order them by UpdatedDate.
var exactMatchResults = context.GetQueryable<GlobalSearchResultModel>()
                                .Where(finalPredicate.And(searchKeywordPredicate))
                                .OrderByDescending(x => x.UpdatedDate)
                                .ToList();
  1. Get the rest of results, order them as required (or keep the default sorting by score) and append to the first result set:
var exactMatchResults = context.GetQueryable<GlobalSearchResultModel>()
                                .Where(finalPredicate.And(splittedKeywordPredicate))
                                .ToList();