Creating a Coveo facet with Sitecore sub-domains and external sites as options

Take this example: you have multiple sub-domains hosted in your Sitecore site, and each one has its own node under Content and its own Home item. You also have various external sites not hosted in Sitecore as completely separate sites. You’re building a search page with Coveo and want to develop a basic facet to list all of your internal sub-domains and external sites so that the end-users can filter pages and content by site. How would one do this? In this blog post, I will explain. Disclaimer: When I did this, I was working with Coveo for Sitecore v4.1 – Hive Framework, so if your version is much different, it’s possible not everything in this post will be accurate. Additionally, please note that the XML/HTML code snippets in this blog post will have the greater than/less than symbols stripped out because WordPress can’t handle those.

Create a Coveo Field for Your Facet

Every new facet you create will need its own field, so lets create one. This is going to be a string, single-value field representing the name of a particular site (i.e. “Store” or “Marketing”, etc). In your Coveo.SearchProvider.Custom.config file, add a new field entry in the fieldMap/fieldNames node. In my case, my field’s name was aopaSiteName, so that naming convention will be used in other parts of this post as well. Your field entry should look something like this:

fieldType fieldName="aopaSiteName" settingType="Coveo.Framework.Configuration.FieldConfiguration, Coveo.Framework" isExternal="true" isFacet="true" /

Note that the field definition has isExternal="true" – this specifies that the field is not a normal Coveo for Sitecore field, so the field name won’t need to be converted. So if you put isExternal="false", your field name will become something like fmyfield99999 instead of just myfield. I suggest you keep this setting true to make development easier. Then, isFacet=true is needed so that this field can be used in a facet.

Creating a Computed Field

First, let’s focus on your internal content. What we need to do is actually rather simple: first, set up a basic computed field. See this page for basic information on how to do that with Coveo, and this page for a code sample if you’re not sure how the code for this would look. Inside the ComputeFieldValue method (see the second link above), you’ll want to get the indexable item, then the actual Sitecore item, and perform null checks on both so you have something you can work with:

        public object ComputeFieldValue(IIndexable indexable)
        {
            var indexableItem = indexable as SitecoreIndexableItem;
            if (indexableItem == null) return "Unknown";

            var item = indexableItem.Item;
            if (item == null) return "Unknown";

            ...
        }

Then, you just need to return a string value for each sub-domain node under Content. You can do this in multiple ways; using Glass is advised if possible. I decided to simply check the path and return the appropriate string for that path:

            ...

            if (item.Paths.Path.Contains("Sites/AOPA Main") ||
                item.Paths.Path.Contains("sitecore/media library")) return "AOPA";
            if (item.Paths.Path.Contains("Sites/Insurance")) return "Insurance";
            if (item.Paths.Path.Contains("Sites/Finance")) return "Finance";
            if (item.Paths.Path.Contains("Sites/PPS")) return "PPS";
            if (item.Paths.Path.Contains("Sites/You Can Fly")) return "You Can Fly";
            if (item.Paths.Path.Contains("Sites/Foundation")) return "Foundation";

            return "AOPA";
        }

Next, you’ll need to add a computed index field reference in your Coveo.SearchProvider.Custom.config file, under fields hint="raw:AddComputedIndexField", like this:

field fieldName="AOPASiteName" sourceField="AOPA.Library.Search.CoveoFields.SiteName, AOPA.Library">AOPA.Library.Search.CoveoFields.SiteName, AOPA.Library

Now, rebuild the Coveo indexes so that new field gets added to the index.

Adding Mappings on External Sources

In this example, since we’re building a facet with options showing each internal and external site, you’ll want to add a field mapping for each external site that you want in that list. You can add one by selecting a source and clicking the three dots, then click Manage mappings… Add a new mapping and select the field you created earlier. Then for the rule, simply put the name of the current site in plain text (i.e. “Blog”). Refer to online documentation for how to create a mapping in more depth. Once done, your mapping should look something like this:

aopa site name mapping

