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

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”

Creating a Custom Facet in Coveo Hive – Part Two

In my previous blog post, I went over the basics of what I needed to do to set up a custom facet / component in Coveo Hive. In this blog post, I will take a deeper dive into how we can have our custom facet interact with the current Coveo query on the search page and manage Coveo state. Let’s get started.

Updating the Current Coveo Query

At this point, you should have a visible and interact-able facet. When a user enters a value into a field of your facet, checks a box, or makes some other type of facet interaction, you’ll want to make sure the Coveo search page reflects this interaction by showing updated search results. In technical terms, we can achieve this by adding our custom facet interactions to the advanced expression of the current query. We will need to do the following:

  1. Add code to process the query change.
    1. Update the Coveo state.
    2. Then, call executeQuery() to run the buildingQuery event, which will set the advanced expression and cause the search results component to refresh.
    3. Finally, log the search event to Coveo Usage Analytics (optional).

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

Creating a Custom Facet in Coveo Hive – Part One

Recently I was tasked with creating a custom facet for a client’s Coveo for Sitecore implementation. I was implementing Coveo Hive, which worked well and it was very simple to add components to the page in a modularized fashion. After reviewing the proof of concept search page, the client realized they didn’t like the date slider component at all, and asked that we instead have a facet with two fields, start date and end date, and each field would have a calendar picker.

I quickly found out this was not available as an OOTB component. I would have to learn how to create a custom component and allow it to be added to a search page. I would have to manage state, facet appearance and breadcrumbs for the new facet. Due to the complexity of this task, I will be creating a blog post series for each major part. In this first blog post, I will go through the basic setup of your custom facet and at the end of this post, you should be able to see and interact with your facet to some extent. Final code will be provided at the end of the final blog post in this series. Let’s get started!

It Starts With Javascript (Or Typescript)

When I first started working on this, Coveo’s documentation suggested creating a custom component using Javascript. It looks like they’ve updated their documentation. You’ll want to start here and pick one of the two paths. I went the JS path, so my code will be different from yours if you choose the TS path.

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

The coveo-facet-empty class

Recently I struggled through the creation of a custom component in Coveo Hive. My goal was to create a custom facet with two fields, start and end date, and get results to filter based on selections in those dropdowns, include breadcrumbs, etc. One issue that I ran into time and time again was my facet not appearing in the facets section. At first I thought the markup simply wasn’t generated, until I inspected the DOM and found it was there, but hidden.

 id="_..." class="CoveoFacet coveo-facet-empty"

I could immediately tell it was empty due to the existence of the coveo-facet-empty class (it makes sense), but I didn’t manually add this class. It turns out Coveo adds this class to any facet, but only for certain conditions:

  • The facet is not pointing to a field where it can get data, or the field simply has no data for ANY of the search results. This was not my problem.
  • The facet’s isFacet setting is set to false. Okay, so this one actually got me once – for some reason, my facet had the setting equal to false. It needed to be true, so I set it to true in the Cloud Platform -> Fields page, and the facet showed up. Later that week though, the facet disappeared again – so keep reading.
  • There is a syntactical error in the Javascript of your custom component. This happened to me several times. The console should explain what the issue is. Usually I was missing a curly bracket or something minute like that.

Hope this helps anyone having the same issue.