Best Bets, synonyms and wildcard queries

When using a wildcard in your search query like ‘search.For(query+”*”)’ or when you used the reversed method suggested in the Breaking changes article Best Bets and Synonyms will stop working/will not work as they do a lookup on the query in the request.

The Best Bets can be “fixed” by using a custom IPhraseCriterion like below and register it

[EPiServerDataStore(
    AutomaticallyRemapStore = true,
    AutomaticallyCreateStore = true,
    StoreName = "EPiServer.Find.Framework.BestBets.DefaultPhraseCriterion"
)]
public class WildCardPhraseCriterion : DefaultPhraseCriterion, IPhraseCriterion
{
    public override bool Match(string searchPhrase)
    {
        if (string.IsNullOrWhiteSpace(searchPhrase) || Phrase == null)
        {
            return false;
        }

        // Remove wildcard
        string cleanedSearchPhrase = searchPhrase.Replace("*", string.Empty).Replace("?", string.Empty);

        return base.Match(cleanedSearchPhrase);
    }
}

For the synonyms you will need to create a custom version of the extension within Find. Though I think this overload should be in the QueryStringSearchExtensions of Find by default.

public static IQueriedSearch<TSource, QueryStringQuery> UsingSynonyms<TSource>(
    this IQueriedSearch<TSource> search, string queryString)
{
    search.ValidateNotNullArgument(nameof (search));

    if (search.Client.Settings.Admin)
    {
        return new Search<TSource, QueryStringQuery>(search, context =>
        {
            if (!(context.RequestBody.Query is QueryStringQuery query2))
            {
                Trace.Instance.Add(new TraceEvent(search, "The use of synonyms are only supported för QueryStringQueries, i.e. with the use of the .For() -extensions. The query will be executed without the use of synonyms.")
                {
                    IsError = false
                });
            }
            else
            {
                string str = queryString ?? string.Empty;
                
                ((QueryStringQuery) context.RequestBody.Query).Query = str.Replace(' ', '¤');

                MultiFieldQueryStringQuery queryStringQuery = query2 as MultiFieldQueryStringQuery;

                if (queryStringQuery.IsNull())
                {
                    ((QueryStringQuery) context.RequestBody.Query).Analyzer = "synonym";
                }
                else if (queryStringQuery.Fields.Except<string>(["_all"]).Any<string>())
                {
                    ((QueryStringQuery) context.RequestBody.Query).Analyzer = context.Analyzer?.SynonymQueryAnalyzer;
                }
                else
                {
                    ((QueryStringQuery) context.RequestBody.Query).Analyzer = "synonym";
                }
            }
        });
    }

    Trace.Instance.Add(new TraceEvent(search, "Your index does not support synonyms. Please contact support to have your account upgraded. Falling back to search without synonyms.")
    {
        IsError = false
    });

    return new Search<TSource, QueryStringQuery>(search, context => { });
}

Or just do the cleaning up of your query in the extension, which most of us have done I think

public static IQueriedSearch<TSource, QueryStringQuery> UsingSynonyms<TSource>(
  this IQueriedSearch<TSource> search)
{
  search.ValidateNotNullArgument(nameof (search));
  if (search.Client.Settings.Admin)
    return (IQueriedSearch<TSource, QueryStringQuery>) new Search<TSource, QueryStringQuery>((ISearch) search, (Action<ISearchContext>) (context =>
    {
      if (!(context.RequestBody.Query is QueryStringQuery query2))
      {
        Trace.Instance.Add((ITraceEvent) new TraceEvent((ITraceable) search, "The use of synonyms are only supported för QueryStringQueries, i.e. with the use of the .For() -extensions. The query will be executed without the use of synonyms.")
        {
          IsError = false
        });
      }
      else
      {
        string str = (query2.Query ?? (FieldFilterValue) string.Empty).ToString();
		
        // If you used the reverse method as described in https://docs.developers.optimizely.com/digital-experience-platform/v1.1.0-search-and-navigation/docs/breaking-changes-for-find15
        // You will need to reverse that again, else no match will be found ever
        // new string(searchPhrase.Reverse().ToArray())
	
        // Remove wildcard
        string cleanedSearchPhrase = str.Replace("*", string.Empty).Replace("?", string.Empty);
		
        ((QueryStringQuery) context.RequestBody.Query).Query = (FieldFilterValue) cleanedSearchPhrase.Replace(' ', '¤');

        MultiFieldQueryStringQuery queryStringQuery = query2 as MultiFieldQueryStringQuery;
        if (queryStringQuery.IsNull())
          ((QueryStringQuery) context.RequestBody.Query).Analyzer = "synonym";
        else if (queryStringQuery.Fields.Except<string>((IEnumerable<string>) new string[1]
        {
          "_all"
        }).Any<string>())
          ((QueryStringQuery) context.RequestBody.Query).Analyzer = context.Analyzer?.SynonymQueryAnalyzer;
        else
          ((QueryStringQuery) context.RequestBody.Query).Analyzer = "synonym";
      }
    }));
  Trace.Instance.Add((ITraceEvent) new TraceEvent((ITraceable) search, "Your index does not support synonyms. Please contact support to have your account upgraded. Falling back to search without synonyms.")
  {
    IsError = false
  });
  return (IQueriedSearch<TSource, QueryStringQuery>) new Search<TSource, QueryStringQuery>((ISearch) search, (Action<ISearchContext>) (context => { }));
}

There’s also a gist with the snippets

Leave a comment