Monday, April 27, 2020

SXA Search- tips for troubleshooting and fix

Open the browser and get the SXA query -

The query will look like this -


{
   "endpoint":"//sxa/search/results/",
   "v":"{224530AA-968E-49AB-84B1-AFA3BB52A589}",
   "s":"{3758B0CF-81A6-4DD3-AF1F-9F27A13740D5}",
   "l":"",
   "p":2,
   "defaultSortOrder":"Title,Ascending",
   "sig":"dd",
   "itemid":"{9DF8EC39-57F4-48B4-A2A4-0F13BEF66C63}",
   "autoFireSearch":true
}


this query will be generated based on the scope items and settings like sort order etc.

Now,  check the solr logs -

Path - Your solr root directory/server/logs


Generate the SOLR query and try in Solr admin panel.

Example -

https://localhost:8983/solr/xp930_sxa_master_index/select?q=_template%3A(f6357da575384e9d8caaaa08fd345f5e)

Compare the data, in this example, I fund that the query was searhing for "searchable_b":true, but the actual data was "searchable_b":false,




The problem is that the

"searchable_b":false is false and the query is expecting it "searchable_b":true

Solution - by default all pages under home are searchable and for data items, basically we need to perform two things.

1. Add the _Searchable base template -



2. Go to settings and add the associated content.



Here is the result -

1. Before changes -

2. After changes -


Please let me know if you have any issue in SXA search-related components. I will happy to help :) 

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 :)