How to Customize Analytics (Event Tracking) for Boost AI Search & Discovery V2

📖 Maximize the power of analytics and optimize your store's performance with Boost AI Search & Discovery V2.

On this page


Why do you need this?

If you're using Boost AI Search & Discovery with app version 2.0 and want to harness the power of advanced analytics, this guide will walk you through the process of customizing event tracking for your theme. By following these steps, you can enjoy enhanced insights and metrics, even if you haven't upgraded to the latest version of our app.


Instruction

âš  This guide only works with Boost AI Search & Discovery V2. To find out which version of our app you are using, please follow this article.

  1. Go to your Shopify Admin Dashboard > Sales channels > Online Store > Themes.
  2. Find the [Target Theme], click the "⋯" button > Edit code

  1. In Assets folder, find boost-pfs-init.js file > simply delete or add a comment prompt (//) to this line of code:

boostPFS.initAnalytics()

You can add comment prompt by adding "//" to the beginning of the line of code.
  1. In the same Assets folder, create a new Javascript file named: boost-pfs-analytics-custom by clicking the folder > select Add a new asset > In Extension, select js > copy and paste this name into Filename: boost-pfs-analytics-custom > click Done.

  1. Copy and paste this code snippet into the newly created boost-pfs-analytics-customs.js file:

/** start: tracking event V2 */

(function () {

// variable

var ANALYTICS_KEY = 'boostPFSAnalytics';

var SESSION_KEY = 'boostPFSSessionId';

var CLICKED_PRODUCT_KEY = 'boostPFSClickedProduct';

var CART_TOKEN = '';

var SESSION = '';

var VIEWED_PRODUCT_DATA = null;

var boostPFSRequestIds = 'boostPFSRequestIds';

var AnalyticsEnum = {

UserAction: {

VIEW_PRODUCT: "view_product",

QUICK_VIEW: "quick_view",

ADD_TO_CART: "add_to_cart",

BUY_NOW: "buy_now"

},

Action: {

FILTER: "filter",

SEARCH: "search",

SUGGEST: "suggest"

}

}


// utils function


/**

* Init analytics on instant search

*/

Analytics.initInstantSearch = function () {

if (Settings.getSettingValue('search.enableSuggestion')) {

if (jQ('.' + Class.searchSuggestionWrapper).length > 0) {

jQ('.' + Class.searchSuggestionWrapper).each((index, suggestionElement) => {

suggestionElement.addEventListener('click', Analytics.onClickProductInSuggestion, true);

document.addEventListener('keydown', Analytics.onClickProductInSuggestion, true);

});

}

}

}


/**

* Init analytics on collection/search page

*/

Analytics.initCollectionSearchPage = function () {

if (Selector.trackingProduct && jQ(Selector.products).length > 0) {

document.addEventListener('click', Analytics.onClickProductInFilterResult, true);

}

}


/**

* Init analytics on product page.

* Find and send a product click data in localStorage to server.

*/

Analytics.initOtherPage = function () {

// Send any analytics that was cancelled before it was sent

var dataList = Analytics.getLocalStorage(ANALYTICS_KEY);

if (!Array.isArray(dataList)) return;

dataList.forEach((data) => {

Analytics.sendProductClickData(data);

if (data.pid == boostPFSAppConfig.general.product_id) {

VIEWED_PRODUCT_DATA = data;

}

})


// If go to product page through our app, bind add to cart & buy now event

if (Utils.isProductPage()) {

if (Selector.trackingAddToCart && jQ(Selector.trackingAddToCart).length > 0) {

document.addEventListener('click', Analytics.onClickAddToCartInProductPage, true);

}

if (Selector.trackingBuyNow && jQ(Selector.trackingBuyNow).length > 0) {

document.addEventListener('click', Analytics.onClickBuyNowInProductPage, true);

}

}


/** Because V2 use Recommendation of V3 so We will listen event clicked to productItem in Recommendation widget

* Remove this product in clicked data for v2 only tracking add_to_cart, buy_now use v3 when click from here

* avoid duplicate event add_to_cart, buy_now

*/

document.addEventListener('click', Analytics.detectClickFromRecommendation, true);

}


Analytics.refreshCartToken = function (dataToRetry) {

// Set up HTTP request

var xhr = new XMLHttpRequest();

xhr.open('GET', '/cart.js');

xhr.onload = function () {

if (xhr.readyState > 3 && xhr.status == 200) {

// On sucesss

var cart = JSON.parse(xhr.responseText);

var cartToken = (cart.item_count <= 0) ? "" : cart.token;

CART_TOKEN = cartToken;

if (dataToRetry) {

dataToRetry.ct = cartToken;

Analytics.sendProductClickData(dataToRetry, true);

}

}

};

xhr.send();

}


/**

* Generates a random unique session ID

* @return {string} random unique ID

*/

Analytics.generateUUID = function () {

return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);

return v.toString(16);

});

}


