Custom Sorting on Coveo Facet

I got to use a new feature of Coveo today because of new requirements. How fun, though the request that came through felt not needed and a corner case in terms of user experience. Pushing through limit is what we have to do if you have got super picky clients. ūüôā
The outcome is always precious as you get to learn something new. Here is the snapshot of what happened and how we were able to get it working per request and requirement.

Goal – Regardless of selections made on Coveo Facet because of url appending, the sort order of values available on facet stay per the field on which sort is based on.

Problem – The inherent default behavior of Coveo for better UX experience. To be honest, I actually like what they have in terms of grouping the values selected to the top and rest are sorted based on the sort field set up. But, we have to some how overwrite this default behavior.

Firstly, our front end contact explored why this happens and how coveo groups them the way it does inherently. The reason for special attention that these selected values get is because of the below. Thanks to our front end resource to find this out. Though we did not pursue anything in modifying this, it is always good to know the reasons behind how it is currently done.

https://developers.coveo.com/display/public/SearchREST/Group+By+Results

So, the reason is that when there is an appending in URL for a selection of a value or set of values in the facet, Coveo inherently tags these as Desired and hence the special treatment for those to be on the top.

For example, if your URL is like below may be because the user clicked on back button post user selected few values and moved on his journey, the user could land on pages like this or if marketing team linked off the user to a page with pre-selected values on a facet.  There could be more scenarios where your user would bump in to this situation, but to name a few.

Example URL – https://www.domain.com/press-releases-events-and-webcasts-landing#f:articlePostYear=[2017,2018]

The selected would be grouped on top like below due to the above explained reason.

Again, I like what Coveo does inherently and I suspect that this needs to overridden in simple cases, but, if you absolutely need to do this? Which is where we stood, I logged a Q and A below and luckily got a direction.  I only wish there were more graceful options, but, we took what we got as well, we had to overwrite this and there was no other option unfortunately.

If you are curious, follow the discussion here and I will explain more in detail how I actually implemented what was recommended by Coveo team.

https://answers.coveo.com/questions/12071/facet-values-mentionedgrabbed-in-allowed-values-be.html?childToView=12086#answer-12086

Steps I did to Implement this solution:

  1. Created a copy of default Coveo MVC view for facet and named it differently and placed under correct project specific location.
  2. Add the below line to the view created just above end of div tag in place for the facet
    data-custom-sort=”2024,2023,2022,2021,2020,2019,2018,2017,2016,2015,2014″>
  3. Note – With addition of this, we should be good on sorting with lower limit of 2014 to upper limit of 2014, based on your project data you could decide on good numbers for these limits. Only deal is post 2024, if this project has same implementation, we will need a code deploy to ensure sort works as expected.
  4. Use this new view item on presentation for the facet and select all appropriate values that a coveo facet would need on the templates affected or will use such type of facet
  5. That is it!! Post this change regardless if values are selected via url or not, the ordering will be based on data-custom-sort.

A different paradigm to bump up your site performance

You might have seen ample number of blogs, information and other resources out there that talk about the same topic.  But, I will try and make this in steps, so you have step-by-step guide all in here and you will surely spend less time scraping this data from 10 other places.
At the end, I will also list references on where I gathered all this from, so, you can refer and read more if you are an enthusiastic reader.

Have you heard about Google page speed extension?  If you did not, it is fine and I will not say you were living in a cave, in this functional desire world, I wonder why performance is always addressed at the very end of the tunnel in software life cycle.  I would go ahead and say a simple performance test plan should be part of every step of implementation.   It could be simple, but invasive.  It would help mitigate surprises which usually take time to resolve.

There are tons of tools out there as well to measure performance, but, I got hooked on to Google page speeds extension as it is easy to visualize and Google does do a good job of providing links to problems that were uncovered which would provide good insight to multiple solutions to the problem on hand.

Let us get this started –

1. First do not expect anything, ¬†don’t assume that your site is great or worst in terms of performance. ¬†Just download the extension of your favorite choice, I like the Google Page Speeds extension, but, you could run anything
2. Address to one’s Google put’s more weight on and are in red. ¬†I think that would be the critical one’s for sure. ¬†I did the following and saw immediate boost on numbers

Images and Images (Scaling and Optimization)

Firstly ensure you are scaling images on server side (Using Glass params) instead of depending on content authors to upload files of certain dimensions.  Though we tried to enforce this through documentation and collaboration, we still saw lapse.
Best way to battle this is use Glass params, then, regardless of whether recommended size is respected or not, your browser will not spend ton of time sizing and re-sizing images to fit your FEE needs(css)
Get the params based on FEE recommendations, the best thing to do is set width and let height be auto.

