Monday, May 22, 2017

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

Saturday, May 20, 2017

When you need more than filtered results

Recently, in one of our major Coveo renderings, there was a splurge of new requirements.  Well, no huge surprise there I think.  When powerful people meet in the same room, new ideas do come by, not such a good news to us developers who have scratched their brains and pulled up hours to wrap up the initial implementation.  Although, I believe it is a good venue when time is on your side to learn something new. :)
This is exactly what happened to us, the requirement sounds pretty simple, but took me few hours to find an optimum solution which solves the problem, but, also would not involve too many changes to existing logic in play.
Goal -   Get all the results that match the filters(facets) selected by end user with out the default pagination applied (Coveo has default pagination and only pulls the values entered in Number of results value given on the rendering properties).   After few hours of going over Coveo documentation, I found something though completely not the same as what I was looking for, it kind of sparked the similar wave of implementation.
I almost always start a Q&A either to confirm my thoughts or to see if there are any other ways to accomplish the same.   In this case, what i was going for just felt right and Coveo team member confirmed the same.  You can follow the trail here.

https://answers.coveo.com/questions/10368/do-we-have-allnot-limited-to-resultsperpage-result.html

So, basically to achieve what I was looking for, you need to fire a parallel query with exact same filters based on user interaction and simply change the results to an upper bound.  It would have been nice to actually set it to Totalresults, I have not tried that, but, could work when you get the value from the correct variable.

Here is a snippet -

Coveo.$('#<%= Model.Id %>').on(Coveo.QueryEvents.querySuccess,function(e, args){
  var query = args.query;
   query.numberOfResults = 2;
//Do any other modifications on the query
Coveo.SearchEndpoint.endpoints["default"].search(query).done(function(data) {
//do any other logic on the data up here
}
 });
});

That is it, we have everything we need plus a handler over all results.  I am pretty sure it would come up often hopefully it helps any one else.

Happy problem solving folks. :)