Thursday, May 7, 2020

Fix for Sitecore XP and commerce- The password for the account has expired.



Recently, We got this issue on one of the VM where we had Sitecore XP, and Sitecore XC installed.

After a quick investigation, were able to resolve it, here are the details for the quick fix.

The issue was because we had recently updated the password for a local user XX which we used to connect with the local machine with the VM services.

After resetting the password and grating the permission, We were able to resolve this issue.

Easy fix :)

Fix for Sitecore XP and commerce- Sitecore Experience Commerce - Shop CommerceEngineDefaultStorefront does not exist


I recently found this error while browsing the Sitecore commerce catalog item. After a quick investigation, I was able to fix it; below are the quick steps.


Make sure under this commerce settings - you have the same store name which had configured in the site group settings.

/sitecore/Commerce/Commerce Control Panel/Storefront Settings/Storefronts

Under this, the item name (Storefront name) should match with one of the site grouping name here.



Easy fix  :) 

Fix for Sitecore XP and commerce- Could not find and method - AddDataProvider (type: Sitecore.Data.DefaultDatabase)


Recently, I got this issue with the latest commerce local deployment. After a quick investigation, I was able to fix it, here is the step and fix details.

I checked the logs in both Sitecore XP and XC authoring and found below error (XC authoring)

00001 14:07:48 INFO Application startup exception
System.IO.FileLoadException: Could not load file or assembly 'System.IO.Compression, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'System.IO.Compression, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
   at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
   at System.Reflection.RuntimeAssembly.GetExportedTypes()
   at Sitecore.Framework.Configuration.Strategies.TypeStrategy`1.<>c.<Resolve>b__0_1(Assembly x)
   at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Sitecore.Framework.Configuration.Strategies.Filters.TypeEnvironmentFilter.Filter(IEnumerable`1 input)
   at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
   at Sitecore.Framework.Configuration.Strategies.StrategyBuilder`1.Using[TNext](IStrategy`2 next)
   at Sitecore.Framework.Runtime.SitecoreBootstrapConfigurationBuilder..ctor(String environmentName)
   at Microsoft.Extensions.DependencyInjection.RuntimeSitecoreServicesConfigurationExtensions.Bootstrap(ISitecoreServicesConfiguration sitecore, IServiceProvider hostServices, String environmentName, Action`1 config)
   at Sitecore.Commerce.Engine.Startup.ConfigureServices(IServiceCollection services)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()


The issue was due to the wrong version of System.IO.Compression ,

I had updated the right version and that resolved the issue.

that's an easy fix :)


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

Saturday, March 7, 2020

Implementation of dimension filter in Solr search for the eCommerce website

 



Solr Dimension Token Filter
After reading this article you will have the context around the dimensions logic in the eCommerce domain will know the value they provide for search and also get enough technical details to modify, apply, or remove the filter from a solr instance. 

What is a Dimension?

Some products have their size as a part of their name in the format of width x length x-height.
Let’s say one product has (66 x18 mm 5.4m) is known as product dimensions. Product dimensions can have 2 or three parts and the x is optional to put.
On the search page, customers might search these products with the dimensions by providing other measuring units, or providing a different dimension parts such as:
  • 6.6x1.8x540cm
  • 0.066x5.4m
  • 0.018m
 The solution for this requirement is to generate all of the permutations and variants of the size and store them so that they can be searched.
This can be achieved by implementing a custom token filter that we will explain in the next sections.

A brief overview of Solr analyzers, tokenizers, and token filters

Analyzer

An Analyzer examines the text of fields and generates a token stream.
They are used both during ingestion, when the document is indexed and at query time.
Analyzers may be a single class or them maybe composed of a series of tokenizer and filter classes.

Tokenizer

Tokenizer break field data into lexical units, or tokens.
for example, a tokenizer breaks the "Bosch 650W Impact Drill" to the following tokens:
"Bosch", "650W", "Impact", "Drill"

Token Filter

Filter examine a stream of tokens and keep them, transform or discard them, or create new ones.
Tokenizers and filters may be combined to form pipelines, or chains, where the output of one is input to the next.
Such a sequence of tokenizers and filters is called an analyzer and the resulting output of an analyzer is used to match query results or build indices.
For example, the lower case token filter converts the "Bosch" to "bosch"
Analyzers can be applied on fields and are defined in the schema.xml file in the config set: 
An example of an analyzer in the project:

Implementation of dimension filter

All of the tokenizers or filters above have been implemented in java and their source code is available in Apache GitHub repository (apache/lucene-solr).
For instance SynonymGraphFilter code.
For Dimension filter our implementation is available in the solr config set repository.

It is tried to decouple solr from the dimension the logic so that even without any solr knowledge, the logic of dimension generation can be modified.
The DimensionGenerator class in the code accumulates dimension parts into one size class for instance "1cm x 2cm x 3cm" and then generates all of the equivalent sizes indifferent units
For example, if the field value contains "1cm x 2mm" this class will generate
the following 36 tokens to be available for searching:




For 3-Dimensional sizes, this class creates tokens having all of the permutations in a similar manner having the same logic.
i.e., 10cm x 25mm x 1.5m => 234 tokens including 10x2.5x150cm an so on.

After the implementation, the code is built and a jar file as output is generated which is stored in the repository besides the code.


How to deploy the dimension filter into a Solr instance

Deploying the dimension filter comprises three main steps:

1- Deploying the jar file

The process of putting the jar file in the solr instances is manual at the time of creating this document (October 2018). It's planned to use Solr Cloud facilities for this purpose. (Adding Custom Plugins in SolrCloud Mode)
For now, the process is to take the jar file from XXX-solr-configset repository and copy it to each solr node. It's a one time task per solr installation, or after having a new version of the file available.

Source 

\XXX-solr-config-sets\solr-analysis\jar\solr-analysis-6.6.2

Target

\solr\solr-6.6.0\dist 

2- Loading the jar file

This step is done via configuration in the solrconfig.xml file in the solr-configset repository. This task is also a one time task and it is already done in the repository.

3- Adding the custom filter to the analyzer

Now that we have the jar file copied and loaded, the filters inside that can be used in any analyzer. The dimension filter is used in the text_with_keyword_analyzer.  

How to disable the dimension filter

If the jar file is not copied to servers or for any reason the dimension filter should be disabled, commenting out the filter would all that needs to be done.
Of course, a deployment of Solr config set is required for the change to take effect. 

Modifying the Dimension filter

The java project is created and added to the config set repository.
In order to build the project take the following steps:

1- IDE installation

Install the IntelliJ Idea which is an IDE for java projects. During the installation, it will guide you through JDK installation.

2- Open the project

Using the IDE open the solr-analysis folder under XXX-solr-configset repository.
It should be immediately ready for run as a test console app is added to the solution to execute the dimension filter and show the results on the screen.

3- Execute

Hit Ctrl+F9 to build the project when you opened the project in your IDE.
Then, there is a java console app in the project just for executing and testing the code that you can find in the src/Program/Main class. 
Select the file and choose the Run in the menu or press Shift+F10 to execute the console app and you shall see the output of the application:

Friday, March 6, 2020

Catalog Item of the Storefront catalog Configuration cannot be found.

 Recently, I found this issue on Vanilla Sitecore XC setup, After an investigation, I came to know it's because of missing catalogue setting under the storefront.




 to fix that, I have selected the right catalog and it resolved the issue.