Showing posts with label query. Show all posts
Showing posts with label query. Show all posts

Monday, February 26, 2024

How to kill your Sitecore editor with one slash

 Is your page opening really slow in the Sitecore Content Editor?

I recently had an issue where one page (the homepage) of a site opened really slow in the Sitecore Content Editor. All other pages went very smooth, so it was clearly something particular to that page. And when I mean slow, I mean you click on the item and it takes one coffee for the editor to react. On the published site, there was no issue.

At first this was just annoying, but after a while you curious and annoyed and you want this fixed.

Debugging

The editor is pure Sitecore coding so how to start... one way is to enable the timing level for all events.
You will find this setting in the Sitecore.config file and if this is just for temporary debugging on a local instance you can alter it there. 

<!-- EVENT MAPS
      events.timingLevel =
        none   - No timing information is logged for any of the events (no matter what their local settings are)
        low    - Start/end timing is logged for events with handlers. Local settings override.
        medium - Start/end timing is logged for all events. Local settings override.
        high   - Start/end timing is logged for all events. Also, start/end for each handler is logged. Local settings override.
        custom - Only local settings apply. Events without settings are not logged.
    -->
  <events timingLevel="custom">
So we will change the timingLevel for all events to "high" instead of custom and restart the site.

Now let's keep an eye on the logs while we go back to the editor and open our homepage item. And bingo.. we get a lot of information in the logs but what was really interesting were the lines that said "Long running operation".

Long running operation

