Upgrading legacy facets

In Funnelback v15.12 the faceted navigation interface was enhanced to provide product support for configuring different types of facets. As part of the enhancement, certain aspects of the data model was also updated. Any existing facets from before 15.12 were automatically updated to the new model and existing facets will work in the same way they always did. However, the upgrade process does not automatically update the Freemarker templates i.e. simple.ftl with support for new facets. This means, if you wish to create new facets, then your Freemarker templates will need to be updated and legacy facets should have their behaviour reviewed.

The categories element contained within the Funnelback data model (beneath response.facets) and the faceted navigation Freemarker macros (used by the pre-15.12 version of faceted navigation) were removed in v16.0.

The search dashboard will display a warning message about legacy facets on the faceted navigation listing screen if any legacy facets are detected in the configuration, or if any templates include old faceted navigation template code.

It is recommended that all legacy facets are upgraded. Freemarker templates containing the old facet macros must be upgraded and any integrations with the search JSON or XML endpoints should be checked to ensure they are still working correctly.

Note: the warning message in the search dashboard may disappear when each of the legacy facets have been updated but not published. Ensure that you publish your facets when you are satisfied with the changes.

Upgrade process

To use the new facets after upgrading:

  1. Update your display of facets.

  2. Review or replace your legacy facets.

  3. Create new facets.

Finally, don’t forget to publish all the facets and templates that are upgraded by selecting the publish or save and publish buttons within the editor screens.

Updating Freemarker templates

The following steps outline what needs to be replaced in templates that are based on the default template (simple.ftl). Other custom templates that utilise faceted navigation will require similar changes and the steps below should be considered as guidance in what needs to be upgraded.

