Friday, November 8, 2013

Returning more than 50 results in a search result (ResultScriptWebPart) is a bit tricky

I’ve thought about breaking the 50 item limitation at times, and have received questions if it’s possible to do. And, yes it’s doable….but I won’t vouch for the robustness of my current solution :-)

First off, the technical reason why it blocks at 50 is this piece of code found in Microsoft.Office.Server.Search.WebControls.ResultScriptWebPart

public void set_ResultsPerPage(int value)
{
    try
    {
        DataVerification.ThrowIfOutOfRange(value, "ResultsPerPage", 1, 50, true);
        this.resultsPerPage = value;
    }
    catch (Exception exception)
    {
        ULS.SendTraceTag(0x15a2e3, ULSCat.msoulscat_SEARCH_Query, ULSTraceLevel.Unexpected, "Setting webpart property failed: {0}", new object[] { exception });
        base.Messages.Add(new ControlMessage(null, MessageLevel.Error, -1, exception.GetType().ToString(), exception.Message, "", exception.StackTrace, ULS.CorrelationGetVisibleID().ToString(), false, true, true));
        throw exception;
    }
}

It’s a hardcoded limit. The logical reason behind this limit is could be either one of user experience, or one of performance, as it takes more resources to load up more items.

If you go the custom web part approach, inheriting from the ResultScriptWebPart and using for example reflection to set the private property for ResultsPerPage does not work, as the property itself is being set at page load time, and will then bomb at the limit verification. You could probably disassemble the whole web part and create a new one without the limit, and you would be golden.

In this post I’ll opt for a different approach, I’ll do it client side… which has one drawback, it won’t kick in on the first loading of the result page, as the results are then embedded in the page itself, and not loaded via ajax. This is however solved by kicking off an ajax call automatically after the page has loaded with the effect that the result list will expand fairly quickly after the initial rendering of the page.

On your result pages add a script editor web part and add the JavaScript at the end of this post and you should be able to get more than 50 results. The default max limit for search is 500, which may be changed on the web application for an on-premises farm:

$ssa = Get-SPEnterpriseSearchServiceApplication
$ssa.MaxRowLimit = 1000
$ssa.Update($true)


image


<script>

String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

Sys.Application.add_load( function() {
    // Get a collection of all divs
    var collection = document.getElementsByTagName("div");
    var length = collection.length;
    var resultsPerPage = 200;
    // Iterate all divs to find the control element for search
    for( var i=0; i<length; i++ )
    {        
        if(collection[i].id.endsWith("_csr") // element name ends with _csr
            && collection[i].control != null // element has a control object
            && collection[i].control.set_resultsPerPage != null) // control object supports resultsPerPage property
        {
            // override the web part setting
            collection[i].control.set_resultsPerPage(resultsPerPage);
            break;
        }
    }    
    
    // wait until search box is in the DOM
    var checkExist = setInterval(function() {
       if ($get("searchImg") != null ) {          
          clearInterval(checkExist);
          // Reload the results using ajax with the new limit
          $get("searchImg").parentElement.click();
       }
    }, 100); // check every 100ms
});
</script>