Thursday, June 20, 2013

Extending the existing search box in SharePoint 2013 with search as you type functionality

Back in February Murad Sæter blogged about using jQuery autocomplete to get real search results from SharePoint 2013 while you are entering the query.

image

Note   Executing live searches like this may cause load on your search server. You should monitor the load or restrict your queries if you see a load issue. You can also increase the delay time from the default 100ms to reduce the load.

imageInspired by this on a recent project I wanted to see if I could add this functionality to the existing search box on the search center.

The search box has three settings for search as you type suggestions. Suggestions based on what everyone searches which is recorded over time (or you can manually add suggestions), suggestions matching people names, and personal favorites – which are searches you perform often.

For my search scenario I was creating a page to find collaboration and project sites. Sort of a search based navigation to locate sites on the intranet. In my particular case I did not have any needs for person name results, and decided to re-use the person name container for real searches instead.

The effect is that while you are entering your query, real searches are executed against the Title field and limiting to sites only (contentclass:STS_Web).

The search suggestions functionality in SharePoint 2013 is provided by ajaxtoolkit.js. In order to extend the existing functionality  I had to override the _update function of the AjaxControlToolkit.AutoCompleteBehavior.prototype which is responsible for the search as you type functionality of the search box.

I also chose to use jQuery ajax and the search REST api in order to get my search results.

A simple way of adding the functionality is to drop a script editor web part on the page and simply paste in the code in this post. The code has some inline comments to describe what is going on.

image

<script type="text/javascript">
function Override() {
// Keep a copy of the original function
AjaxControlToolkit.AutoCompleteBehavior.prototype._update2 = AjaxControlToolkit.AutoCompleteBehavior.prototype._update;

// register the searchOnSuccess on the same prototype object in order for the update2 function to keep the
// context with all variables
AjaxControlToolkit.AutoCompleteBehavior.prototype.searchOnSuccess = function(data,prefixText,completionItems,cacheResults) {
var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;

var names = [];
for (var i = 0; i < results.length; i++) {
var title = results[i].Cells.results[3].Value;
// Add highlighting of the search term
var idx = title.toLowerCase().indexOf(prefixText.toLowerCase());
if( idx >= 0) {
var hhtitle = title.substr(0,idx) + "" + title.substr(idx,prefixText.length) + "" + title.substr(idx+prefixText.length);
names.push(hhtitle);
} else {
names.push(title); // fallback if indexof fails
}
/* href = results[i].Cells.results[6].Value; */
}
// put our results in the people name container as we're not using it on our page
completionItems.set_peopleNames(names);
// call the original update function which renders out the results
this._update2(prefixText, completionItems, cacheResults);
};

// Register an overload update function which executes a real search
AjaxControlToolkit.AutoCompleteBehavior.prototype._update = function(prefixText, completionItems, cacheResults) {
var context = this;
$.ajax(
{
// Get top 5 results searching only on title field. Other paramaters can be added as well
// Query term is what the user has entered with a wildcard appended
url: _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?rowlimit=5&querytext='title:\"" + prefixText + "*\"'",
method: "GET",
headers: {
"accept": "application/json;odata=verbose;charset=utf-8",
},
success: function(data){ context.searchOnSuccess(data,prefixText,completionItems,cacheResults);},
error: onError
}
);

function onError (err) {
alert(JSON.stringify(err));
}
}

}

// Replace url to your own jquery path
RegisterSod("jquery.min.js", "/_layouts/15/mAdcOW.SearchSuggestions/js/jquery-1.9.0.min.js");
LoadSodByKey("jquery.min.js", function () {
$(function () {
// Make sure ajaxtoolkit is loaded before registering the functions
ExecuteOrDelayUntilScriptLoaded(Override, 'ajaxtoolkit.js');
});
});
</script>

13 comments:

  1. Very nice post...thanks

    ReplyDelete
  2. Hi Mikael,

    Exactly found, what i was looking.
    Just one question, Will it work for read only user ? as we are using "._update" function. Not sure because of that or not.

    I used the same code. If i logged in with OOTB-Contributor user. It works. It doesn't work with read only user.

    any thought on it.






    ReplyDelete
    Replies
    1. Search suggestions won't work for anon users. I have another post on how to achieve that for on-premises with a custom http handler.

      Delete
  3. Works fine - thanks for sharing - but how to I get the user into the e.g site /document set that is returned and not redirected to the search result page?

    ReplyDelete
    Replies
    1. Add "A" html tags in the list you create to navigate you. It's all html.

      Delete
    2. In line 17 in the sample above where the string is created.

      Delete
  4. can you elaborate? (sorry - I'm a syntax NOOB .. :)... )

    ReplyDelete
    Replies
    1. Sorry, but don't have time. You need to learn some javascript and html and not just copy/paste :)

      Delete
  5. Hi Mikael,
    thanks for this example its working fine on chrome but unfortunately it's did not work on IE , could you please support me.

    ReplyDelete
    Replies
    1. Which version of IE, and open dev tool with F12 and look in the console to see if there are any errors.

      Delete
  6. thanks for your quick response, i have IE 11 and there is no error on dev tool but i have a lot of warning:

    [22][AppendScriptsToLoad][Script '/_catalogs/masterpage/display templates/search/control_searchbox.js?ctag=1157$$15.0.4753.1000' added.]
    [23][No_ID:Srch.ScriptApplicationManager][-----RaiseLoadEvent-----][]
    [24][ctl00_PlaceHolderSearchArea_SmallSearchInputBox1_csr:Srch.SearchBox][ScriptApplication_Load][ClientControl]
    [25][ctl00_ctl39_g_4ee207f7_851a_46fa_b991_a7c91f228f95_csr:Srch.TaxonomyRefinement][ScriptApplication_Load][ClientControl]
    [26][ctl00_ctl39_g_4ecd8e67_3b09_4b95_aba0_f70be6932d12_csr:Srch.SearchBox][ScriptApplication_Load][ClientControl]

    ReplyDelete