Using Funnelback to produce an A-Z listing

Managed by | Updated .


Funnelback is frequently used to provide A-Z listings for staff directories, service listings, etc.

'Dumb' A-Z browse listings will not disable fields that have no results starting with a given letter, allowing users to browse to zero-result listing pages (similar to a zero-result search page).

Facet-based searches can be used to intelligently render A-Z listings, disabling (or even suppressing) zero-match letters, in addition to indicating the number of matching results.  A-Z listings can be further refined by sibling facets, if required.


Assuming you've been able to create an index containing a metadata field with a single alphanumeric character from the start of a field (e.g. 'LastNameInitial', 'ServiceNameInitial', etc.).  Generally, the 'Initial' fields will be derivable (most likely through use of the MetadataScraper):

<meta name="lastnameInitial" content="C" />
<meta name="lastname" content="Citizen" />

Map the initial field (we'll use '0' in this example):

# Example metadata field mapped to initial of last name

Create a metadata-driven facet from this field:

<Facets qpoptions=" -rmcf=0">

Configure the ModernUI to run an extra search that returns all facet values:


Create a pre-process hook script to listen for, process, and rewrite '?letter=X' CGI parameters:

def q = transaction.question
    // Hookscripts for A-Z listings.  Assume:
    // - 'lastnameInitial' facet
    // - sourced from metadata field '0')
    if(q.extraSearch == false){ 
        if (q.inputParameterMap["letter"] != null) {
              q.query = "!padrenullqueryAZ"
              q.inputParameterMap["sort"] = "meta0"
              // Avoid pagination - show 5000 results when browsing A-Z
              q.additionalParameters["num_ranks"] = ["5000"] as List
              // Apply facet constraint on selected letter     
              if (q.inputParameterMap["letter"] != "all") {
                q.inputParameterMap["f.lastnameInitial|0"] = q.inputParameterMap["letter"]

Add the A-Z listing macro to your template, specifying which result metadata count field (rmcfield) is used for the initial.  Suggested placement is above the result set summary message, and below pagination:

<@azList rmcField="0"/>
<div id="search-result-count" class="text-muted">
<@azList rmcField="0" />

Prevent the query that generates A-Z listings from being reported in analytics:


Optional extras

Create a dedicated profile for A-Z listings to suppress these queries from ever being logged:


Create an additional check inside the results loop to output letter tiers (useful when browsing by 'All'):

<#assign currentLetter = "0-9" >
    <#if query.inputParameterMap["letter"] = "all">  
      <#if currentLetter != s.result.metaData["0"]>
          <h2 class="letter-tier text-muted">${s.result.metaData["0"]}</h2>
      <#assign currentLetter = s.result.metaData["0"]>

Create a sort drop-down that suppresses 'Relevance' when browsing in A-Z mode (but still allows other sort modes):

<#macro sortDropdown>
<#-- SORT MODES -->
        <div class="dropdown pull-right">
            <a class="dropdown-toggle text-muted" data-toggle="dropdown" href="#" id="dropdown-sortmode" title="Sort">
            <small><span class="glyphicon glyphicon-sort"></span>&nbsp;Sort</small>
            <ul class="dropdown-menu" role="menu" aria-labelledby="dropdown-sortmode">
            <#if !question.inputParameterMap["letter"]??><li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}"><span class="glyphicon glyphicon-sort-by-attributes-alt"></span> Relevance</a></li></#if>
            <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=metaj">Last Name (A-Z)</a></li>
            <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=dmetaj">Last Name (Z-A)</a></li>
             <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=metag">First Name (A-Z)</a></li>
            <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=dmetag">First Name (Z-A)</a></li>
            <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=metaS">Department (A-Z)</a></li>
            <li role="menuitem"><a href="${question.collection.configuration.value("ui.modern.search_link")}?${removeParam(QueryString,["start_rank","sort"])?html}&amp;sort=dmetaS">Department (Z-A)</a></li>

A-Z macro template

Create an A-Z listing (including 'All', and '0-9' options) based on facets
returned from a metadata field count. 
@param rmcField Mandatory result metadata count field
<#macro azList rmcField>
  <#if question.inputParameterMap["letter"]?? >       
    <#assign currentLetter = (question.inputParameterMap["letter"]!"a")?lower_case />
    <#assign alphabet = ["0-9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] />
    <ul class="pagination pagination-sm">
        <li<#if !currentLetter?? || currentLetter = "all"> class="active"</#if>>
            <a href="/s/search.html?collection=<@s.cgi>collection</@s.cgi>&amp;<@s.IfDefCGI name="profile">profile=<@s.cgi>profile</@s.cgi>&amp;</@s.IfDefCGI>letter=all">All</a>
        <#list alphabet as letter>
            <#if letter == currentLetter>
                <li class="active">
                    <a href="/s/search.html?collection=<@s.cgi>collection</@s.cgi>&amp;<@s.IfDefCGI name="profile">profile=<@s.cgi>profile</@s.cgi>&amp;</@s.IfDefCGI>letter=${letter}">${letter?upper_case}</a>
                <#assign rmc = rmcField + ":" + letter?upper_case  />                
                  <#if extraSearches.FACETED_NAVIGATION??
                    && extraSearches.FACETED_NAVIGATION.response.resultPacket.rmcs??
                    && extraSearches.FACETED_NAVIGATION.response.resultPacket.rmcs[rmc]??>

                    <#assign count = extraSearches.FACETED_NAVIGATION.response.resultPacket.rmcs[rmc] />
                            title="Browse by letter: '${letter?upper_case}' (${count} results)"
                            href="/s/search.html?collection=<@s.cgi>collection</@s.cgi>&amp;<@s.IfDefCGI name="profile">profile=<@s.cgi>profile</@s.cgi>&amp;</@s.IfDefCGI>letter=${letter}">
                        <li class="disabled"><a href="#">${letter?upper_case}</a></li>


A-Z listing example.

Was this artcle helpful?

Features: Frontend > Templating Frontend > Freemarker