Unwanted Lucene Search Results in EpiServer Commerce R2

Issue: User was getting unexpected search results for products, e.g. with a search key word ‘Sate’ he was getting same result as for ‘Site’. Although for most of the clients they will happy with these results but this particular client was unhappy with this result due to their business requirements. We do not have any word like ‘Sate’ in our Database or in Index file for Lucene. Clearly it was Fuzzy search that was causing this although in code we have set FuzzySearch = false;

Reason:
// Perform Lucene search
SearchResults results = searchFilterHelper.SearchEntries(criteria) as SearchResults;
In the Mediachase.Commerce.Website, Version=5.2.243.2, SearchFilterHelper’s SearchEntries() method, below:
public virtual ISearchResults SearchEntries(CatalogEntrySearchCriteria criteria) {  try  {
  _Results = Manager.Search(criteria);
 }
 catch (SystemException)
 {
  if (HttpContext.Current.IsDebuggingEnabled)
   throw;
 }
 // Perform fuzzy search if nothing has been found 
if (_Results.TotalCount == 0)  {
  criteria.IsFuzzySearch = true;
  criteria.FuzzyMinSimilarity = 0.7f;
  _Results = Manager.Search(criteria);
 }
 return _Results;
}
As you can see there is an initial call to Manager.Search().
Before returning, the TotalCount property on the ISearchResults object returned is checked to see if there were no results.
If there are no results a fuzzy search is done.

That was causing the issue although iSFuzzySearch was false at the time of request.

Fix: A fix is present in Episerver Commerce R2 Sp2. There is an extra Function is available that can be used to stop FuzzySearch if no result is found with actual keyword.
public virtual ISearchResults SearchEntries(CatalogEntrySearchCriteria criteria, bool isFuzzySearch, float minSimilarity)
// Perform Lucene search
SearchResults results = searchFilterHelper.SearchEntries(criteria,false,0) as SearchResults;

Special thanks to Jeff from EpiServer’s Developer Support team to find out the reason and fix.

Example Code:
internal CatalogIndexSearchDataSource CreateDataSource(SearchFilterHelper filter, int pageNo, int pageSize, ref int totalRows, bool cacheResults, int cacheTime, string nodeString, string keywords, List<string> metaClasses)
        {
            var currentCulture = Thread.CurrentThread.CurrentUICulture;
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(SiteContext.Current.LanguageName);
            var recordsToRetrieve = pageSize;
            //cache timeout
            TimeSpan cacheTimeout = new TimeSpan(0, (int)cacheTime, 0);
            // Perform search
            SearchSort sortObject = null;
            // Put default sort order if none is set
            if (sortObject == null)
                sortObject = CatalogEntrySearchCriteria.DefaultSortOrder;
            var criteria = filter.CreateSearchCriteria(keywords, sortObject);
            //If meta classes are given to search.
            if (metaClasses != null)
                foreach (string metaClass in metaClasses)
                    criteria.SearchIndex.Add(metaClass);
           
            //Add catalogs
            foreach (var row in CatalogContext.Current.GetCatalogDto(SiteContext.Current.SiteId).Catalog)
            {
                if (row.IsActive && row.StartDate <= FrameworkContext.Current.CurrentDateTime && row.EndDate >= FrameworkContext.Current.CurrentDateTime)
                    criteria.CatalogNames.Add(row.Name);
            }
            //add catalog nodes
            if (!string.IsNullOrEmpty(nodeString))
            {
                foreach (string outline in SearchFilterHelper.GetOutlinesForNode(nodeString))
                {
                    criteria.Outlines.Add(outline);
                }               
            }
            CatalogIndexSearchDataSource dataSource = null;
            // No need to perform search if no catalogs specified
            if (criteria.CatalogNames.Count != 0)
            {
                Entries entries = new Entries();
            // Perform Lucene search
            SearchResults results = searchFilterHelper.SearchEntries(criteria,false,0) as SearchResults;
           
            // Get IDs we need
            int[] resultIndexes = results.GetIntResults(pageNumber , maxRows+5); // we add padding here to accomodate entries that might have been deleted since last indexing
            if (resultIndexes.Length > 0)
            {
                int[] productResultIndexes = RefinementHelper.GetParentProducts(pageSize, pageNumber, resultIndexes, ref totalRows);
                // Retrieve actual entry objects, with no caching
                entries = CatalogContext.Current.GetCatalogEntries(productResultIndexes, false, new TimeSpan(), responseGroup);
                // Add in attribute information
                AddAttributes(entries, results);
                // Insert to the cache collection
                entries.TotalResults = totalRows;
                CatalogCache.Insert(cacheKey, entries, cacheTimeout);                dataSource = new CatalogIndexSearchDataSource { TotalResults = totalRows, CatalogEntries = entries };
            }
            Thread.CurrentThread.CurrentUICulture = currentCulture;
            return dataSource;
        }