Now, add the field as a mapping for all your other external sites, then (preferably once you’ve added all mappings necessary for other features), re-build the source for your external site.

Reference Your External Sources

We need to find a way to reference your external sources in the search interface. Again, I did this with Coveo Hive, so if you are using the legacy framework, what you need to do will be different (and unfortunately I can’t help you there). First off, if you haven’t already created a custom version of your Search Interface view (usually located at /Views/Coveo Hive/Search Interfaces), do that now and place it under /Views/Coveo Hive/Custom/Search Interfaces and create any new directories necessary.

Then, in your Search Interface view, in the main div which iterates through the raw properties, add a new div with class CoveoForSitecoreExternalContent and a data attribute of data-sc-sources, and make the value of that attribute a comma delimited string of the names of your external sources. For example, here’s mine:

div class="CoveoForSitecoreExternalContent" data-sc-sources='AOPA Airports Directory,AOPA Blog,AOPA Brightcove Videos,AOPA Hangar'>
The names referenced there need to be exact as they are in the cloud platform. As of the writing of this post, source names in Coveo Cloud V2 can’t be changed after the source is created, so no worries regarding a source getting renamed and then you having to update it in the view. Here is where you might want to put a little extra effort in, perhaps find a way to render the value of a Sitecore field here so you can manage this string from Sitecore instead.

Completion

Finally, add a new facet to your search interface using the Experience Editor and select the field you created (or type in the field name). Once the facet renders it should show you options for all the sites you referenced in your code or in the source mappings.

sites facet completion

Advertisements

MediaCreatorOptions.KeepExisting is gone in Sitecore 9

I have a module I’ve been working on that sets the MediaCreatorOptions.KeepExisting setting to false. This module has several different versions for different versions of Sitecore, so there is always a version of it that works in any Sitecore version. In the master branch of the code repository we had version 8.1 (initial release) of Sitecore.Kernel.dll in the references. This has led to an interesting revelation regarding this KeepExisting property.

In Sitecore 8.1 Update 2 (rev. 160302), Sitecore deprecated KeepExisting but it could still be used in code. Instead, you were asked to use OverwriteExisting. When our module ran on this version or later (up to the final revision of 8.2), no errors occurred so we were oblivious to anything being wrong. Since our solution had 8.1 initial release DLLs referenced, we did not see a warning in Visual Studio.

Anyway, KeepExisting was still allowed all the way until Sitecore 9.0 (initial release) when they removed the property altogether. So when we began testing our module in Sitecore 9, we noticed this error:

Method not found: 'Void Sitecore.Resources.Media.MediaCreatorOptions.set_KeepExisting(Boolean)'.

So, the message is: If you have a module that uses KeepExisting, you’ll need to create a new release of it for Sitecore 9 and swap that out with OverwriteExisting. I had to do a lot of digging through the Sitecore.Kernel.dll file for each Sitecore version to find out when this was property was deprecated and when it was initially removed. Unfortunately, Sitecore did not include any of this information in their release notes / breaking changes. Boo!

When does Sitecore Certification expire?

An associate pinged me with this question the other day and I really had no idea. My first thought was that they didn’t expire, since I didn’t actually receive a certificate after becoming certified; instead, just an email reporting that I passed the exam, my score, and the instructor’s congratulations. Nothing about when to retake the exam or when my certification expires.

My first thought of who to ask about this was the Sitecore community on Slack. At first I received my mixed answers, but someone provided an accurate answer: every 3 years. This seemed kind of vague for me, so I decided to get an official answer. I ended up emailing back my instructor (after almost 3 years!) and asking about when it expires, and I got an even better answer:

Certification expiration dates are the same as the Mainstream Support End Date for the corresponding Sitecore version per Sitecore Product Support Lifecycle policy. You can find the mainstream support end date for every version of Sitecore here:  https://kb.sitecore.net/articles/641167

By checking the version of Sitecore I was trained on and its mainstream support end date, I was able to officially determine when my certification expires. However, you should make a point to get re-certified regularly as new versions of Sitecore come out with major changes. Hope this helps some of you out there!

 

 

