Search
Overview
Prerequisite
Indexing has to be set-up before moving on to Search-related functionalities. For more details, please refer to Indexing - How it Works
Overall Approach
In order to search without restrictions, using the full potential of Hawksearch, the connector provides the Optimizely.Hawksearch.Client package. This package contains classes which will correspond to the requests and responses provided by Hawksearch’s Search API.
- SearchRequest
- SearchResponse
It also provides a client class named SearchClient that will be responsible for calling the mentioned APIs and for deserializing the responses.
For a better experience and easier development, we included in the Optimizely.Hawksearch.Connector package:
- Request builders → Fluent APIs to create a search request
- Search filters → Used for searching with facets
The Flow
- Using only Optimizely.Hawksearch.Client
- Create and populate a SearchRequest object
- Use the Search method of the SearchClient class (or its async counterpart)
public SearchResponse<T> Search<T>(SearchRequest searchRequest) where T : HawksearchBaseItem public async Task<SearchResponse<T>> SearchAsync<T>(SearchRequest searchRequest) where T : HawksearchBaseItem
- Get the SearchResponse object then map it to your business models
-
Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector-
Create and populate a SearchQuery object
-
Build a SearchRequest object by passing the created SearchQuery object to a SearchRequestBuilder and its Fluent API
-
Use one of the Search methods of the SearchClient class shown above
-
Get the SearchResponse object then map it to your business models
A high-level example:var searchQuery = new SearchQuery { Keyword = "jacket", Page = 1, PageSize = 5 }; var results = Search(searchQuery); public ProductSearchResults Search(SearchQuery searchQuery) { var searchRequest = new SearchRequestBuilder() .PublishedOnly() .WithQueryOn(HawksearchConstants.PropertyNames.ItemType, HawksearchConstants.DocumentTypes.Product, FieldSearchType.Keyword) .ForLanguage("en") .InMarket("US") .InCatalog(0) .Build(searchQuery); //SearchClient _client var searchResponse = _client.Search<HawksearchProductItem>(searchRequest); var mappedResponse = MapResults(searchResponse); return mappedResponse; }
-
Autocomplete
Autocomplete works in a similar fashion to the general search functionalities.
Optimizely.Hawksearch.Client package contains classes which will correspond to the requests and responses provided by Hawksearch’s Autocomplete API.
- AutocompleteRequest
- AutocompleteResponse
However, it does not contain Search filters.
The Flow
-
Using only Optimizely.Hawksearch.Client
- Create and populate an AutocompleteRequest object
- Use the Autocomplete method of the SearchClient class (or its async counterpart)
public AutocompleteResponse<TProduct, TContent> Autocomplete<TProduct, TContent>(AutocompleteRequest searchRequest) where TProduct : HawksearchProductItem where TContent : HawksearchContentItem public async Task<AutocompleteResponse<TProduct, TContent>> AutocompleteAsync<TProduct, TContent>(AutocompleteRequest searchRequest) where TProduct : HawksearchProductItem where TContent : HawksearchContentItem
- Get the AutocompleteResponse object then map it to your business models
-
Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector-
Build an AutocompleteRequest object by passing a keyword (string) to an AutocompleteRequestBuilder and its Fluent API
-
Use one of the Autocomplete methods of the SearchClient class show above
-
Get the AutocompleteResponse object then map it to your business models
A high-level example:public AutocompleteResults Autocomplete(string keyword) { var request = new AutocompleteRequestBuilder() .ForLanguage("en") .DisplayFullResponse() .Build(keyword); //SearchClient _client var response = _client.Autocomplete<HawksearchProductItem, HawksearchContentItem>(request); var mappedResponse = MapAutocompleteResults(response); return mappedResponse; }
-
Request Builders
The request builder is a class responsible for constructing a SearchRequest object which will be sent to the Hawksearch Search API. It provides a Fluent API.
Base class: BaseRequestBuilder
Methods
- Instructs the request to be made for this specific language code
public TBuilder ForLanguage(string languageCode)
- Instructs the request to be made for this specific catalog
public TBuilder InCatalog(int catalogId)
- Instructs the request to be made for this specific market
public TBuilder InMarket(string marketId)
- Instructs the request to be made for Optimizely published content only
public TBuilder PublishedOnly()
- Instructs the request to be made for a specific field with a given value and a known field search type.
This field search type is described at Field-Specific Search - V2 and has a code-wise equivalent using theFieldSearchType
enumerationpublic TBuilder WithQueryOn(string fieldName, string value, FieldSearchType searchType = FieldSearchType.None)
- Instructs the request to perform a custom query. The query must use the syntax exemplified in the official Hawksearch documentation: Field-Specific Search - V2
public TBuilder CustomQuery(string customQuery)
Search
Class: SearchRequestBuilder
Methods
- Constructs the final search request based on the provided SearchQuery object.
public SearchRequest Build(SearchQuery searchQuery)
- Adds a search filter convertor to the current builder.
public SearchRequestBuilder AddSearchFilterConvertor(ISearchFilterConvertor convertor)
Example
//var searchQuery = ...;
var searchRequest = new SearchRequestBuilder()
.PublishedOnly()
.WithQueryOn(HawksearchConstants.PropertyNames.ItemType,
HawksearchConstants.DocumentTypes.CmsPage, FieldSearchType.Keyword)
.ForLanguage("en")
.Build(searchQuery);
Autocomplete
Class: AutocompleteRequestBuilder
Methods
- Constructs the final search request based on the provided keyword.
public AutocompleteRequest Build(string keyword)
- Indicates whether a standard response is sent or a full item response needs to be used: Hawksearch v4.0 - Autocomplete API
public AutocompleteRequestBuilder DisplayFullResponse()
Example
var request = new AutocompleteRequestBuilder()
.ForLanguage("en")
.DisplayFullResponse()
.Build("jacket");
Type Search - Products & Content
In order to search for specific item types (products/variants/bundles/packages/pages), we will use the request builder’s WithQueryOn
method and the ItemType
field defined in https://techromix.atlassian.net/wiki/spaces/HC/pages/3718774833Request
Searching for Products example
var searchRequest = new SearchRequestBuilder()
.WithQueryOn(HawksearchConstants.PropertyNames.ItemType, HawksearchConstants.DocumentTypes.Product, FieldSearchType.Keyword)
Searching for Content (CMS Pages) example
var searchRequest = new SearchRequestBuilder()
.WithQueryOn(HawksearchConstants.PropertyNames.ItemType, HawksearchConstants.DocumentTypes.CmsPage, FieldSearchType.Keyword)
Searching for both Content and Products (Tabs)
var searchRequest = new SearchRequestBuilder()
.PublishedOnly()
.ForLanguage("en")
//... no .WithQueryOn(HawksearchConstants.PropertyNames.ItemType, ...)
In order to search for both content and products at the same time, we simply do not use the request builder’s WithQueryOn
method and the ItemType
field.
However, to make a clear distinction between the results from Hawksearch, the tab facet type should be configured in the Hawksearch dashboard: Facet and Field Types
In terms of the response from Hawksearch, tab is no different from a normal facet. You could also use another facet type as long as the associated field is always ItemType
.
Important
SearchClient
class from Optimizely.Hawksearch.Client package can only deserialize the response from Hawksearch into eitherHawksearchContentItem
orHawksearchProductItem
objects. In order to use this “common” search approach, you should either:
- Deserialize into a specific model based on some flag. Example of searching + deserialization based on the tab selected by a user on the frontend:
// searchQuery = ... var results = new SearchResults(); var request = new SearchRequestBuilder() .PublishedOnly() .ForLanguage("en") .Build(searchQuery); if (targetTab == "page") // targetTab retrieved by some user action (like pressing on a tab in the frontend) { var searchResponse = _client.Search<HawksearchContentItem>(request); var mappedResponse = MapContentResults(searchResponse); results.Content = mappedResponse; } else { var searchResponse = _client.Search<HawksearchProductItem>(request); var mappedResponse = MapProductResults(searchResponse); results.Products = mappedResponse; }
- Or create a common model that inherits from
HawksearchBaseItem
and holds common data from bothHawksearchContentItem
andHawksearchProductItem
. Then use this custom type when performing search + deserialization:public class HawksearchCommonItem : HawksearchBaseItem { public override string It { get; } // Properties from HawksearchProductItem public List<string> CatalogId { get; set; } public List<string> Code { get; set; } public List<string> Category { get; set; } //... // Properties from HawksearchContentItem public List<string> SiteId { get; set; } //... } // searchQuery = ... var results = new SearchResults(); var request = new SearchRequestBuilder() .PublishedOnly() .ForLanguage("en") .Build(searchQuery); var searchResponse = _client.Search<HawksearchCommonItem>(request); var mappedResponse = MapCommonResults(searchResponse); results.CommonResults = mappedResponse;
Search Filters/Facets
Search filter is used to retrieve and filter by facets from the Hawksearch Search API.
Possibilities to filter:
- Populate the FacetSelections property of a SearchRequest object directly following the syntax from the official documentation Hawksearch v4.0 - Search API
- Populate the Filters property of a SearchQuery object, then pass it to the
- method of a SearchRequestBuilder
public SearchRequest Build(SearchQuery searchQuery)
The Filters property is a list of SearchFilter objects.
There are 2 out of the box search filters provided by the connector: TermsSearchFilter and RangeSearchFilter, both inheriting from the SearchFilter class.
- TermsSearchFilter → can be used for exact values in a facet (ex: color → Red, size → XS)
- RangeSearchFilter → can be used for a range of values (ex: price → 250$ to 500$).
Example
var query = new SearchQuery();
var filters = new List<SearchFilter>();
filters.Add(new TermsSearchFilter("Color", "Red"));
filters.Add(new TermsSearchFilter("Brand", new List<string> {"Nike", "Adidas"}));
filters.Add(new RangeSearchFilter("Price", 250, 500));
query.Filters = filters;
How to extend
You can create custom filters if the need arise. To do so, create a class that inherits from SearchFilter.
Then, you will need to create a search filter convertor for your new filter. Create a class implementing the ISearchFilterConvertor interface:
string[] Convert(SearchFilter filter);
bool CanConvert(SearchFilter filter);
Convert
method will determine how the new search filter will be converted to the search request.
CanConvert
method determines what are the conditions that need to be met in order for our convertor to take action.
For exemplification, this is how the RangeSearchFilterConvertor class is implemented:
public class RangeSearchFilterConvertor : ISearchFilterConvertor
{
public string[] Convert(SearchFilter filter)
{
var rangeFilter = filter as RangeSearchFilter;
return new[] {$"{rangeFilter.From},{rangeFilter.To}"};
}
public bool CanConvert(SearchFilter filter)
{
return filter is RangeSearchFilter;
}
}
Lastly, to register the new convertor of type ISearchFilterConvertor, you need to call the
public SearchRequestBuilder AddSearchFilterConvertor(ISearchFilterConvertor convertor)
method of the current SearchRequestBuilder and pass in your custom convertor.
Behind the scenes, the Build(…) method of the SearchRequestBuilder will iterate through all possible convertors in order to find one suitable for your custom search filter, then pick it up and convert the search filter to the necessary data that needs to be sent inside the search request to Hawksearch.
Sorting
To sort search results based on different criteria, we first have to configure valid values in Workbench → Data Configuration → Sorting/Pagination as described in Pagination & Sorting | Sorting. After that, we can retrieve the sort options by looking at the Sorting property of the SearchResponse object.
Example (selecting the configured sort options):
public class SelectListItem
{
public string Text { get; set; }
public string Value { get; set; }
public bool Selected { get; set; }
}
//SearchClient _client
var searchResponse = _client.Search<HawksearchProductItem>(searchRequest);
var sortOptions = searchResponse.Sorting.Items.Select(x => new SelectListItem
{
Text = x.Label, Value = x.Value, Selected = x.Selected
}),
We have 2 ways to sort our results (just like we have 2 ways to make a search request):
- Using only Optimizely.Hawksearch.Client
Set the SortBy property directly in the SearchRequest object. - Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector- Create and populate a SearchQuery object
- Set the SortOrder property of the SearchQuery object
- Build a SearchRequest object by passing the created SearchQuery object to a SearchRequestBuilder and its Fluent API
Example
var query = new SearchQuery
{
Keyword = "jacket",
Page = 1,
PageSize = 5,
SortOrder = "Z-A" //Valid values are configured in Workbench > Data Configuration > Sorting/Pagination
};
Pagination
To use pagination options, we first have to configure valid values in Workbench → Data Configuration → Sorting/Pagination as described in Pagination & Sorting | Pagination . After that, we can retrieve the pagination options by looking at the Pagination property of the SearchResponse object.
Example (selecting the configured page sizes and selections from the pagination options):
public class PaginationItem
{
public int PageSize { get; set; }
public bool Selected { get; set; }
}
//SearchClient _client
var searchResponse = _client.Search<HawksearchProductItem>(searchRequest);
var paginationOptions = searchResponse.Pagination.Items.Select(x => new PaginationItem
{
PageSize = x.PageSize, Selected = x.Selected
})
We have 2 ways to use the Hawksearch pagination options (just like we have 2 ways to make a search request):
- Using only Optimizely.Hawksearch.Client
Set the PageNo and MaxPerPage properties directly in the SearchRequest object. - Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector- Create and populate a SearchQuery object
- Set the Page and PageSize properties of the SearchQuery object
- Build a SearchRequest object by passing the created SearchQuery object to a SearchRequestBuilder and its Fluent API
Example
var query = new SearchQuery
{
Keyword = "jacket",
Page = 2, //Valid values are configured in Workbench > Data Configuration > Sorting/Pagination
PageSize = 15 //Valid values are configured in Workbench > Data Configuration > Sorting/Pagination
};
Landing Pages
To create and administer landing pages, please refer to the official documentation Landing Pages | Landing Pages Creating a Content LandingPage
We have 2 ways to retrieve landing pages (just like we have 2 ways to make a search request):
- Using only Optimizely.Hawksearch.Client
Set the CustomUrl property directly in the SearchRequest object and do not set the Keyword property - Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector- Create and populate a SearchQuery object
- Set the CustomUrl property of the SearchQuery object and do not set the Keyword property
Important
If CustomUrl property is provided, the search request builder will always ignore the Keyword property. Thus, do not use CustomUrl for normal searching (searching that requires no landing pages as results)
- Build a SearchRequest object by passing the created SearchQuery object to a SearchRequestBuilder and its Fluent API
Example
var query = new SearchQuery
{
CustomUrl = "/en/my-landing-page/" //Valid values are configured in Workbench > Merchandising > Landing Pages
};
Category Pages
Category pages (or product listing pages) can be easily implemented in Optimizely using Hawksearch Landing Pages.
The main steps are:
- Create a Product Listing Landing Page as described in Landing Pages | LandingPages Creating a Content Landing Page
- In the Select Items to Display section, specify the Category commerce field and its target value:
- In the Facet Configuration section, activate Override Defaulte Facets? flag, then specify the wanted facets you want to be displayed on your category page:
- After these configurations, you can retrieve the category pages and their items the same way a normal Landing Page would be retrieved: https://luminoslabs.atlassian.net/wiki/spaces/HC/pages/3807150094Request access
Sitemap
The Hawksearch engine can generate sitemap files for its Landing Pages. The process is described here: SEO Traffic Builder
We provide the Sitemap Fetch Job in order to bring these generated sitemaps inside the Optimizely platform.
Go to CMS → Admin → Admin tab → Scheduled jobs → [Hawksearch] Sitemap Fetch Job.
After running the job, sitemaps can be found inside CMS → Media tab (right-pane menu) → Hawksearch folder → Sitemap. Each subfolder is language-specific and corresponds to the Hawksearch engine it was configured to point to. Subfolders contain all the XML files downloaded from Hawksearch.

Important
This scheduled job does not generate new sitemaps. It only downloads the already generated sitemaps from all configured Hawksearch engines and displays them in Optimizely CMS.
Recommendations
Prerequisite
Indexing has to be set-up before moving on to Recommendations-related functionalities. For more details, please refer to Indexing - How it Works
Event tracking is also a prerequisite. Prior to that, an order data export should be done and the resulting files should be uploaded in the Hawksearch Dashboard. This way, the recommendations engine can create meaningful recommendations before any tracking data is gathered from the live website.
Overall Approach
In order to fetch recommendations, the connector provides the Optimizely.Hawksearch.Client package. This package contains classes which will correspond to the requests and responses provided by Hawksearch’s Recommendation API
- RecommendationWidgetsRequest → Requesting Product Recommendation Widgets
- RecommendationBulkWidgetsRequest → Requesting Multiple Recommendation Widgets
- RecommendationResponse
It also provides a client class named RecommendationsClient that will be responsible for calling the mentioned APIs and for deserializing the responses.
For a better experience and easier development, we included in the Optimizely.Hawksearch.Connector package:
- Recommendation Widget Block → Optimizely CMS Block with its own client to fasten the development process
The Flow
- Using only Optimizely.Hawksearch.Client
- Create and populate a RecommendationWidgetsRequest or RecommendationBulkWidgetsRequest object (both derive from BaseRecommendationRequest)
- Use the GetWidgetItems or GetBulkWidgetItems method of the RecommendationsClient class (or their async counterparts)
public RecommendationResponse GetWidgetItems(RecommendationWidgetsRequest request) public async Task<RecommendationResponse> GetWidgetItemsAsync(RecommendationWidgetsRequest request) public RecommendationResponse GetBulkWidgetItems(RecommendationBulkWidgetsRequest request) public async Task<RecommendationResponse> GetBulkWidgetItemsAsync(RecommendationBulkWidgetsRequest request)
- Get the RecommendationResponse object then map it to your business models
- Using both
Optimizely.Hawksearch.Client and Optimizely.Hawksearch.Connector
Order Data Export
The Hawksearch engine needs a one-time import of orders from the last 30 days to provide relevant recommendations before enough tracking events are registered in a production website. The process is described here: Importing Order Data
We provide the Order Data Export Job in order to create the 2 necessary files containing information about the orders and required by Hawksearch:
- orders.txt
- order_items.txt
Go to CMS → Admin → Admin tab → Scheduled jobs → [Hawksearch] Order Data Export Job.
After running the job, the files can be found inside CMS → Media tab (right-pane menu) → Hawksearch folder → Orders. The folder contains both text files which can be downloaded from here.

Important
This scheduled job does not upload the generated files automatically to Hawksearch. These files have to be manually uploaded at Hawksearch Dashboard → Workbench → Recommendations → Imports as a single .zip file (the .zip file can have any name).
Recommendation Widget Block
This is an Optimizely block provided in the Optimizely.Hawksearch.Connector package.
Class: RecommendationWidgetBlock
Base class: BlockData
It contains 3 properties:
- Widget Guid → the GUID of the widget from Hawksearch Dashboard
- Render Html → flag that, if set, Widget HTML will be returned from the Hawksearch Dashboard. If not set, recommendation items will be returned in JSON format
- Enable Preview → flag that indicates whether preview has been enabled in the Hawksearch Dashboard for that widget
Recommendation Widget Client
This is a class present in Optimizely.Hawksearch.Connector.Recommendations namespace that uses the Recommendation Widget Block directly to make requests to the Hawksearch Recommendation API.
Class: RecommendationWidgetClient
It provides 2 methods, each one of them using as a parameter the RecommendationWidgetBlock
data type together with other types (required and optional):
public RecommendationResponse GetWidgetItems(RecommendationWidgetBlock block, string visitId,
string visitorId, string languageCode,
string landingPageUrl = null, string uniqueId = null,
Dictionary<string, object> contextProperties = null,
Dictionary<string, object> customProperties = null,
Dictionary<string, object> extendedCustomProperties = null)
public async Task<RecommendationResponse> GetWidgetItemsAsync(RecommendationWidgetBlock block, string visitId,
string visitorId, string languageCode,
string landingPageUrl = null, string uniqueId = null,
Dictionary<string, object> contextProperties = null,
Dictionary<string, object> customProperties = null,
Dictionary<string, object> extendedCustomProperties = null)
Block Usage
Steps to integrate this block in your project:
- Create a Partial View for the block Index.cshtml
- Create a Block Controller for the block
public class RecommendationWidgetBlockController : BlockController<RecommendationWidgetBlock>
- Inject the RecommendationWidgetClient inside your new controller
- Use the GetWidgetItems method of the client to retrieve the target widget (the target widget will match the Widget Guid from the block created in CMS Edit) and its contained recommendations
//RecommendationWidgetClient _client; public override ActionResult Index(RecommendationWidgetBlock currentBlock) { //... var visitId = HawksearchCookieManager.GetOrCreateVisitIdCookie(); var visitorId = HawksearchCookieManager.GetOrCreateVisitorIdCookie(); var languageCode = "en"; var response = _client.GetWidgetItems(currentBlock, visitId, visitorId, languageCode); var widget = response.WidgetItems.FirstOrDefault(); var recommendations = widget?.RecommendationItems; //works if 'Render Html' is not set inside the block var html = widget?.Html; //works if 'Render Html' is set inside the block //... return PartialView(viewModel); }
- Add any other code for your specific business needs inside the controller
After building your project, you can check the functionality by following these steps:
- Create a “Recommendation Widget Block“ from Optimizely CMS → Edit interface
- Fill in the necessary data
- Place the block inside any content area on the website (that has no restrictions for this block type)
- The block should be rendered on the frontend containing your view, style, and Hawksearch recommendations, based on your custom implementation
Event Tracking
The tracking part is mostly done client-side with a minor exception (Login tracking). More on tracking can be found in the official documentation: Event Tracking API
In order to ease the client-side aspect of tracking, we provide a JavaScript file with all the methods mentioned in the Events Tracking API
The contents of the file:
class EventTracking {
constructor(baseUrl, clientGuid, visitorId, visitId) {
this.trackingEndpoint = "/api/trackevent";
this.baseUrl = baseUrl;
this.clientGuid = clientGuid;
this.visitorId = visitorId;
this.visitId = visitId;
}
pageLoad(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("PageLoad", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
pageLoadByType(pageTypeId, customDictionary = null, trackingProperties = null) {
var eventDataModel = new Object();
eventDataModel.pageTypeId = pageTypeId;
eventDataModel.qs = window.location.search;
eventDataModel.requestParh = window.location.pathname;
eventDataModel.ViewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
eventDataModel.ViewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
var request = this.#buildRequest("PageLoad", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
addToCart(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Add2Cart", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
addToCartMultiple(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Add2CartMultiple", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
sale(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Sale", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
rate(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Rate", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
click(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Click", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
search(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Search", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
bannerClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("BannerClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
bannerImpression(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("BannerImpression", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
recommendationClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("RecommendationClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
autocompleteClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("AutocompleteClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
customEvent(eventName, eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest(eventName, eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
#buildRequest(eventType, eventData, customDictionary = null, trackingProperties = null) {
var request = new Object();
request.clientGuid = this.clientGuid;
request.eventType = eventType;
request.eventData = btoa(JSON.stringify(eventData));
request.visitorId = this.visitorId;
request.visitId = this.visitId;
request.customDictionary = customDictionary;
request.trackingProperties = trackingProperties;
return request;
}
#sendRequest(request) {
var xhr = new XMLHttpRequest();
var url = this.baseUrl + this.trackingEndpoint;
xhr.open("POST", url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(request));
}
}
Usage
In order to use the client-side tracking:
- Add the script to your webpage
- Instantiate an EventTracking object with the required parameters
- Build the event data model for the specific tracking action. These event data models are also described in Events Tracking API Integration
- Call the needed method of the EventTracking object by passing the created event data to it
Examples:
- PageLoad event type (script that executes when the page is loaded)
<script src="path/to/tracking.js"></script> <script> var baseUrl = 'https://tracking-dev.hawksearch.net'; // Your environment var clientGuid = 'B33A205A-A58E-4CED-93CF-69C2E2327646'; // Your API Client Guid var visitorId = 'B9A82E3F-5349-459E-AC73-8FCCD395E2C2'; // Visitor Id generated on your end (and perhaps stored in a cookie) var visitId = '6E169ACE-2C47-463B-A5BD-4B9B0B1F8BBD'; // Visit Id generated on your end (and perhaps stored in a cookie) var eventTracking = new EventTracking(baseUrl, clientGuid, visitorId, visitId); eventTracking.pageLoadByType('1'); </script>
- Add2Cart event type (script that executes when the user makes an action → pressing the ‘Add To Cart’ button)
<script src="path/to/tracking.js"></script> <script> var eventTracking = new EventTracking(...); // call a similar function when the user clicks on the 'Add To Cart' button function trackAddToCart(item) { var eventData = new Object(); eventData.uniqueId = item.ItemId; eventData.price = item.AddedItemPrice; eventData.quantity = item.Quantity; eventData.currency = item.Currency; eventTracking.addToCart(eventData); } </script>
Login Tracking
The official Hawksearch documentation Events Tracking API Integration states that a server-to-server approach must be taken in the case of tracking login events (if applicable).
To make this kind of tracking possible, we created the LoginTrackingClient class. This class is a part of the Optimizely.Hawksearch.Client package and provides 2 methods:
public void TrackLogin(LoginTrackingRequest loginTrackingRequest);
public async Task TrackLoginAsync(LoginTrackingRequest loginTrackingRequest);
To implement login tracking, inject a LoginTrackingClient
instance in your class and use its methods in the code sequence responsible for your login operation. Example:
private readonly LoginTrackingClient _loginTrackingClient;
public LonginApiController(LoginTrackingClient loginTrackingClient, ...)
{
__loginTrackingClient = loginTrackingClient;
//...
}
public async Task<ActionResult> Login(...)
{
// Your previous code..
await _loginTrackingClient.TrackLoginAsync(new LoginTrackingRequest
{
UserId = userId, // Unique Identifier of the logged in user that is stored in your database
VisitId = visitId, // Visit Id generated on your end (and perhaps stored in a cookie)
VisitorId = visitorId, // Visitor Id generated on your end (and perhaps stored in a cookie)
Language = new CultureInfo("en") // Targeting the English engine from Hawksearch
});
//...
}
tracking.js
class EventTracking {
constructor(baseUrl, clientGuid, visitorId, visitId) {
this.trackingEndpoint = "/api/trackevent";
this.baseUrl = baseUrl;
this.clientGuid = clientGuid;
this.visitorId = visitorId;
this.visitId = visitId;
}
pageLoad(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("PageLoad", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
pageLoadByType(pageTypeId, customDictionary = null, trackingProperties = null) {
var eventDataModel = new Object();
eventDataModel.pageTypeId = pageTypeId;
eventDataModel.qs = window.location.search;
eventDataModel.requestParh = window.location.pathname;
eventDataModel.ViewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
eventDataModel.ViewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
var request = this.#buildRequest("PageLoad", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
addToCart(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Add2Cart", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
addToCartMultiple(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Add2CartMultiple", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
sale(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Sale", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
rate(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Rate", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
click(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Click", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
search(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("Search", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
bannerClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("BannerClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
bannerImpression(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("BannerImpression", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
recommendationClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("RecommendationClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
autocompleteClick(eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest("AutocompleteClick", eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
customEvent(eventName, eventDataModel, customDictionary = null, trackingProperties = null) {
var request = this.#buildRequest(eventName, eventDataModel, customDictionary, trackingProperties);
this.#sendRequest(request);
}
#buildRequest(eventType, eventData, customDictionary = null, trackingProperties = null) {
var request = new Object();
request.clientGuid = this.clientGuid;
request.eventType = eventType;
request.eventData = btoa(JSON.stringify(eventData));
request.visitorId = this.visitorId;
request.visitId = this.visitId;
request.customDictionary = customDictionary;
request.trackingProperties = trackingProperties;
return request;
}
#sendRequest(request) {
var xhr = new XMLHttpRequest();
var url = this.baseUrl + this.trackingEndpoint;
xhr.open("POST", url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(request));
}
}
Updated about 1 year ago