/**

* Handle analytic on click product list in the collection/search page.

* Save the clicked product data to localStorage.

* @param {Event} event - the click event

*/

Analytics.onClickProductInFilterResult = function (event) {

if (!event || !event.target) return;

var $clickedElement = jQ(event.target);


var action = Utils.isSearchPage() ? AnalyticsEnum.Action.SEARCH : AnalyticsEnum.Action.FILTER;

var userAction = AnalyticsEnum.UserAction.VIEW_PRODUCT;

if (Selector.trackingQuickView && $clickedElement.closest(Selector.trackingQuickView).length > 0) {

userAction = AnalyticsEnum.UserAction.QUICK_VIEW;

}

if (Selector.trackingAddToCart && $clickedElement.closest(Selector.trackingAddToCart).length > 0) {

userAction = AnalyticsEnum.UserAction.ADD_TO_CART;

}

if (Selector.trackingBuyNow && $clickedElement.closest(Selector.trackingBuyNow).length > 0) {

userAction = AnalyticsEnum.UserAction.BUY_NOW;

}


// If the user clicked quickview button,

// and then click add to cart/buy now within the quick view modal,

// but the modal is outside of the product grid item,

// we'll use the last clicked id from the quick view event.

var productId = '';

var $productElement = $clickedElement.closest(Selector.trackingProduct);

// If found product grid item

if ($productElement.length > 0) {

productId = $productElement.attr('data-id');

// If not found product grid item, maybe we're inside a quickview modal.

} else if (VIEWED_PRODUCT_DATA) {

// Add to cart and buy now within modal

if (userAction == AnalyticsEnum.UserAction.ADD_TO_CART || userAction == AnalyticsEnum.UserAction.BUY_NOW) {

productId = VIEWED_PRODUCT_DATA.pid;

}

}

if (!productId) return;


var data = Analytics.buildProductClickData(productId, userAction, action);

Analytics.addProductClickData(data);

Analytics.sendProductClickData(data);


if (userAction == AnalyticsEnum.UserAction.QUICK_VIEW) {

VIEWED_PRODUCT_DATA = data;

} else {

VIEWED_PRODUCT_DATA = null;

}

}



/**

* Handle analytic on click product in search suggestion.

* Save the clicked product data to localStorage.

* @param {Event} event - the click event

*/

Analytics.onClickProductInSuggestion = function (event) {

if (!event || !event.target) return;


// Check for keyboard enter event

if (event.type == 'keydown' && event.keyCode != 13) return;


var $clickedElement = jQ(event.target);

var $productElement = $clickedElement.closest('.' + Class.searchSuggestionItem + '-product');

if (!$productElement) return;


var productId = $productElement.attr('data-id');

if (!productId) return;


var data = Analytics.buildProductClickData(productId, AnalyticsEnum.UserAction.VIEW_PRODUCT, AnalyticsEnum.Action.SUGGEST);

Analytics.addProductClickData(data);

}


Analytics.onClickAddToCartInProductPage = function (event) {

if (event && event.target && jQ(event.target).closest(Selector.trackingAddToCart).length > 0) {

var data = {

tid: Globals.shopDomain,

pid: boostPFSAppConfig.general.product_id.toString(),

u: AnalyticsEnum.UserAction.ADD_TO_CART,

ct: CART_TOKEN

};


var productClickedData = Analytics.getLocalStorage(CLICKED_PRODUCT_KEY);


if (productClickedData[boostPFSAppConfig.general.product_id]) {

data = productClickedData[boostPFSAppConfig.general.product_id];

data.u = AnalyticsEnum.UserAction.ADD_TO_CART;

Analytics.addProductClickData(data);

Analytics.sendProductClickData(data);

}

}

}


