Assuming that the SearchUnit scripts are located in a sub folder named Keyoti_SearchEngine_Web_Common, and the index is in a sub folder named Keyoti_Search_Index. The following page will create a search box control inside the DIV with id sew_searchBoxControl, and a search result viewer inside the DIV with id sew_searchResultControl.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link href="Keyoti_SearchEngine_Web_Common/SearchUnit.css" rel="stylesheet" />
<script src="Keyoti_SearchEngine_Web_Common/SearchUnit.js"></script>
</head>
<body>
<div id="sew_searchBoxControl"></div>
<div id="sew_searchResultControl"></div>
</body>
</html>
If you are using .NET 2, you must also call
keyotiSearch.useWCFService=false;
Note: If you get this error "Request format is unrecognized for URL unexpectedly ending in ..." then you may need to add the HttpPost protocol to the webservices section of web.config, eg.
<configuration> <system.web> <webServices> <protocols> <add name="HttpGet"/> <add name="HttpPost"/> </protocols> </webServices> </system.web> </configuration>
Example output after a query has been entered.
If the script folder is not the default Keyoti_SearchEngine_Web_Common then it is necessary to point to the folder;
...
<link href="/myscriptfolder/SearchUnit.css" rel="stylesheet" />
<script src="/myscriptfolder/SearchUnit.js"></script>
<script type="text/javascript">
keyotiSearch.commonFolderUrl = "/myscriptfolder";
</script>
...
If the index directory is not the default Keyoti_Search_Index then it is necessary to point to the folder (use the tilda ~ notation to point to the application root, or use an absolute file path);
...
<script type="text/javascript">
keyotiSearch.indexDirectory = "~/myindexdirectory";
</script>
...
Building on the Single Page Setup example above, it is possible to initiate the search from another page.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link href="Keyoti_SearchEngine_Web_Common/SearchUnit.css" rel="stylesheet" />
<script src="Keyoti_SearchEngine_Web_Common/SearchUnit.js"></script>
</head>
<body>
<div id="sew_searchBoxControl"></div>
<div id="sew_searchResultControl"></div>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link href="Keyoti_SearchEngine_Web_Common/SearchUnit.css" rel="stylesheet" />
<script src="Keyoti_SearchEngine_Web_Common/SearchUnit.js"></script>
<script type="text/javascript">
keyotiSearchBox.resultURL = "search.html";
</script>
</head>
<body>
<div id="sew_searchBoxControl"></div>
</body>
</html>
If you are using .NET 2, you must also call
keyotiSearch.useWCFService=false;
The sew_searchBoxControl can be laid out in anyway, provided it has at least an <input type="text" id="sew_searchBox" /> element (extra attributes such as class and style can be added too) and an element with id sew_searchButton.
<div id="sew_searchBoxControl">
<input type="text" id="sew_searchBox" style="border: 2px solid black; font-size:20px;"/>
<a href="#stayhere" onclick="keyotiSearchBox.doSearch()" id="sew_searchButton">Search</a>
</div>
<div id="sew_searchBoxControl">
<input type="text" id="sew_searchBox" />
<a href="#stayhere" onclick="keyotiSearchBox.doSearch()" id="sew_searchButton"><img src="...." /></a>
</div>
Image roll-over effects can be achieved using conventional techniques such as CSS sprites or Javascript mouseovers.
The sew_searchResultControl has a template mechanism to define it's layout and elements (such as custom data). There are 3 component DIVs to the sew_searchResultControl, which may be optionally specified; div class='sew_ajax_loader_backer' (which holds the loading animation, defined in CSS), div class='sew_ajax_error' (which holds any error messages) and div id='sew_resultView' (which displays search results).
sew_resultView contains 3 DIV components, which are the header (id='sew_resultHeader'), result list (id='sew_resultList') and footer (id='sew_resultFooter').
<div id="sew_searchResultControl">
<div class="sew_ajax_loader_backer"><div class="sew_ajax_loader"></div></div>
<div class="sew_ajax_error">
<span class='sew_errorTitle'>Error</span>
<p class='sew_errorBody'></p>
<div class="sew_ajax_error_footer">
<input type="button" value="OK" id="sew_errorOKButton" />
</div>
</div>
<div id="sew_resultView">
<div id="sew_resultHeader">
</div>
<div id="sew_resultList">
</div>
<div id="sew_resultFooter"></div>
</div>
</div>
Elements can be arranged as desired and templates defined by the developer can control the layout and content of the header, result list and footer.
For example;
...
<div id="sew_resultHeader">
<div id="sew_headerTEMPLATE" class="sew_header">
Showing result page <b>${PageNumber}</b>.
There are <b>${NumberOfResults}</b> results for <b>“${Query}”</b>.
<span id="sew_ignoredWords">The following common words were ignored: ${IgnoredWords}</span>
<div id="sew_didYouMean">Did you mean: <a href="#stayhere"
onclick="keyotiSearch.search('${SuggestedSearchExpressionEscaped}', 1)">${SuggestedSearchExpression}</a></div>
</div>
</div>
...
The dynamic elements are: ${PageNumber} (the result page being shown), ${NumberOfResults} (the number of results in total), ${Query} (the search query that the results are for) and ${IgnoredWords} (list of any common words that were ignored, the stop words). It is also possible to place other controls, such as the sort control inside the header, this is discussed in the custom data filters section.
Note that it is important to wrap the 'ignored words' section with an element with id sew_ignoredWords, because this element is automatically made invisible if there are no ignored words.
Note that it is important to wrap the 'did you mean' section with an element with id sew_didYouMean, because this element is automatically made invisible if there are no misspelled words.
For example;
...
<div id="sew_resultFooter">
<div id="sew_footerTEMPLATE" class="sew_footer">
<span id="previousPageLink">${PreviousPageLink}</span>
<span id="pageLinksBlock">${PageLinksBlock}</span>
<span id="nextPageLink">${NextPageLink}</span>
</div>
</div>
...
The dynamic elements are: ${PreviousPageLink} (link text to the previous page), ${PageLinksBlock} (links to each page number), and ${NextPageLink} (link text to the next page).
For example;
...
<div id="sew_resultList">
<div id="sew_resultItemTEMPLATE" class="sew_resultItem">
<span class="sew_resultItemLink"><a href="${UriStringWithKeywords}">${Title}</a></span>
<span class="sew_resultItemSummary">${Summary}</span>
<span class="sew_previewResultWrapper"><img alt="Click to preview the document text" src="/Keyoti_SearchEngine_Web_Common/ResultPreview_Expander_Closed.png"
onclick="keyotiSearchResultPreviewer.toggleResultPreview(this,
'${UriStringAsStored}',
'/Keyoti_SearchEngine_Web_Common/ResultPreview_Expander_Closed.png',
'/Keyoti_SearchEngine_Web_Common/ResultPreview_Expander_Opened.png')"/>
<span class="sew_previewResultContent">Loading document...</span></span>
<div style="clear:both; height:1px;"></div>
<span class="sew_resultItemURL">${UriString}</span>
<span class="sew_keywordHitMap">${KeywordHitMap}</span>
<span class="sew_location">${Location}</span>
<span class="sew_location">${Content}</span>
<!--Example Custom data elements-->
<img data-src="${CustomDataDictionary.img}" />
<span class="${CustomDataDictionary.dateDisplayClass}">Publish date: ${CustomDataDictionary.date}</span>
</div>
</div>
...
There are no requirements for layout, element types or data elements to show - the above is just an example that uses what is available.
The dynamic elements are: ${UriString} (the URL for the result), ${Title} (the document/page title), ${Summary} (the result summary, this can be set to be dynamic or statically generated, see configuration), ${UriStringAsStored} (the document URI as it is contained in the index, this can be different to ${UriString}, typically for database results), ${KeywordHitMap} (a breakdown of number of hits within each document for each keyword), ${Location} (the document's location category), ${Content} (the document's content categories) and ${CustomDataDictionary.*} (custom data fields).
If the document contains custom data, stored in URL encoded GET parameter style (eg. fieldName1=data1&fieldName2=data2) then that data is made available to the template through the CustomDataDictionary object. In the above example, the 'img' and 'date' fields from the document's custom data are displayed.
Custom Data Dictionary formatting:
Note that the img tag set "data-src" instead of "src", this is only because if the "src" attribute is set to ${CustomDataDictionary.img}, the browser will attempt (and fail) to download the image from the URL http://.../${CustomDataDictionary.img}. By using data-src this is avoided since the "src" attribute is only set when the template is used for result generation.
In order to hide the custom data field and its surrounding text, the dynamically generated CSS class ${CustomDataDictionary.dateDisplayClass} is used. Each field has a dynamically generated class ${CustomDataDictionary.fieldNameDisplayClass}. When there is no text in the custom data field, the class name will be fieldNameNoContent, and when there is content it will be fieldNameContent. For example;
...
<span class="${CustomDataDictionary.authorDisplayClass}">Author: ${CustomDataDictionary.author}</span>
...
If the 'author' custom data field contains no data, the generated HTML will be;
...
<span class="authorNoContent">Author: </span>
...
Therefore adding a style rule to hide the span will make the "Author" text invisible if there is no author information.
.authorNoContent{
display:none;
}
The text output from ${CustomDataDictionary.fieldName} is directly from the custom data field in the index, however it can be modified with a Javascript function at search time. For example, specify a function for keyotiTemplateUtility.formatCustomData;
keyotiTemplateUtility.formatCustomData = function (customDataDictionaryItemName, value) {
if (customDataDictionaryItemName == 'publishDate' && value != null && value.length == 10) {
//format date to user's locale
var date = new Date(value.substring(0, 4), value.substring(5, 7) - 1, value.substring(8, 10));
return date.toLocaleDateString();
}
//default provide as-is.
return value;
};
This code (which should be called after the Keyoti scripts have been imported) will convert the date format for the 'publishDate' field from yyyy-mm-dd to the user's locale format (eg. mm/dd/yyyy in the US).