Monday, April 27, 2020

Sitecore SXA Search - Custom SearchQueryToken resolver.

Sitecore SXA provided a few OOTB search query token resolvers as mentioned below.
  1. TaggedTheSameAsCurrentPage|SxaTags
  2. TaggedWithAtLeastOneTagFromCurrentPage|SxaTags
  3. UnderCurrentPage
  4. ExcludeCurrentPage
  5. ItemsOfTheSameTemplateAsTheCurrentPage
  6. ItemsWithTheSameValueInField|FieldName
The resolveSearchQuery tokens basically is a pipeline used to add search filters. This pipeline is defined in the Sitecore.XA.Foundation.Search.config file. 
I've added a new one which is ItemsWithTheSameValueInQueryString|FieldName



Benefit- This custom search resolver can be used anywhere where we want to search/ or filter the SXA result based on the query string (Field and Value).

Example - https://website/searchresult?name1=name1Value&name2=name2Value.

Implementation - Add a new class and implement the ResolveSearchQueryTokensProcessor


 public class ItemsWithTheFieldAndValueInQueryString : ResolveSearchQueryTokensProcessor
    {
        protected string TokenPart { get; } = "ItemsWithTheSameValueInQueryString";

        [SxaTokenKey]
        protected override string TokenKey => FormattableString.Invariant(FormattableStringFactory.
            Create("{0}|FieldName", "ItemsWithTheSameValueInQueryString"));


        public override void Process(ResolveSearchQueryTokensEventArgs args)
        {
            if (args.ContextItem == null)
                return;

            for (var index = 0; index < args.Models.Count; ++index)
            {
                var model = args.Models[index];
                if (!model.Type.Equals("sxa", StringComparison.OrdinalIgnoreCase) || !ContainsToken(model)) continue;
                var inputTokenFields = model.Value.Replace(TokenPart, string.Empty).TrimStart('|');
                var fieldNames = inputTokenFields.Split(',').ToList();
                if (HttpContext.Current.Request.Url != null)
                {
                    var queryCollection = HttpUtility.ParseQueryString
                        (HttpContext.Current.Request.Url.Query);

                    NameValueCollection queryCollectionReferrer = null;
                    if (HttpContext.Current.Request.UrlReferrer != null)
                    {
                        queryCollectionReferrer = HttpUtility.ParseQueryString
                       (HttpContext.Current.Request.UrlReferrer.Query);
                    }
                    

                    foreach (var field in fieldNames)
                    {
                        if (!string.IsNullOrWhiteSpace(field) && !string.IsNullOrWhiteSpace(queryCollection[field]))
                        {
                            args.Models.Insert(index, BuildModel(field.ToLower(), queryCollection.Get(field)));
                        }
                        else if(queryCollectionReferrer != null && !string.IsNullOrWhiteSpace(field) && queryCollectionReferrer.AllKeys.Contains(field) && !string.IsNullOrWhiteSpace(queryCollectionReferrer[field]))
                        {
                            args.Models.Insert(index, BuildModel(field, queryCollectionReferrer.Get(field)));                            
                        }

                    }

                }

                args.Models.Remove(model);
            }
        }
        protected virtual SearchStringModel BuildModel(string replace, string fieldValue)
        {
            return new SearchStringModel("custom", FormattableString.Invariant(FormattableStringFactory.Create("{0}|{1}", replace, fieldValue)))
            {
                Operation = "must"
            };
        }

        protected override bool ContainsToken(SearchStringModel m)
        {
            return Regex.Match(m.Value, FormattableString.Invariant(FormattableStringFactory.Create("{0}\\|[a-zA-Z ]*", "ItemsWithTheSameValueInQueryString"))).Success;
        }
    }

Registration -


 <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    <pipelines>
      <resolveSearchQueryTokens>
        <processor type="Feature.ItemsWithTheFieldAndValueInQueryString, Namespeace" resolve="true" patch:before = "*[1]" />
      </resolveSearchQueryTokens>
    </pipelines>
  </sitecore>

patch:before = "*[1]"  -  This will be used to keep this patch on the top.


Steps to verify -

Add a SXA Search result component on the page -


You can define the search location, template and this new custom scope item -


Add all the fields required as part of query strings in this custom token resolver.



Add this scope item to the SXA search result component-



Now, you can browse the page and pass the key value in the query string, like this https://website/searchresult?name1=name1Value&name2=name2Value.
 and the SXA result component will provide the updated result :)

No comments:

Post a Comment