Analytics.onClickBuyNowInProductPage = function (event) {

if (event && event.target && jQ(event.target).closest(Selector.trackingBuyNow).length > 0) {

var data = {

tid: Globals.shopDomain,

pid: boostPFSAppConfig.general.product_id.toString(),

u: AnalyticsEnum.UserAction.BUY_NOW,

ct: CART_TOKEN

};


var productClickedData = Analytics.getLocalStorage(CLICKED_PRODUCT_KEY);


if (productClickedData[boostPFSAppConfig.general.product_id]) {

data = productClickedData[boostPFSAppConfig.general.product_id];

data.u = AnalyticsEnum.UserAction.BUY_NOW;

Analytics.addProductClickData(data);

Analytics.sendProductClickData(data);

}

}

}


/**

* Build product click data in collection/search page and in instant search.

* @param {Number} productId

* @param {AnalyticsEnum.UserAction} userAction - UserAction enum

* @param {AnalyticsEnum.Action} action - Action enum

* @return {Object} data - the click data to be add to localStorage/send to server.

*/

Analytics.buildProductClickData = (productId, userAction, action) => {

var currentTime = new Date();


// Get cart token from global

var cartToken = CART_TOKEN;


// Merge quick_view and view_product when sending to backend

var mergeUserAction = userAction == AnalyticsEnum.UserAction.QUICK_VIEW ? AnalyticsEnum.UserAction.VIEW_PRODUCT : userAction;


// Get query string data

var queryString = '';

if (action == AnalyticsEnum.Action.FILTER) {

queryString += 'collection_scope=' + Globals.collectionId;

} else {

queryString += 'q=' + Globals.currentTerm;

}

if (action == AnalyticsEnum.Action.FILTER || action == AnalyticsEnum.Action.SEARCH) {

var filteredKeys = Object.keys(Globals.queryParams).filter(key => key.startsWith(Globals.prefix));

if (filteredKeys && filteredKeys.length > 0) {

filteredKeys.forEach(key => {

var values = Globals.queryParams[key];

if (Array.isArray(values)) {

values.forEach(value => {

queryString += '&' + key + '=' + encodeURIComponent(value);

})

} else {

queryString += '&' + key + '=' + encodeURIComponent(values);

}

})

}

}


var requestIds = Analytics.getLocalStorage(boostPFSRequestIds);


// Build data

var data = {

tid: Globals.shopDomain,

ct: cartToken,

pid: productId,

t: currentTime.toISOString(),

u: mergeUserAction,

a: action,

qs: queryString,

r: document.referrer,

rid: requestIds[action]?.rid

}


// save productClickedData key

var preValue = Analytics.getLocalStorage(CLICKED_PRODUCT_KEY) || {};


Analytics.setLocalStorage(CLICKED_PRODUCT_KEY, {

[productId]: {

...data

},

...preValue,

});


return data;

}


// catch event when click productItem in Recommendation

// TODO

Analytics.detectClickFromRecommendation = function(event) {

if (!event || !event.target) return;


var $clickedElement = jQ(event.target);

var productItemV3 = $clickedElement.closest('.boost-sd__product-item');

if (productItemV3.length > 0) {

var productId = productItemV3[0].id;

var variantId = productItemV3[0]['data-product-id'];


productId && Analytics.removeProductIdClickedProductItemInRecommendation(productId);

variantId && Analytics.removeProductIdClickedProductItemInRecommendation(variantId);

}

}


// Remove productId from Object CLICKED_PRODUCT_KEY avoid duplicate event when v2 use recommendation

Analytics.removeProductIdClickedProductItemInRecommendation = function(productId){

var preValue = Analytics.getLocalStorage(CLICKED_PRODUCT_KEY) || {};


delete preValue[productId];


Analytics.setLocalStorage(CLICKED_PRODUCT_KEY, {

...preValue,

});

}


/**

* Add product click data in local storage.

* @param {Object} data - product click data

*/