Example :@RenderImage(carouselSlide, m => m.Carousel_Image, new { @class = “carousel__image”, w = 684, width = 684 }, true, outputHeightWidth: true)
Please re-upload all imagery that is not respecting the dimensions.  This is important for performance and critical
Also, ensure good/web optimized imagery is loaded on to media library.  If tool complains that some images could do better in terms of performance, please re-upload the culprits.  Should be quick and the tool offers a download option on each of the offensive currently uploaded image.  Go to sitecore editor and do simple attach/detach.  You are done.
Make sure all your site specific css/js is minified and only those minified versions are accessed in your HTML or view code
Ensure web server has compression enabled. ¬†This is a simple check of a checkbox available on IIS under compression for a specific site(Ensure static compression is checked). ¬†Sometimes, if installation is not done correctly even if you checkbox this it might not work. ¬†Please ensure this works by looking at Network request and content-encoding attribute is being set as ‘gzip’. ¬†You can check in fiddler or on Google chrome by toggling the column display to show this response header. ¬†If you dont see gzip, please check with support once you ensure you have enabled the IIS settings properly and things seem off. ¬†This is v important for performance and must be ensured is working
Enable cache control on static resources on web.config to ensure you enable this after talking to your FEE team on what would be a good max age time.  It could vary based on multiple things and should be verified with your team.

After these steps, please follow sitecore performance tuning guide no less than Bible(link in references), go over all content delivery preferences and ensure you set all of them correctly.  When in question about something please check with your support team.

That is it, you just made your sitecore instance super efficient.  The tuning guide should also cover caching best practices which should bump up good performance benefit second load on wards.

Happy Sitecoring!!

Few Good References

https://developers.google.com/speed/pagespeed/insights/
https://sdn.sitecore.net/upload/sitecore7/70/cms_tuning_guide_sc70-72-usletter.pdf
https://chrome.google.com/webstore/detail/google-pagespeed-insights/edbkhhpodjkbgenodomhfoldapghpddk?hl=en
https://developers.google.com/speed/docs/insights/EnableCompression

Coveo Computed Field based on Sitecore Parent needs additional configuration

In some of our critical pieces of implementation revolving around Coveo renderings, due to our heavy hierarchical data for SEO benefit, we had multiple situations where we were having computed fields that read a piece of data from direct or in-direct parent of the current item in question in Sitecore.

It was working out perfectly, no additional load time or performance issue as indexing operation should fully load what we need on the current Sitecore item.  Until!!!

Recently, I ran in to this situation where say you have the whole hierarchical data already in play, which just means we did not create a child and did not publish the current item.  But, a scenario where a field that you are reading in computed field from parent actually changed.  In this situation, due to inherent behavior of how indexing would work, the parent would be re-indexed(As that is the item that is changed and hence added to delta by Coveo).  Now, we have child having an outdated computed field which in index memory still has stale data until you force build indexes or publish the child.

In real life scenario, you would take an item through workflow, in this case parent is changed, so it would enter the Sitecore workflow and will be pushed live.  But, in our case, every time a parent changes, we want the children also to re-index, so, we do not have situation of out-of-sync data.

So, additional configuration would be needed every time you have a scenario where a computed field say depends on some other item (like parent in our case or it could be something else in the sitecore tree as well).  To address this and ensure it works as expected in ideal scenario, we need to do below steps

1. ¬†Patch to¬†coveoItemProcessingPipeline and add a new processor – call it say ‘RelatedItemsPreProcessor’
2. Implement what happens in the processor, in our case we had to get all children and add it to List of items that Coveo re-indexes when this specific item is in delta.  Quick example below, it could be changed per your needs.  There is a basic example on Coveo documentation as well.