In each template find and replace the following sections:

  1. Import the new css styling. Add the following to the <head> tag.

    <link rel="stylesheet" type="text/css" href="${GlobalResourcesPrefix}css/funnelback.faceted-navigation.css" />
  2. Add support for tabbed facets by replacing

    <div class="col-md-<@s.FacetedSearch>9 col-md-push-3</@s.FacetedSearch><@s.FacetedSearch negate=true>12</@s.FacetedSearch>">

    with

    <#-- Display tabbed faceted navigation -->
    <#if response.facets??>
      <#list response.facets as facet>
        <#if facet.allValues?size gt 0 && facet.guessedDisplayType == "TAB">
        <div class="col-md-12">
          <ul class="nav nav-tabs">
            <#list facet.allValues as value>
            <li role="presentation" class="${(value.count?? && value.count lt 1)?then('disabled', '')} ${value.selected?then('active', '')}">
              <a href="${value.toggleUrl}" title="Show only '${value.label}'">${value.label}<#if value.count??> (${value.count})</#if></a>
            </li>
            </#list>
          </ul>
          <br>
        </div>
        </#if>
      </#list>
    </#if>
    <div class="col-md-${response.facetExtras.hasNonTabFacets?then('9 col-md-push-3', '12')}">
  3. Add support for displaying the set of applied facets above the results. Insert the following after the </div> with id search-result-count.

        <#-- Display applied faceted navigation -->
        <#if response.facetExtras.hasSelectedNonTabFacets>
          <div id="search-facets-breadcrumb"><span class="facets-breadcrumb-label">Refined by:</span>
            <#list response.facets as facet>
              <#if facet.selected && facet.guessedDisplayType != "TAB">
                <ul class="facets-applied list-inline">
                  <li><a class="btn btn-xs btn-link" href="${facet.unselectAllUrl}" title="Remove all '${facet.name}' refinements">
                    <small class="glyphicon glyphicon-remove"></small>
                    <small class="hidden">&#10060;</small><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                    ${facet.name}
                  </a></li>
                  <#list facet.selectedValues as value>
                    <li><a class="btn btn-xs btn-info" href="${value.toggleUrl}" title="Remove '${facet.name}: ${value.label}'">
                      <#if facet.guessedDisplayType == "SINGLE_DRILL_DOWN" && value?counter != 1><span>&#8627;</span></#if>
                      <small class="glyphicon glyphicon-remove"></small>
                      <small class="hidden">&#10060;</small><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                      ${value.label}
                    </a></li>
                  </#list>
                </ul>
              </#if>
            </#list>
            <#if response.facetExtras.unselectAllFacetsUrl??>
              <a class="btn btn-xs btn-default" href="${response.facetExtras.unselectAllFacetsUrl}" title="Remove all refinements">
                <small class="glyphicon glyphicon-remove"></small>
                <small class="hidden">&#10060;</small><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                Clear all filters
              </a>
            </#if>
          </div>
        </#if>
  4. Change to the new facet model. Replace the entire <@s.FacetedSearch>...</@s.FacetedSearch> section with:

    <#-- Display faceted navigation -->
    <#if response.facets?? && response.facets?size gt 0>
      <div class="col-md-3 col-md-pull-9 hidden-print">
        <h2 class="sr-only">Refine</h2>
        <div class="panel-group flb-panel" id="search-facets" role="tablist">
        <#list response.facets as facet>
          <#if facet.allValues?size gt 0 && facet.guessedDisplayType != "TAB">
          <div class="panel panel-default">
            <div class="panel-heading">
              <a class="panel-toggle" data-target="#facet-${facet?counter}" data-toggle="collapse" aria-expanded="true" aria-controls="facet-${facet?counter}">
                ${facet.name}
              </a>
              <#if facet.selected>
              <a href="${facet.unselectAllUrl}" class="btn btn-link btn-sm" title="Remove all '${facet.name}' refinements">
                <small class="glyphicon glyphicon-remove"></small><small class="hidden">&#10060;</small> Clear all
              </a>
              </#if>
            </div>
            <div class="list-group panel-collapse collapse in" id="facet-${facet?counter}">
            <#list facet.allValues as value>
              <#if value?counter == 9><div id="facet-list-${facet?counter}" class="collapse"></#if>
              <#assign isDisabled = value.count?? && value.count lt 1 && !value.selected />
              <a class="list-group-item ${(value.selected)?then('selected-' + facet.guessedDisplayType?lower_case, '')} ${isDisabled?then('disabled', '')}" href="${isDisabled?then('#', value.toggleUrl)}" title="${(value.selected)?then('Remove', 'Refine by')} '${facet.name}: ${value.label}'">
                <#-- Show the category value e.g. 🔘 Bob, ☑ Bob, ❌ Bob  -->
                <span class="item-label">
                  <#if facet.guessedDisplayType == 'RADIO_BUTTON'>
                    <span class="${value.selected?then('glyphicon glyphicon-record', 'radio-unchecked')}"></span>
                    <span class="hidden"><#noescape>${value.selected?then('&#128280;', '&#9711;')}</#noescape></span><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                  <#elseif facet.guessedDisplayType == 'CHECKBOX'>
                    <span class="glyphicon glyphicon-${value.selected?then('check', 'unchecked')}"></span>
                    <span class="hidden"><#noescape>${value.selected?then('&#9745;', '&#9744;')}</#noescape></span><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                  <#elseif value.selected>
                    <#if facet.guessedDisplayType == "SINGLE_DRILL_DOWN" && value?counter != 1><span style="margin-left: ${(value?counter - 1) * 10}px">&#8627;</span></#if>
                    <small class="glyphicon glyphicon-remove"></small>
                    <small class="hidden">&#10060;</small><#-- Fall back to Unicode chars if bootstrap is unavailable -->
                  </#if>
                  ${value.label}
                </span>
                <#if value.count?? && !value.selected><span class="badge">${value.count}</span></#if>
              </a>
              <#-- Limit the number of category values shown to the user initially -->
              <#if !value_has_next && facet.allValues?size gt 8>
                </div>
                <a class="list-group-item collapse-trigger collapsed" data-target="#facet-list-${facet?counter}" data-toggle="collapse"></a>
              </#if>
            </#list>
            </div>
          </div>
          </#if>
        </#list>
        </div>
      </div>
    </#if>
  5. Remove the JQuery based more/less script that looks like this:

    // Faceted Navigation more/less links
    var displayedCategories = 8;
    jQuery('div.facet ul').each( function() {
        jQuery(this).children('li:gt('+(displayedCategories-1)+')').hide();
    });
    jQuery('.search-toggle-more-categories').each( function() {
      var nbCategories = jQuery(this).parent().parent().find('li').size();
      if ( nbCategories <= displayedCategories ) {
        jQuery(this).hide();
      } else {
        jQuery(this).css('display', 'block');
        jQuery(this).click( function() {
          if (jQuery(this).attr('data-state') === 'less') {
            jQuery(this).attr('data-state', 'more');
            jQuery(this).parent().parent().find('li:gt('+(displayedCategories-1)+')').hide();
            jQuery(this).find('span').text(jQuery(this).attr('data-more'));
          } else {
            jQuery(this).attr('data-state', 'less');
            jQuery(this).parent().parent().find('li').css('display', 'block');
            jQuery(this).find('span').text(jQuery(this).attr('data-less'));
          }
        });
      }
    });