Analytics.addProductClickData = function (data) {

// Get data list from local storage

var dataList = Analytics.getLocalStorage(ANALYTICS_KEY)

if (!Array.isArray(dataList)) dataList = [];


// Add new data to the list, without duplicated id

var newDataList = dataList.filter(x => x.pid != data.productId);

newDataList.push(data);


Analytics.setLocalStorage(ANALYTICS_KEY, newDataList);

}


Analytics.removeProductClickData = function (productId) {

// Get data list from local storage

var dataList = Analytics.getLocalStorage(ANALYTICS_KEY);

if (!Array.isArray(dataList)) return;


// Filter for products that doesn't match the id

var newDataList = dataList.filter(x => x.pid != productId);

Analytics.setLocalStorage(ANALYTICS_KEY, newDataList);

}


Analytics.getLocalStorage = function (key) {

try {

return JSON.parse(localStorage.getItem(key));

} catch {

return null;

}

}


Analytics.setLocalStorage = function (key, value) {

try {

if (value != null) {

localStorage.setItem(key, JSON.stringify(value));

} else {

localStorage.setItem(key, "");

}


} catch { }

}


/**

* Send product click data to server.

* @param {Object} data - product click data

* @param {boolean} triedToGetToken - tried to get cart token by calling cart.js or not

*/

Analytics.sendProductClickData = function (data, triedToGetToken) {


if (!triedToGetToken && !data.ct) {

setTimeout(function () {

Analytics.refreshCartToken(data);

}, 1000);

return;

}


data.sid = SESSION;


// Set up HTTP request

var xhr = new XMLHttpRequest();

xhr.open('POST', Api.getApiUrl('analytics'));

xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

xhr.onload = function () {

// On sucess

if (xhr.readyState > 3 && xhr.status == 200) {

Analytics.removeProductClickData(data.pid);

}

};

xhr.send(JSON.stringify(data));

}


Analytics.getSessionId = function () {

if (SESSION) {

return SESSION;

} else {

SESSION = Analytics.getLocalStorage(SESSION_KEY);

}

if (!SESSION) {

SESSION = Analytics.generateUUID();

Analytics.setLocalStorage(SESSION_KEY, SESSION);

}

return SESSION;

}


// customize function

if ((Utils.isSearchPage() || Utils.isSearchPage()) && !Utils.isProductPage()) {

FilterApi.afterCall = function (result, eventType, eventInfo) {

var key = AnalyticsEnum.Action.FILTER;

if (Utils.isSearchPage()) {

key = AnalyticsEnum.Action.SEARCH;

}


var preValue = Analytics.getLocalStorage(boostPFSRequestIds) || {};


Analytics.setLocalStorage(boostPFSRequestIds, {

...preValue,

[key]: {

rid: result?.meta?.rid,

data: result

}

})

}

}


InstantSearchApi.afterCall = function (result) {

var preValue = Analytics.getLocalStorage(boostPFSRequestIds) || {};


Analytics.setLocalStorage(boostPFSRequestIds, {

...preValue,

[AnalyticsEnum.Action.SUGGEST]: {

rid: result?.meta?.rid,

data: result

}

})

}


Analytics.init = function () {

if (!window.XMLHttpRequest) return;


CART_TOKEN = "";

SESSION = Analytics.getLocalStorage(SESSION_KEY);

if (!SESSION) {

SESSION = Analytics.generateUUID();

Analytics.setLocalStorage(SESSION_KEY, SESSION);

}


Analytics.initInstantSearch();

Analytics.initCollectionSearchPage();

Analytics.initOtherPage();

}


window.Analytics = Analytics;

})();

/** end: tracking event V2 */


Analytics.init();

  1. Moving on to Snippets folder, locate this file: boost-pfs.liquid

  1. In the boost-pfs.liquid file, copy and paste this line of code into the end of the file:

<script defer src="{{ 'boost-pfs-analytics-custom.js' | asset_url }}"></script>

  1. Click Save to make sure you won't lose all the changes you have made and we are set!

By following these steps, you'll be able to customize event tracking for Boost AI Search & Discovery V2. Gain valuable insights into shopper interactions and behavior, even without upgrading to the latest app version. For any assistance or inquiries, our support team is here to help.