public class RelatedItemsPreProcessor : IProcessor<CoveoItemProcessingPipelineArgs>
{
public void Process(CoveoItemProcessingPipelineArgs p_Args)
{

SitecoreIndexableItem indexableItem = p_Args.Item as SitecoreIndexableItem;
if(indexableItem != null)
{
Item item = indexableItem.Item;

if(item.TemplateID.Guid.Equals(yourguidgoeshere))
{
if(item.HasChildren)
{
//Get all children using query
string query = string.Format(“descendant – or – self::*[@@templateid = ‘{0}’ ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬† ¬†]”, YourGUID);
var childrenOfItem = item.Axes.SelectItems(query);
if(childrenOfItem != null && childrenOfItem.Any())
{
foreach(var itm in childrenOfItem)
{
//Add the item to the output list
var itemToBeAdded = new SitecoreIndexableItem(itm);
if (!p_Args.OutputItems.Contains(itemToBeAdded))
p_Args.OutputItems.Add(new SitecoreIndexableItem(itm));
}
}
}
}

3.  Add this new processor in your Coveo.SearchProvider.Custom.config
<coveoItemProcessingPipeline>
<processor type=”namespace.RelatedItemsPreProcessor, Extensions” />
</coveoItemProcessingPipeline>

That is it! Post this simple steps, I can be rest assured that our data integrity would be in place post the workflow is implemented.  It is really easy to miss this in your implementation when most of your users are logging in using admin credentials for example and probably publishing sub-items every time they publish a change.  Keep an eye open for these kind of situations.

Review pipeline’s available here ¬†https://developers.coveo.com/display/public/SitecoreV3/Understanding+the+Indexing+and+Search+Pipelines;jsessionid=814B71D25E05C8684E2C029CEC416070

Sitecore Tagging Surprises

Sitecore tagging is immensely powerful out of the box starting Sitecore 7+.  You can tag any items( including bucketed items)
The below links will get you started on the basic features –

https://visionsincode.wordpress.com/2014/11/16/tagging-is-fun-in-sitecore/

https://community.sitecore.net/technical_blogs/b/sitecore_7_development_team/posts/content-tagging-with-sitecore-7

https://doc.sitecore.net/sitecore_experience_platform/content_authoring/managing_items/general/add_a_tag_to_an_item

Now, coming to the surprises…

  1. While working with the basics,  the __Semantics field on my local sitecore instance did not look anything like the articles explain.  After some pondering,  it turns out my ‘raw’ view was checked.  So, for the multi-value search field to behave and allow selection as expected make sure this is unchecked.
  2. After adding some new tags into tag repository(),  when i go back to tagging section of my item, I did not see the newly added tags in the selection list.   This is because tagging depends and reads from indexes.  when i re-built my indexes, I could then see the new one’s
  3. Since, tagging is so dependent on indexing,  selecting a good indexing strategy is very important.  Doing this operation only as often as needed and not overdoing goes long way.
To tackle #3,  it would be handy to know the default indexing strategies on master and web databases.  For master, the default is syncMaster and for web database, the default strategy is onPublishEndAsync.   You can apply different one’s as needed, but, in our case the defaults would just do it. 
You can read more on indexing strategies available through Sitecore OOTB on the below article, Good reads. ūüôā

https://doc.sitecore.net/sitecore_experience_platform/setting_up__maintaining/search_and_indexing/indexing/index_update_strategies

https://community.sitecore.net/technical_blogs/b/sitecorejohn_blog/posts/sitecore-7-index-update-strategies

https://doc.sitecore.net/sitecore_experience_platform/setting_up__maintaining/search_and_indexing/indexing/rebuild_search_indexes

Multi line Field With Embed code Experience Editor Issues

Media Media Media!!!!

Every website has media like videos for example where the brand and the company want to showcase and put their best foot forward. 
To Enhance content author experience, we thought it was a good idea not to give them the whole RTE, but, instead to avoid any more thinking than needed and to avoid experimentation, we though multi line text field was best option.  So, the content author can grab the embed code from where ever and toss it on the field we would suggest to them. 
All was good, jumped on with implementation, only to see that this field with embed code behaves totally wierd on page editor/experience editor mode sitecore 8.1 /Update 2. 
I researched else where saw nothing about this and decided to log a sitecore support ticket, it  turns out that starting sitecore 7.2, Experience editor trims any tags on multi line text field for security purposes. Bummer!! Sitecore advised us to to RTE instead, but, due to third party we were using even that was not a possibility as the embed code had video tags and script tags.  
We moved towards using basic fields as parameters that we would need like Video ID, Account ID, etc and ended up constructing the html on partial view.  So far so good. 
More reference details on why multi line field would not work on latest editions of sitecore from support – 
Sitecore encodes all tags in the single-line/multi-line text fields when you edit it in Experience Editor application and it’s an expected behavior.
This behavior was implemented since Sitecore CMS 6.6:
-Page Editor
 In 6.6 Update-3, the <renderField> pipeline was modified to HTML encode the field value when rendering single-line/ multi-line text fields (ref. no. 327905). This did not work correctly in the Page Editor which displayed the encoded value. And if the user saved the page, the already encoded value would be HTML  encoded again. (384997)