The search query is denied because it originated from a Coveo quick view

A few months ago I was troubleshooting an issue with a failing Coveo index rebuild. Several times, while reviewing the Sitecore logs, I noticed an error occurring very frequently (~250k times in one log file) after the index rebuild process had started:

WARN  The search query is denied because it originated from a Coveo quick view. For more information, enable DEBUG logging for the 'Coveo.SearchProvider.LinqToCoveoIndex`1[TItem]' class.

I put this error message into Google only to find absolutely nothing. Our search page wasn’t even using quick views, so why was this error showing up in the logs? I enabled DEBUG logging but was not able to find a cause of the error. I contacted Coveo Support.

Wait… What is a Quick View Anyway?

Basically it’s a feature of Coveo that lets you preview a page in the index, quickly. You can read more about it here. Even if you aren’t using quick views on your search page, Coveo still uses and renders them. For example, if you open an indexed item’s properties in the Content Browser in Coveo Cloud, you will see a quick view tab. Upon opening that tab, Coveo generates a preview of your page. It’s much like opening the page manually in a browser, except the page renders much faster in a quick view.

Understanding the Issue

When a Coveo index is rebuilding, Coveo visits the item to fill its quick view. If Coveo is visiting a page where you have a very complex component or module running, that might prompt this error to occur. For example, in my client’s site they have a component named Related Articles, which basically shows a list of other articles that are related to this one. This site happened to have hundreds of thousands of articles, all which needed to be indexed, so the Coveo crawler was hitting these articles and their related articles over and over again.

Solutions

Coveo Support provided an article with a potential solution. Apparently, adding a Request.UserAgent check in complex components on indexed pages is one option. This can be placed on a view, model or sublayout, as the article states, but I also found out that you can add it to your back end code, too; and in my case, that seemed like the best thing to do. In the code that gets the related articles, I put this in at the very beginning of the method:

    if (HttpContext.Current != null && HttpContext.Current.Request != null && HttpContext.Current.Request
        .UserAgent.Contains("Coveo Sitecore Search Provider"))
    {
        return null;
    }

After rebuilding the indexes, the quick view warning was suddenly gone.

Another option Coveo provided was to disable queries from quick views completely. You could add the code in Step 1 of this article to your custom search provider config, but supply a value of false. As the document states,

This setting acts as a master switch. If the setting is set to false, the search queries are never executed in the context of the quick view.

Conclusion

The point is, if you have any complex logic running on a page you know you are indexing, you need to handle that via one of the two above solutions. This could speed up your rebuild time too.

Creating a Custom Facet in Coveo Hive – Part Three

This is the 3rd (and most likely final) blog post in the Creating a Custom Facet in Coveo Hive series. If you’ve completed parts one and two, you’ve got a working custom facet that can be interacted with and can modify the current query and state, and update the search results that appear on the page. The last big piece of functionality, in my opinion, is setting up the facet breadcrumbs. This step is totally optional, and you should check with project management to make sure it falls within scope and the client requested to implement.

I struggled on this piece greatly because I wasn’t able to find any documentation on how to do this for a custom (Hive) facet. I had to contact Coveo Support, who then referred me to a Typescript file in their GitHub repositories which had the code for setting up breadcrumbs (albeit in a language I wasn’t too familiar with). I didn’t quite understand how the code worked, even after getting past the language barriers, and I wasn’t sure which parts I actually needed in my code. Thus, I created this blog post to answer some of those questions, for those that are in my same shoes at some point in the future.

What Functionality Do We Need?

Before thinking about exactly what code is required to implement breadcrumbs, it may help to think about what our goals are, on a high level. If you are unfamiliar with Coveo breadcrumbs, they are pieces of markup that are generated and added to the DOM when a Coveo facet value changes (an option is checked, a value is submitted, etc). By default they appear above the search results. This is often termed the “breadcrumb bar”.

breadcrumb bar

Continue reading “Creating a Custom Facet in Coveo Hive – Part Three”