20204 11:29:46 DEBUG Long running operation: renderContentEditor pipeline[id={98E57B60-071A-44D1-A763-B6C1BCE0C630}]
20204 11:29:48 DEBUG Long running operation: getLookupSourceItems pipeline[item=/sitecore/templates/Feature/.../CtaRenderingParameters/__Standard Values, source=query:/$site/Data/.../*]
20204 11:29:50 DEBUG Long running operation: getLookupSourceItems pipeline[item=/sitecore/templates/Feature/.../CtaRenderingParameters/__Standard Values, source=query:/$site/Data/.../*]
20204 .....
20204 11:30:19 DEBUG Long running operation: getLookupSourceItems pipeline[item=/sitecore/templates/Feature/.../CtaRenderingParameters/__Standard Values, source=query:/$site/Data/.../*]
20204 11:30:19 DEBUG Long running operation: Running Validation Rules

Apparently we have a long running operation getLookupSourceItems and we are not doing this once, but a dozen times on the home page.  This is our cause - and ok, I exaggerated when I said a coffee but even a minute feels long to open an item.

So, we now now that the CtaRenderingParameters are doing something very slow and it is caused by the source for a field.

In that template I found (even two) droplist fields that use the same source: 
query:/$site/Data/Link Types/*

I am not writing Sitecore queries every day so I admit it took me a few minutes to figure out what was wrong with this thing. It looks like a normal query that fetches all items in a certain path. And it's just a small detail, but a significant one: the first slash should not be there 

query:$site/Data/Link Types/*

This works much smoother and the issue was fixed.


$site

We are using a resolveToken in the source. In the Sitecore docs you will find the list of available tokens and we are using $site here to get the path to the current site. 

But that path will already start with a slash. So although you (probably) want your query to start with a slash, you do not want it to start with two. As described in the query axes, one slash will give you the children (and will form one path) but two will give all descendants. And that is in most cases a very costly operation.


Conclusion

Be careful when putting sources - especially when using tokens. You might not notice anything bad or slow when just one component is added, but it can add up to a really slow item. Use the events timing level to get debugging information.


Wednesday, January 2, 2019

Extending SXA search queries with SearchQueryTokens

Tokens in Sitecore Search

SXA comes with a set of tokens that you can use in search queries to apply additional search filters.

They can be very useful when creating queries that will be used on multiple pages. In my case, I wanted to extend a search scope with some business logic based on the value of a field on the current page.

The "ItemsWithTheSameValueInField" came close to what I needed but not entirely..  but these tokens can be easily extended. Let's implement our own ResolveSearchQueryTokensProcessor!

ResolveSearchQueryTokens Processor

To start my implementation, I had a (dot)peek at the ItemsWithTheSameValueInField source code as mine would be rather similar. First thing to do is define the key for our SXA token:
[SxaTokenKey]
protected override string TokenKey => FormattableString.Invariant(FormattableStringFactory.Create("{0}|FieldName", "YourTokenName"));

Next thing is override the Process method:
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))
    {
      var fieldName = model.Value.Replace(TokenPart, string.Empty).TrimStart('|');
      var field = args.ContextItem.Fields[fieldName];
      if (field?.Value != null)
      {
        args.Models.Insert(index, BuildModel(fieldName, field.Value));
      }

      args.Models.Remove(model);
    }
  }
}

Let's see what is happening here:

Find our SearchStringModel

The code loops over all the SearchStringModel objects in the arguments. Each SearchStringModel defines a part of the query and has a value, a type and an operation.  When looking at the search box interface like in the above image:
  • the type of the SearchStringModel is the first part (in the image above that would be "Sxa") 
  • the value of the model is the second part (e.g. "ItemsWithTheSameValueInField|FieldName")
  • the operation is "must", "should" or "not"

So we search until we find a model of type sxa. The ContainsToken function adds this check to make sure we have our the token that corresponds with our code:
protected override bool ContainsToken(SearchStringModel m)
{
  return Regex.Match(m.Value, FormattableString.Invariant(FormattableStringFactory.Create("{0}\\|[a-zA-Z ]*", "YourTokenName"))).Success;
}

If the model is found, we fetch the provided FieldName and test if the context item has a value in this field. If that is the case, we build a new SearchStringModel to be inserted in the list. The current model is removed as that has been handled.

Build a SearchStringModel

protected virtual SearchStringModel BuildModel(string fieldName, string fieldValue)
{
  var name = SomeBusinessLogicWithFieldName(fieldName);
  var value = SomeBusinessLogicWithFieldValue(fieldValue);
  return new SearchStringModel("custom", FormattableString.Invariant(FormattableStringFactory.Create("{0}|{1}", name, value)))
  {
    Operation = "must"
  };
}

I am building a SearchStringModel based on the field name and value. The model type is "custom" as we are not using predefined Sitecore fields. You can test such queries in a search box as well. If you would use them in a SXA scope, the scope string would look like "+custom:fieldName|fieldValue".


ResolveSearchQueryToken configuration

To tell Sitecore that we have our own tokens, we have to add the processor to a pipeline:
<sitecore>
  <pipelines>
    <resolveSearchQueryTokens>
   <processor type="YourNameSpace.YourType, YourAssembly" resolve="true" patch:before = "*[1]" />
    </resolveSearchQueryTokens>
  </pipelines>
</sitecore>
With this patch I am including our tokens as the first one in the list.

Once this config patch and the code is available, your Sitecore instance will detect the SxaToken and it will be available in the search list once you type "Sxa".


Conclusion / Issues

I have used this succesfully in scopes with SearchResults components to make them aware of the context item (adapting the actual query based on a field value of the current page). You can of course make the code as complex as you would like
  • pass more parameters in
  • create multiple models based on your data
  • ...

I did find one issue though: in the SearchResults component we have a ContextItem. But that is not always the case. Our code will not break (we do check for null) but the extra filtering is not happening in that case. Sxa's checklist facet component -in 1.7.1- does not send the ContextItem and it does make sense that a facet uses the same data as the corresponding results component.
I've created a Sitecore Support ticket to fix this and did get a patch (ref. 301406).

But (again) a very nice extension point for the Sitecore Experience Accelerator.

Wednesday, April 19, 2017

Sitecore SXA: PageList component with an Item Query

Sitecore Experience Accelerator (SXA) PageList component & Item Queries

Our requirement was simple: 
Display a set of fields from a list of items, based on a single template under a root item.
We checked the available components in SXA to see if we could do this in a decent way (and without any custom code) and found the PageList component:
Displays lists of pages by predefined or composed queries.

Item Queries as from SXA 1.3

We are using SXA 1.3 and in that version we can define Item Queries in the Settings of the site:


As you can see here we created an Item Query called "Speakers".
We used the "Build query" option on the Query field to create a query to select the desired template at a location. We could extend this with more clauses if needed.

The query build tool looks very familiar to editors who are used to work with the search tools in Sitecore and creates a search index query. You can see the resulting query in the Crawling log file.


Page List

The Page List component has a few options to determine the resulting items. 

Datasource

The "datasource" field also has a "Build query" option. Our first attempt was actually to use this to build our entire query but that failed as we did get multiple results and Sitecore does not like that in the Datasource field. So you have the options to simply select a single item as source or use a query to determine the datasource (but make sure that query does not return more than 1 item or the control will fail). You can combine the item datasource with a query (like the predefined "Children"). 

But in our case we didn't need a datasource as we had set everything on the query - not exactly sure how everything is handled behind the scenes, but as far as I could see they used a combination of the datasource and the query so a single index query seems a better option here.

Source Type

This is the option we did use - by creating our "item query" we added a new option to this dropdown (next to the standard ones):
We select our item query "Speakers" as source type. Save, publish, ..  and there is our list with the exact items we wanted.


Fields - Variants

In order to display the fields we want in the list, we can use Variants. How to create a variant is explained on the Sitecore doc site. In short:

  1. Go to /sitecore/content/[tenant folder/tenant/site/...]/Presentation/Rendering Variants/Page List
  2. Create a new Variant Definition
  3. Add all the VariantFields you want (mark them as link if you want them to link to the underlying item - for a page list you probably want at least one of them to do so)
  4. Select your newly created variant in the Styling properties of the control on your page

Credits

Thanks to Dawid (daru) & Adam (adamnaj) for the support on Sitecore Slack - the SXA channel there is a real good place to get help!  Next to Sitecore Stack Exchange of course, already having 58 questions on the SXA tag as we speak (and apparently even one where item queries are covered - d'oh).

Tuesday, March 1, 2016

Query.MaxItems in Sitecore 8.1

Small tip for people using Query.MaxItems and upgrading to 8.1


Setting the maximum number of results from a Query

As you might know Sitecore has a setting called "Query.MaxItems". The default value of this setting is 100 (well, in the base config file). Setting this value to 0 will make queries return all results without limit - which might (will) have a negative impact on performance in case of a large result set.

We aware that this setting not only influences your own queries, but also some api calls (Axes, SelectItems, ...) and Sitecore fields that use queries underneath. It does not affect fast queries.


The Query.MaxItems value in Sitecore 8.1 : 260

Using queries is in lots of cases not a good idea and as a fan of indexes I (almost) never use them myself but some questions came up when people were upgrading so I decided to blog this little tip: 
in Sitecore 8.1 the Query.MaxItems value is patched in Sitecore.ExperienceExplorer.config and set to 260
If you patched the value yourself and did not use a separate include file (as you should!) and did not take care that your include file comes at the end (prefix with z is a common trick - using a subfolder starting with z- is a better one) this new value of 260 will overwrite yours.