For Developers
Installing the connector
Goal
This article provides information on how to install the connector to a Kentico project.
Prerequisite
- The backend website must be on Kentico 13 for NuGet version 13.x.x or at least Kentico 12.0.29 for the version 12.x.x NuGet.
- The frontend website needs to be an ASP .NET Core MVC for the version 13.x.x NuGet or an ASP .NET Web Application for the version 12.x.x NuGet.
- Corresponding version of Kentico connector NuGet package:
- Install from the following NuGet repository - https://developer.hawksearch.com/nuget
- Install the NuGet from local repository - Place the NuGet package in a local folder on your workstation and set it as Package source in Visual Studio
- Kentico 13 - NuGet version 13.x.x
- Kentico 12 - NuGet version 12.x.x
Steps to install the connector
Live frontend project
Kentico 13
- Install the Hawksearch.Kentico.Xperience.UI NuGet package.
- In the startup ConfigureServices method, add the “AddHawksearchConnector()” extension method on the result of “AddControllersWithViews()”.
- In the same method, add a call to “AddHawksearchViews()” to the IServiceCollection.
- In the Configure method, add a call to “UseHawksearchConnector()“ on the IApplicationBuilder.
- In your layout file, there are a few things that need to be added.
<!DOCTYPE html>
@{
var settings = CMS.Core.Service.Resolve<Hawksearch.Kentico.Xperience.CMS.Configuration.IConnectorSettingsProvider>().GetSettings();
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<page-builder-styles />
@if (settings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.React)
{
<link rel="stylesheet" href="/assets/hawksearch/react/dist/main.css" />
}
</head>
<body>
<div>
@RenderBody()
</div>
<page-builder-scripts />
@if (settings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue)
{
<script src="/assets/hawksearch/vue/dist/main.js"></script>
}
else
{
<script src="/assets/hawksearch/react/dist/main.js"></script>
}
</body>
</html>
- Rebuild your solution.
The namespace for all the extension methods is Hawksearch.Kentico.Xperience.Configuration.
ConfigureServices method:
Configure method:services.AddControllersWithViews().AddHawksearchConnector();
app.UseHawksearchConnector();
Kentico 12
-
Install the Hawksearch.Kentico.Xperience.UI NuGet package.
-
In your main web.config file, add the following lines:
<system.webServer> <handlers> //Additional web.config code removed for clarity <remove name="StaticFile"/> //Additional web.config calls removed for clarity <add name="StaticFile" path="*" verb="*" preCondition="integratedMode" type="System.Web.StaticFileHandler"/> </handlers> //Additional web.config calls removed for clarity </system.webServer>
-
In your layout file, there are a few things that need to be added.
@using Kentico.PageBuilder.Web.Mvc @{ var settings = CMS.Core.Service.Resolve<Hawksearch.Kentico.Xperience.CMS.Configuration.IConnectorSettingsProvider>().GetSettings(); } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> @Styles.Render("~/Content/css") @Html.Kentico().PageBuilderStyles() @if (settings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.React) { <link rel="stylesheet" href="/assets/hawksearch/react/dist/main.css" /> } </head> <body> <div class="container body-content"> @RenderBody() </div> @Scripts.Render("~/bundles/scripts") @Html.Kentico().PageBuilderScripts() @if (settings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue) { <script src="/assets/hawksearch/vue/dist/main.js"></script> } else { <script src="/assets/hawksearch/react/dist/main.js"></script> } @RenderSection("scripts", required: false) </body> </html>
Backend CMSApp project
- Install the Hawksearch.Kentico.Xperience.CMS NuGet package.
- Rebuild your solution.
Warning
Run the CMSApp project and make sure at least one request passes through. Kentico will automatically install all required objects in the database.
Next steps
Kentico: System Settings Setup
Extending the connector
Presentation Layer
Getting Started
Goal
This article provides information on managing the front-end libraries in the connector.
Overview
The Kentico connector is distributed with several built-in libraries for managing the presentation of Hawksearch. Currently, the connector supports ReactJS and Vue.js powered templates which enable the front-end layer to have structured data integration and modern behavior, look and feel.
Steps for switching between the libraries
- From the application selector side menu (F2 or button top left) choose the Settings application.
- Go to Integration / Hawksearch where you’ll find the settings for the connector.
- Go to the Search settings category.
- Change the Frontend framework to whichever you want to use.
Vue.js
Customize Search results in Vue.js
Goal
The purpose of this article is to show you how to extend the existing search results widget.
Overview
All components in the Vue.js framework are quite extendable and can be modified to handle custom layout, styles and behaviour. This is possible due to the built-in capability of the platform to attach templates as script tag and therefore supplying a dynamic structure of the presentation.
Prerequisite
Configured Hawksearch widgets - Kentico: Working with widgets
Extending the templates
- In order to override the default view file the Hawksearch search results widget uses you need to create a razor view file in your frontend project at Views/Shared/Components/SearchResults/Default.cshtml.
- In order to add additional templates, create corresponding view files under Views/Shared/Components/SearchResults/ . When displayed in the widget settings, they will be split by capital letters and words after the first will be lowercased. So a view file called “SearchResultsTemplateOne“ will be displayed like “Search results template one” in the widget settings.
The way the connector is designed it will automatically find the razor view files in that folder and use them. Below is the Default.cshtml code used for each version of the NuGet.
Kentico 12 NuGet
@model Hawksearch.Kentico.Xperience.UI.Widgets.SearchResultsViewModel
@{
var trackedEvents = new List<string>()
{
Model.Properties.TrackBannerClickEvent ? "BannerClick" : "",
Model.Properties.TrackBannerImpressionEvent ? "BannerImpression" : "",
Model.Properties.TrackClickEvent ? "Click" : "",
Model.Properties.TrackPageLoadEvent ? "PageLoad" : "",
Model.Properties.TrackRecommendationClickEvent ? "RecommendationClick" : ""
};
var eventsJson = Newtonsoft.Json.JsonConvert.SerializeObject(trackedEvents.Where(a => !string.IsNullOrEmpty(a)));
var settingsJson = Newtonsoft.Json.JsonConvert.SerializeObject(Model.ConnectorSettings);
var language = Model.ConnectorSettings.MultilingualIndexingEnabled ?
CMS.Localization.LocalizationContext.CurrentCulture.CultureCode : "";
var languageIndifferentFields = Newtonsoft.Json.JsonConvert.SerializeObject(Model.FieldNames);
}
@if (Model.ConnectorSettings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue)
{
<div class="vue-app-wrapper-ae hawk"
data-indexname="@Model.Properties.IndexName"
data-language="@language"
data-languageindifferentfields="@languageIndifferentFields"
data-additionalparameters="@Model.Properties.Data"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson"
data-initialsearch="true">
<div class="hawk__body">
<facet-list></facet-list>
<results></results>
</div>
</div>
<script id="custom-result-item" type="x-template">
<div class="result-item">
<p class="result-item-title">{{ getField('title') }}</p>
<p class="result-item-description">{{ getField('description') }}</p>
</div>
</script>
}
else
{
<div id="hawksearch-react-search-results"
data-indexname="@Model.Properties.IndexName"
data-language="@language"
data-languageindifferentfields="@languageIndifferentFields"
data-additionalparameters="@Model.Properties.Data"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson">
</div>
}
Kentico 13 NuGet
@model Hawksearch.Kentico.Xperience.UI.Widgets.SearchResultsViewModel
@{
var trackedEvents = new List<string>()
{
Model.ComponentViewModel.Properties.TrackBannerClickEvent ? "BannerClick" : "",
Model.ComponentViewModel.Properties.TrackBannerImpressionEvent ? "BannerImpression" : "",
Model.ComponentViewModel.Properties.TrackClickEvent ? "Click" : "",
Model.ComponentViewModel.Properties.TrackPageLoadEvent ? "PageLoad" : "",
Model.ComponentViewModel.Properties.TrackRecommendationClickEvent ? "RecommendationClick" : ""
};
var eventsJson = Newtonsoft.Json.JsonConvert.SerializeObject(trackedEvents.Where(a => !string.IsNullOrEmpty(a)));
var settingsJson = Newtonsoft.Json.JsonConvert.SerializeObject(Model.ConnectorSettings);
var language = Model.ConnectorSettings.MultilingualIndexingEnabled ?
CMS.Localization.LocalizationContext.CurrentCulture.CultureCode : "";
var languageIndifferentFields = Newtonsoft.Json.JsonConvert.SerializeObject(Model.FieldNames);
}
@if (Model.ConnectorSettings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue)
{
<div class="vue-app-wrapper-ae hawk"
data-indexname="@Model.ComponentViewModel.Properties.IndexName"
data-language="@language"
data-languageindifferentfields="@languageIndifferentFields"
data-additionalparameters="@Model.ComponentViewModel.Properties.Data"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson"
data-initialsearch="true">
<div class="hawk__body">
<facet-list></facet-list>
<results></results>
</div>
</div>
<script id="custom-result-item" type="x-template">
<div class="result-item">
<p class="result-item-title">{{ getField('title') }}</p>
<p class="result-item-description">{{ getField('description') }}</p>
</div>
</script>
}
else
{
<div id="hawksearch-react-search-results"
data-indexname="@Model.ComponentViewModel.Properties.IndexName"
data-language="@language"
data-languageindifferentfields="@languageIndifferentFields"
data-additionalparameters="@Model.ComponentViewModel.Properties.Data"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson">
</div>
}
If you want to modify the actual result item template (the one used to render each result item), you need to modify the html code inside the element. It is important to note that the overall widget needs to be wrapped in a div element with the class “vue-app-wrapper-ae“ and to have the data attributes present as in the snippet above, otherwise functionality might not work as expected. Currently, the connector locates those divs in a page when initializing the Vue.js components.
Note
You can use the “getField” call as on row 43 of the snippet above to get fields values that were returned for each document from Hawksearch.
If you’d like to use some additional components, please refer to the following pages: Creating widgets with Vue SDK
Example: Overriding the result item component with two column layout using bootstrap classes
Steps to override
- Navigate to Views/Shared/Components/SearchResults/ (create this path if necessary) in your frontend project.
- Create a cshtml file with your desired name at that location, for example CustomSearchResults.cshtml.
- Copy the Default.cshtml code found at Kentico: Customize Search results in Vue.js .
- Edit the script with the id “custom-result-item” to the following;
<script id="custom-result-item" type="x-template">
<div class="row">
<div class="col-md-6">
<div><h3>{{ getField('title') }}</h3></div>
<div>{{ getField('info') }}</div>
</div>
<div class="col-md-6">
<div>{{ getField('content') }}</div>
</div>
</div>
</script>
Apply the desired structure in this format. All indexed fields are accessible through getField().
- Save & Rebuild.
- Launch the backend CMSApp project and go to the page with the widget you want this override to be for.
- Open the widget settings and choose the template from the dropdown menu. If you have named your cshtml file CustomSearchResults, from the dropdown menu you will find Custom search results.
- Save the widget and see the applied template in the live website.
Example: Overriding the result item component to include images and links
Steps to override
-
Navigate to Views/Shared/Components/SearchResults/ (create this path if necessary) in your frontend project.
-
Create a cshtml file with your desired name at that location, for example CustomSearchResults.cshtml.
-
Copy the Default.cshtml code found at Kentico: Customize Search results in Vue.js .
-
Edit the script with the id “custom-result-item” to the following;
<script id="custom-result-item" type="x-template"> <div> <a :href="getField('url')"> <img :src="getField('image')"> </a> <p>{{ getField('description') }}</p> </div> </script>
Apply the desired structure in this format. All indexed fields are accessible through getField(). Note the syntax of binding the attributes (no {{ }} and: placed before the attribute).
-
Save & Rebuild.
-
Launch the backend CMSApp project and go to the page with the widget you want this override to be for.
-
Open the widget settings and choose the template from the dropdown menu. If you have named your cshtml file CustomSearchResults, from the dropdown menu you will find Custom search results.
-
Save the widget and see the applied template in the live website.
Example: Extending the result items with different layout for products and content
Steps to override
-
Navigate to Views/Shared/Components/SearchResults/ (create this path if necessary) in your frontend project.
-
Create a cshtml file with your desired name at that location, for example CustomSearchResults.cshtml.
-
Copy the Default.cshtml code found at Kentico: Customize Search results in Vue.js .
-
Edit the script with the id “custom-result-item” to the following;
<script id="custom-result-item" type="x-template"> <div :class="getField('type') == 'Content' ? 'hawk-results__contentItem' : 'hawk-results__item'" v-on:click="onClick"> <template v-if="getField('type') == 'Content'"> <h4 class="hawk-results__hawk-contentTitle"> <a :href="absoluteUrl(getField(linkField))">{{ getField('itemname') }}</a> </h4> <template v-if="getField('description_short')"> <div>{{ getField('description_short') }}</div> </template> </template> <template v-else> <result-image :imagePath="getField('image')"></result-image> <div class="hawk-results__item-name"> <span>{{ getField('itemname') }}</span> </div> </template> </div> </script>
Apply the desired structure in this format. All indexed fields are accessible through getField(). Vue.js directives are applied in the template override ( Vue.js Directives ). Note that the layout of the item is based on two different types: Products or Content. Since data held in them could differ a lot, they have a specifically dedicated layout.Save & Rebuild.
-
Launch the backend CMSApp project and go to the page with the widget you want this override to be for.
-
Open the widget settings and choose the template from the dropdown menu. If you have named your cshtml file CustomSearchResults, from the dropdown menu you will find Custom search results.
-
Save the widget and see the applied template in the live website.
Example: Extending result item component to include a field from the response object
Steps to override
- Navigate to Views/Shared/Components/SearchResults/ (create this path if necessary) in your frontend project.
- Create a cshtml file with your desired name at that location, for example CustomSearchResults.cshtml.
- Copy the Default.cshtml code found at Kentico: Customize Search results in Vue.js .
- Edit the script with the id “custom-result-item” to the following;
Note, that there is an input with a specific method invoked. Using getResponseField(), the value of the TrackingId field is passed to an element placed inside the component. The method takes the field name as a parameter. In this case, it is on the top level of the response object:
<script id="custom-result-item" type="x-template"> <div> <div><h3>{{ getField('title') }}</h3></div> <div>{{ getField('content') }}</div> <input type="hidden" :value="getResponseField('TrackingId')"> </div> </script>
Deeper properties can also be accessed:{ "Facets": [ ... ], "VisitorTargets": [ { "Id": 9356, "Name": "International" }, { "Id": 10187, "Name": "awe" } ], "TrackingId": "87ac0720-a61c-4ba7-93d6-77835c60e67c", "Success": true, "Pagination": { ... }, "Keyword": "", "Results": [ ... ], "Selections": {}, "Sorting": { ... }, "Redirect": {}, "Merchandising": { ... }, "FeaturedItems": { ... }, "SearchDuration": 34 }
<input type="hidden" :value="getResponseField('VisitorTargets.0.Name')">
- Save & Rebuild.
- Launch the backend CMSApp project and go to the page with the widget you want this override to be for.
- Open the widget settings and choose the template from the dropdown menu. If you have named your cshtml file CustomSearchResults, from the dropdown menu you will find Custom search results.
- Save the widget and see the applied template in the live website.
Example: Extending search results view to show additional fields based on the type of the result item
Steps to override
- Navigate to Views/Shared/Components/SearchResults/ (create this path if necessary) in your frontend project.
- Create a cshtml file with your desired name at that location, for example CustomSearchResults.cshtml.
- Copy the Default.cshtml code found at Kentico: Customize Search results in Vue.js .
- Edit the script with the id “custom-result-item” to the following;
Apply the desired structure in this format. All indexed fields are accessible through getField().
<script id="custom-result-item" type="x-template"> <div> <template v-if="getField('contenttype') == 'custom.PageTypeOne'"> <div><h3>{{ getField('title') }}</h3></div> <div><img :src="getField('image')"></div> <div>{{ getField('content') }}</div> </template> <template v-else-if="getField('contenttype') == 'custom.PageTypeTwo'"> <div><h3>{{ getField('title') }}</h3></div> <div>{{ getField('summary') }}</div> <p><a :href="getField('link')">Read more</a></p> </template> <template v-else> <div>{{ getField('content') }}</div> </template> </div> </script>
- Save & Rebuild.
- Launch the backend CMSApp project and go to the page with the widget you want this override to be for.
- Open the widget settings and choose the template from the dropdown menu. If you have named your cshtml file CustomSearchResults, from the dropdown menu you will find Custom search results.
- Save the widget and see the applied template in the live website.
Customize Search box in Vue.js
Goal
The purpose of this article is to show you how to extend the existing search box widget.
Overview
All components in the Vue.js framework are quite extendable and can be modified to handle custom layout, styles and behaviour. This is possible due to the built-in capability of the platform to attach templates as script tag and therefore supplying a dynamic structure of the presentation.
Prerequisite
Configured Hawksearch widgets - Kentico: Working with widgets
Extending the templates
- In order to override the default view file the Hawksearch search box widget uses you need to create a razor view file in your frontend project at Views/Shared/Components/SearchBox/Default.cshtml.
- In order to add additional templates, create corresponding view files under Views/Shared/Components/SearchBox/ .
When displayed in the widget settings, they will be split by capital letters and words after the first will be lowercased. So a view file called “SearchBoxTemplateOne“ will be displayed like “Search box template one” in the widget settings.
The way the connector is designed it will automatically find the razor view files in that folder and use them. Below is the Default.cshtml code used for each NuGet version.
Kentico 12 NuGet
@model Hawksearch.Kentico.Xperience.UI.Widgets.SearchBoxViewModel
@{
var trackedEvents = new List<string>()
{
Model.Properties.TrackAutocompleteClickEvent ? "AutocompleteClick" : "",
Model.Properties.TrackSearchEvent ? "Search" : ""
};
var eventsJson = Newtonsoft.Json.JsonConvert.SerializeObject(trackedEvents.Where(a => !string.IsNullOrEmpty(a)));
var settingsJson = Newtonsoft.Json.JsonConvert.SerializeObject(Model.ConnectorSettings);
var language = Model.ConnectorSettings.MultilingualIndexingEnabled ?
CMS.Localization.LocalizationContext.CurrentCulture.CultureCode : "";
}
@if (Model.ConnectorSettings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue)
{
<div class="vue-app-wrapper-ae"
data-indexname="@Model.Properties.IndexName"
data-language="@language"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson">
<div class="hawk">
<div class="hawk__header">
@if (string.IsNullOrWhiteSpace(Model.SearchPageUrl))
{
<search-box></search-box>
}
else
{
<search-box search-page="@Model.SearchPageUrl"></search-box>
}
</div>
</div>
</div>
}
else
{
<div id="hawksearch-react-search-box"
data-indexname="@Model.Properties.IndexName"
data-language="@language"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson"
data-searchpageurl="@Model.SearchPageUrl">
</div>
}
Kentico 13 NuGet
@model Hawksearch.Kentico.Xperience.UI.Widgets.SearchBoxViewModel
@{
var trackedEvents = new List<string>()
{
Model.ComponentViewModel.Properties.TrackAutocompleteClickEvent ? "AutocompleteClick" : "",
Model.ComponentViewModel.Properties.TrackSearchEvent ? "Search" : ""
};
var eventsJson = Newtonsoft.Json.JsonConvert.SerializeObject(trackedEvents.Where(a => !string.IsNullOrEmpty(a)));
var settingsJson = Newtonsoft.Json.JsonConvert.SerializeObject(Model.ConnectorSettings);
var language = Model.ConnectorSettings.MultilingualIndexingEnabled ?
CMS.Localization.LocalizationContext.CurrentCulture.CultureCode : "";
}
@if (Model.ConnectorSettings.FrontendFramework == Hawksearch.Kentico.Xperience.CMS.Configuration.FrontendFrameworkType.Vue)
{
<div class="vue-app-wrapper-ae"
data-indexname="@Model.ComponentViewModel.Properties.IndexName"
data-language="@language"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson">
<div class="hawk">
<div class="hawk__header">
@if (string.IsNullOrWhiteSpace(Model.SearchPageUrl))
{
<search-box></search-box>
}
else
{
<search-box search-page="@Model.SearchPageUrl"></search-box>
}
</div>
</div>
</div>
}
else
{
<div id="hawksearch-react-search-box"
data-indexname="@Model.ComponentViewModel.Properties.IndexName"
data-language="@language"
data-trackedevents="@eventsJson"
data-hawksearchsettings="@settingsJson"
data-searchpageurl="@Model.SearchPageUrl">
</div>
}
Feel free to modify the given code to your liking, but there are a few things to be careful of. It is important to note that the overall widget needs to be wrapped in a div element with the class “vue-app-wrapper-ae“ and to have the data attributes present as in the snippet above, otherwise functionality might not work as expected. Currently, the connector locates those divs in a page when initializing the Vue.js components.
Note
If you’d like to use some additional components, please refer to the following pages:
Understanding the used Vue SDK - Creating widgets with Vue SDK
React
Overview
The React integration of Hawksearch results can be customized with additional functionalities and extended layouts. For this, it is required to install the react-hawksearch NPM package separately and include the extended build resources.
Goal
This document provides information regarding extending the Hawksearch results widget and its layout and styles.
Prerequisite
- Getting started - Kentico: Getting started
- React SDK Setup - React SDK setup
Extending the widget template
-
Follow the React SDK setup steps to set up the npm package in your target project. This should result in a dedicated folder with the specified structure (e.g. hawksearch/react).
-
Make a new .jsx file that will contain our custom search results markup and name it for example custom-search-results.jsx. Place it in a dedicated folder, e.g. hawksearch/react/widgets. Below is the default code used. Modify the react component markup to your specifications.
import React from 'react'; import ReactDom from 'react-dom'; import { Hawksearch, QueryStringListener, FacetRail, Results } from 'react-hawksearch'; const InitializeSearchResults = (props) => { const config = props.config; return ( <Hawksearch config={config}> <QueryStringListener /> <div className="hawk"> <div className="hawk__body"> <FacetRail /> <Results /> </div> </div> </Hawksearch> ); } function RenderSearchResults(element, config) { if (!element || !config) { return; } ReactDom.render(<InitializeSearchResults config={config} />, element); } export default RenderSearchResults;
-
Make a new .jsx file that will be the entry point for the application. In the following snippet you can find the default code used. Make sure you change the import on line 18 to point to the overriden search results component we just made.
import 'react-hawksearch/dist/esm/react-hawksearch.css'; import 'rheostat/css/rheostat.css'; import 'react-dates/lib/css/_datepicker.css'; import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet'; import cssInterface from 'react-with-styles-interface-css'; import RheostatDefaultTheme from 'rheostat/lib/themes/DefaultTheme'; import ReactDatesDefaultTheme from 'react-dates/lib/theme/DefaultTheme'; ThemedStyleSheet.registerInterface(cssInterface); ThemedStyleSheet.registerTheme({ ...RheostatDefaultTheme, ...ReactDatesDefaultTheme, }); import RenderSearchBox from '../widgets/search-box.jsx'; import RenderSearchResults from '../widgets/search-results.jsx'; function GetConfig(element) { if (!element) { return null; } const config = {}; var indexNameAttr = element.getAttribute('data-indexname'); var languageAttr = element.getAttribute('data-language'); var additionalParametersAttr = element.getAttribute('data-additionalparameters'); var trackedEventsAttr = element.getAttribute('data-trackedevents'); var hawksearchSettingsAttr = element.getAttribute('data-hawksearchsettings'); var searchPageUrlAttr = element.getAttribute('data-searchpageurl'); var indexName = indexNameAttr ? indexNameAttr : ""; var language = languageAttr ? languageAttr : null; var additionalParameters = additionalParametersAttr ? JSON.parse(additionalParametersAttr) : {}; var trackedEvents = trackedEventsAttr ? JSON.parse(trackedEventsAttr) : {}; var hawksearchSettings = hawksearchSettingsAttr ? JSON.parse(hawksearchSettingsAttr) : {}; var searchPageUrl = searchPageUrlAttr ? searchPageUrlAttr : ""; var configOverrides = { additionalParameters: additionalParameters, language: language, indexName: indexName, trackConfig: trackedEvents, searchPageUrl: searchPageUrl }; Object.assign(config, hawksearchSettings); Object.assign(config, configOverrides); return config; } window.addEventListener('load', function() { var searchBox = document.getElementById('hawksearch-react-search-box'); var searchResults = document.getElementById('hawksearch-react-search-results'); RenderSearchBox(searchBox, GetConfig(searchBox)); RenderSearchResults(searchResults, GetConfig(searchResults)); });
-
Build the JS resources as explained in React SDK setup .
-
Make sure the created JS file is loaded in your Layout.cshtml file.
-
You should now be able to see your custom search results.
Business Layer
Indexing API
Multisource indexing: Creating an external indexing service
Goal
The Hawksearch connector exposes an API for notifying external data sources while monitoring their indexing process. This way you can integrate the connector with external data source and modify the indexing flow based on data source indexing outcome. This article will provide information for creating an external indexing service compatible with Hawksearch connector.
Designing the external indexing service
-
Receiving a request from the Hawksearch connector
The service must expose a POST endpoint, which will receive an object with ApiKey and IndexName from the request body.METHOD: POST { "ApiKey": "558ad504-9b2d-4b67-be4f-7ab9ed3028fb", "IndexName": "hawksearchindexname.20201020.112252.suffix" }
The response must follow the HTTP protocol status code semantics.
-
Sending a request to the Hawksearch connector
When the indexing process is completed, the service must send a POST request to administration-site-domain/api/datasource/status containing the ApiKey received from the first request, and Status which notifies if the indexing is successful or failed.Note
You can set the status to either “Success” or “Fail” depending on the indexing result.
METHOD: POST
{
"ApiKey":"558ad504-9b2d-4b67-be4f-7ab9ed3028fb",
"Status":"Success",
"Message":"Rebuild Hawksearch Index finished successfully"
}
ApiKey and Status properties are required.
Creating your own Indexing API Indexer
Goal
The purpose of this article is to show you how to create your own Indexer and how to use it instead of the default one.
Overview
In order to define your custom Hawksearch Indexer which will be used instead of the default one, there are 2 approaches - create your own indexer class which implements the IHawksearchIndexer interface or inherit from the existing Indexer class and override your desired functionality. You also need to register the new indexer class within Kentico’s CMS.Core.Service container.
Steps to creating your own indexer
-
Create a new module to contain the initialization code: Initializing modules to run custom code | Xperience 13 Documentation . For the sake of simplicity, you can create the following files directly inside the CMSApp project that references Hawksearch.Kentico.Xperience.CMS NuGet package. Realistically, you would most likely want to create your own project which will reference Hawksearch.Kentico.Xperience.CMS and then have the CMSApp project reference your newly created project.
-
Either create your own class which implements Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Contracts.IHawksearchIndexer or inherit from Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Indexer.
-
Implement your custom logic.
-
In the module’s OnPreInit method, register your new indexer class. Here CustomIndexer is assumed to be your indexer class, so replace it with your own class name:
protected override void OnPreInit() { base.OnPreInit(); CMS.Core.Service.Use<IHawksearchIndexer,CustomIndexer>(); }
-
Now the system will use your custom indexer.
Example: Adding additional information to a document
Goal
The purpose of this article is to show you how to add additional information to a document. For the purpose of this tutorial, it will be assumed that you have inherited the Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Indexer class and want to extend its behavior instead of writing your own Indexer from scratch.
Prerequisite
Created custom indexer - Kentico: Creating your own Indexing API Indexer
Steps to adding additional information to a document
-
Override the GetDocuments method and write out your custom logic. For example the following snippet adds a custom field called “CustomEventItemFieldName” on all Documents which have a field with a name “ContentType” which has a value of “Event”
public override IEnumerable<SubmitDocument> GetDocuments(IEnumerable<FieldMappingInfo> fieldMappings, IEnumerable<string> identifiers = null) { var documents = base.GetDocuments(fieldMappings, identifiers); foreach (var document in documents) { if (document.Fields.Any(a => a.Name == "ContentType" && a.Values.Any(b => b == "Event"))) { document.Fields.Add(new SubmitField { Name = "CustomEventItemFieldName", Values = new List<string> { "CustomEventItemValue" } }); } } return documents; }
-
Override the CreateIndex method and make sure that the index has the field we just added to our documents.
public override string CreateIndex(IndexMappingInfo indexMapping, IEnumerable<FieldDefinition> fields) { var indexSuffix = indexMapping.IndexDisplayName.Replace(' ', '-').ToLower(); var indexFields = fields.ToList(); if (!indexFields.Any(a => a.Name == "CustomEventItemFieldName")) { indexFields.Add(new FieldDefinition { Name = "CustomEventItemFieldName", Type = "String", IncludeInResults = true }); } var createIndexResponseJson = this.Client.CreateIndex(indexFields, indexSuffix).Content.ReadAsStringAsync().Result; var createIndexResponse = JsonConvert.DeserializeObject<CreateIndexResponse>(createIndexResponseJson); return createIndexResponse.IndexName; }
-
That’s it. Our custom indexer will now add the custom field we created to every event item before sending it to Hawkseach.
Example: Exclude documents based on a field value
Goal
The purpose of this article is to show you how to exclude certain documents from being sent to Hawksearch based on some field value. For the purpose of this tutorial, it will be assumed that you have inherited the Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Indexer class and want to extend its behavior instead of writing your own Indexer from scratch.
Prerequisites
Created custom indexer - Kentico: Creating your own Indexing API Indexer
Steps to excluding documents from being indexed
-
Override the GetDocuments method and implement your custom logic. In the following snippet we will exclude all documents which have an “EventEndDate” field name with a value that is in the past, meaning that the event has already passed.
public override IEnumerable<SubmitDocument> GetDocuments(IEnumerable<FieldMappingInfo> fieldMappings, IEnumerable<string> identifiers = null) { var documents = base.GetDocuments(fieldMappings, identifiers); documents = documents.Where(a => !a.Fields.Any(b => b.Name == "EventEndDate" && DateTime.Now > DateTime.Parse(b.Values.First()))); return documents; }
-
That’s it. Now the indexer will exclude documents which have an event end date in the past.
File Export
Creating your own File Exporter
Goal
The purpose of this article is to show you how to create your own Exporter and how to use it instead of the default one.
Steps to creating your own exporter
-
Create a new module to contain the initialization code: Initializing modules to run custom code | Xperience 13 Documentation . For the sake of simplicity, you can create the following files directly inside the CMSApp project that references Hawksearch.Kentico.Xperience.CMS NuGet package. Realistically, you would most likely want to create your own project which will reference Hawksearch.Kentico.Xperience.CMS and then have the CMSApp project reference your newly created project.
-
Either create your own class which implements Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Contracts.IHawksearchExporter or inherit from Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Exporter.
-
Implement your custom logic.
-
In the module’s OnPreInit method, register your new exporter class. Here CustomExporter is assumed to be your exporter class, so replace it with your own class name:
protected override void OnPreInit() { base.OnPreInit(); CMS.Core.Service.Use<IHawksearchExporter,CustomExporter>(); }
-
Now the system will use your custom exporter.
Example: Exclude documents based on a column value
Goal
The purpose of this article is to show you how to exclude certain documents from being written to a csv file based on some database column value. For the purpose of this tutorial, it will be assumed that you have inherited the Hawksearch.Kentico.Xperience.CMS.Services.Indexing.Exporter class and want to extend its behavior instead of writing your own Exporter from scratch.
Prerequisites
Created custom exporter - Kentico: Creating your own File Exporter
Steps to excluding documents from being indexed
-
Override the AddData method and implement your custom logic. In the following snippet we will exclude all documents which have an “EventEndDate” column name with a value that is in the past, meaning that the event has already passed.
public override IList<DataRetrievingModel> AddData(IList<DataRetrievingModel> data) { var baseData = base.AddData(data); foreach (var baseDataModel in baseData) { if (baseDataModel.Key == "Content") //Exclude only content items. { for (int i = baseDataModel.FeedData.Count - 1; i >= 0; i--) { var columnValue = baseDataModel.FeedData[i]["EventEndDate"]; if (columnValue != null) { var columnDate = DateTime.Parse(columnValue.ToString()); if (columnDate < DateTime.Now) //Date is in the past. { baseDataModel.FeedData.RemoveAt(i); //Remove from content items. } } } } } return baseData; }
-
That’s it. Now the exporter will exclude documents which have an event end date in the past
Updated over 1 year ago