Integrating with Google Analytics and Optimizely


Funnelback’s default logging and analytics tools cover a large portion of most search-related reporting, however, there are often finer-grained behaviours or UI components that web managers may want to measure in the context of the rest of their site.

This tutorial assumes that you already have a Google Analytics account, we’ll extend the account to track several search-specific events and behaviours that involve Funnelback at some point.

Google Analytics configuration

  1. Login to Google Analytics, and select view settings from the admin menu in the top navigation.

    Google Analytics menu bar
    Google Analytics admin
  2. Scroll down to the site search settings area and set site search tracking to on, and enter your query parameter (query).

    Site search settings

    This would be the bare minimum level of integration with Funnelback and Google Analytics - but a useful starting point.

Tracking search events

Changes to collection.cfg

To ensure the FTL snippet below is as reusable as possible, we can configure the event tracking to listen to particular DOM elements in the Funnelback-rendered HTML via CSS selectors by adding this to collection.cfg:

event-tracking_result-click=ol#search-results li h4 a

When focusing solely on the Funnelback-rendered HTML output, we can add the following script into the footer of simple.ftl - note the additional changes to the GA tracking ID (UA-*) and changes to collection.cfg below

  //Google Analytics & Optimizely Event Tracking
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  ga('create', 'UA-XXXXXXXXX-X', 'auto'); //Change 'UA-*' to your Google Analytics tracking ID
  ga('send', 'pageview');

    //Optimizely setup
    window['optimizely'] = window['optimizely'] || [];

    //Initial form submit tracking
    $('#initial').on('submit', function(e) {
      query = $(this).find('#query').val();
      ga('send', 'event', 'form', 'submit', query);
      window.optimizely.push(["trackEvent", "initialFormSubmit"]);

      // Zero-results event
      <#if response.resultPacket.resultsSummary.totalMatching == 0>
        ga('send', 'event', 'page', 'no results', '${question.query!?html}');
        window.optimizely.push(["trackEvent", "NoResultsPage"]);

      // Partially-matching results event
      <#if response.resultPacket.resultsSummary.fullyMatching == 0 && response.resultPacket.resultsSummary.partiallyMatching != 0>
        ga('send', 'event', 'page', 'partially matching', '${question.query!?html}');
        window.optimizely.push(["trackEvent", "PartiallyMatching"]);

      //Sorting event
      <#if question.inputParameterMap.sort??>
        ga('send', 'event', 'page', 'sort', '${question.inputParameterMap.sort}');
        window.optimizely.push(["trackEvent", "SortBy"]);

      // Result click event
      <#if question.collection.configuration.value('event-tracking_result-click')??>
      $("${question.collection.configuration.value('event-tracking_result-click')}").on('click', function(e) {
        ga('send', 'event', 'result', 'resultclick', $(this)[0].text.trim());
        window.optimizely.push(["trackEvent", "resultClick"])

      // Vimeo play event
      <#if question.collection.configuration.value('event-tracking_video-click')??>
      $("${question.collection.configuration.value('event-tracking_video-click')}").on('click', function() {
        ga('send', 'event', 'result', 'vimeoplay', $(this).attr('title'));
        window.optimizely.push(["trackEvent", "vimeoplay"])


Tracking typeahead events with GA and Optimizely events

Given that users' entry points into search can come from any one of a number of locations, the script below may need to be hard-coded and centrally-managed.

If you’re focusing on testing auto-completion events solely in the context of a Funnelback-rendered search results page, and you’re using the Typeahead library for integrating with Funnelback’s auto-completion web service (/s/suggest.json), the following JS/FTL snippet will assist:

  jQuery(document).ready( function() {
    //Base URL finder
    var profile = '&profile=' + $('form[role="search"] input[name="profile"]').val();
    if (profile == '&profile=undefined') { profile = ''; }
    var form = '&form=' + $('form[role="search"] input[name="form"]').val();
    if (form == '&form=undefined') { form = ''; }
    var baseurl = location.origin + location.pathname + '?collection=' + $('form[role="search"] input[name="collection"]').val() + profile + form;
    var jsonurl = '${SearchPrefix}<@s.cfg>query_completion.program</@s.cfg>'
                //+ '?collection=<@s.cfg>collection</@s.cfg>'
                + '?collection=showcase-typeahead'
                + '&partial_query=%QUERY'
                + '&show=<@s.cfg></@s.cfg>'
                + '&sort=<@s.cfg>query_completion.sort</@s.cfg>'
                + '&alpha=<@s.cfg>query_completion.alpha</@s.cfg>'
                + '&fmt=json++'
                //+ '&profile=${question.profile!}'
                + '&profile=_default_preview';
    var fbSuggestions = new Bloodhound({
      datumTokenizer: Bloodhound.tokenizers.obj.whitespace('disp'),
      queryTokenizer: Bloodhound.tokenizers.whitespace,
      remote: jsonurl
      highlight: true
      name: 'fb-suggestions',
      displayKey: 'key',
      source: fbSuggestions.ttAdapter(),
      templates: {
        empty: [
          '<span class="empty-message">Unable to find any suggestions for your query...</span>'
        suggestion: function(data){
          displayitem = "";
          switch (data.disp_t) {
              case 'J':
                  $.each(data.disp, function(key, value) {
                      displayitem += (key, value);
                      displayitem += " "
                  displayitem = data.disp;
          switch (data.action_t) {
              case 'Q':
                  displayitem = '<a href="' + baseurl + '&query=' + data.action + '">' + displayitem + '</a>';
              case 'E':
                  displayitem = '<a href="' + baseurl + '&query=' + data.key + '&' + data.action + '">' + displayitem + '</a>';
              case 'U':
                  displayitem = '<a href="' + data.action + '">' + displayitem + '</a>';
              case 'C':
                  displayitem = '<a href="#" onClick="' + data.action + '">'  + displayitem + '</a>';
                  displayitem = '<a data-gs="nolink" href="#">'  + displayitem + '</a>';
          return displayitem;
    ).on('typeahead:selected', function($e, data) {
      //Typeahead GA & Optimizely events.
      switch (data.action_t) {
            case 'Q':
              ga('send', 'event', 'autocomplete', 'query', data.disp);
              window.optimizely.push(["trackEvent", "autocomplete-query"]);
            case 'E':
              ga('send', 'event', 'autocomplete', 'category', data.action);
              window.optimizely.push(["trackEvent", "autocomplete-category"]);
            case 'U':
              ga('send', 'event', 'autocomplete', 'URL', data.action);
              window.optimizely.push(["trackEvent", "autocomplete-url"]);
            case 'C':
              ga('send', 'event', 'autocomplete', 'Callback', data.action);
              window.optimizely.push(["trackEvent", "autocomplete-callback"]);
              ga('send', 'event', 'autocomplete', 'default', data.disp);
              window.optimizely.push(["trackEvent", "autocomplete-default"]);

Reviewing events in GA

Inside Google Analytics reporting, select overview from the events section in the left hand navigation. Here you can see all of your events sorted by category.

Google Analytics events reporting