Integrating with Google Analytics and Optimizely
Background
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
-
Login to Google Analytics, and select view settings from the admin menu in the top navigation.
-
Scroll down to the site search settings area and set site search tracking to on, and enter your query parameter (query).
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
event-tracking_video-click=.vimeo-play
Tracking additional search-related events
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
...
<script>
//Google Analytics & Optimizely Event Tracking
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXXXXX-X', 'auto'); //Change 'UA-*' to your Google Analytics tracking ID
ga('send', 'pageview');
$(document).ready(function(){
//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"]);
});
<@s.AfterSearchOnly>
// Zero-results event
<#if response.resultPacket.resultsSummary.totalMatching == 0>
ga('send', 'event', 'page', 'no results', '${question.query!?html}');
window.optimizely.push(["trackEvent", "NoResultsPage"]);
</#if>
// 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"]);
</#if>
//Sorting event
<#if question.inputParameterMap.sort??>
ga('send', 'event', 'page', 'sort', '${question.inputParameterMap.sort}');
window.optimizely.push(["trackEvent", "SortBy"]);
</#if>
// 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"])
});
</#if>
// 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"])
});
</#if>
</@s.AfterSearchOnly>
});
</script>
...
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:
...
<script>
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;
//Typeahead
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>query_completion.show</@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
});
fbSuggestions.initialize();
$('#query').typeahead({
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>'
].join('\n'),
suggestion: function(data){
displayitem = "";
switch (data.disp_t) {
case 'J':
$.each(data.disp, function(key, value) {
displayitem += (key, value);
displayitem += " "
});
break;
default:
displayitem = data.disp;
break;
}
switch (data.action_t) {
case 'Q':
displayitem = '<a href="' + baseurl + '&query=' + data.action + '">' + displayitem + '</a>';
break;
case 'E':
displayitem = '<a href="' + baseurl + '&query=' + data.key + '&' + data.action + '">' + displayitem + '</a>';
break;
case 'U':
displayitem = '<a href="' + data.action + '">' + displayitem + '</a>';
break;
case 'C':
displayitem = '<a href="#" onClick="' + data.action + '">' + displayitem + '</a>';
break;
default:
displayitem = '<a data-gs="nolink" href="#">' + displayitem + '</a>';
break;
}
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"]);
break;
case 'E':
ga('send', 'event', 'autocomplete', 'category', data.action);
window.optimizely.push(["trackEvent", "autocomplete-category"]);
break;
case 'U':
ga('send', 'event', 'autocomplete', 'URL', data.action);
window.optimizely.push(["trackEvent", "autocomplete-url"]);
break;
case 'C':
ga('send', 'event', 'autocomplete', 'Callback', data.action);
window.optimizely.push(["trackEvent", "autocomplete-callback"]);
break;
default:
ga('send', 'event', 'autocomplete', 'default', data.disp);
window.optimizely.push(["trackEvent", "autocomplete-default"]);
break;
}
});
});
</script>
...