Re-ordering of facets

Upon using the new facet model in Step 4 above, the facets will be displayed in an order based on when they were modified. You may wish to update the template to control what facets are shown and the facet display order.

Review or replacing legacy facets

Legacy facets are identified within the search dashboard in two ways:

  • on the faceted navigation listing screen legacy facets are marked as type: legacy

  • when editing a legacy facet the category matching logic is listed as legacy

Legacy facets by default have the following configuration:

{
  "facetValues": "FROM_SCOPED_QUERY",
  "constraintJoin": "LEGACY",
  "selectionType": "SINGLE",
}

Which is how they previously worked. However, many implementations made use of custom hook scripts or macros to post process this model and remove values. If you updated your display of facets, its possible these custom modifications no longer work.

Once you upgrade a legacy facet, it can not be reverted to a legacy facet through the user interface. If you absolutely need to revert to a legacy facet, you will need to go directly to the faceted navigation API. You can use the Facet Configuration JSON on the Test facet page with the configuration updated to use the default legacy configuration values above.

To edit a legacy facet through the new interface:

  1. Browse to the search dashboard.

  2. Select the results page the facet belongs to.

  3. Select customize faceted navigation from the customize section.

  4. Select the legacy facet you wish to update.

  5. Follow further instructions depending on the type of facet below.

Hierarchical facets

For hierarchical facets, some macros manually removed or hid values at the same level.

Under the v15.12 facets, this can be replicated through the new interface by:

  1. Setting the Category matching logic to ALL selected values.

  2. Save your update and test that it behaves as you expect it to. Your legacy facet will now become a v15.12 facet and display accordingly.

Date facets

If you are upgrading a date facet and previously defined a sort using the faceted_navigation.date.sort_mode collection.cfg option you will need to make sure you select the desired sort mode from the facet editor screen as the collection.cfg option will no longer have any effect once the facet is updated.

GitHub replacement faceted navigation

If you are using the unofficial GitHub faceted-navigation code (faceted_navigation_v2.ftl, or the bundled hook scripts for sort and rename) then the following should also be addressed:

  1. The <@fbf.AppliedFacets> macro code can be replaced with the applied facets code detailed in the updating Freemarker templates section above.

  2. Any checkbox facets (<@fbf.Facet checkbox=true>) will need to be upgraded in the configuration editor and converted to filter on multiple category facets, selecting the ANY facet combine mode for equivalent behaviour. To replace a checkbox facet:

  3. Set the Selection type to multiple values.

  4. Set the Category matching logic to ANY selected value.

  5. Set the Scope to original query and other facets.

  6. Save your update and test that it behaves as you expect it to.

Your legacy facet will now become a v15.12 facet and display accordingly.

  1. Any sort options configured using collection.cfg should be converted to the corresponding sort option for the facet, available on the new editor screen and the collection.cfg facet sort mode options removed.

  2. Any defined renames will continue to work with the existing hook script, however updating the hook script to use just the rename code is recommended for efficiency.