Question

Scenario: Client has BCS wired up directly to SQL Server (stored procedure) to ReadList and ReadItem. Search crawls the BCS external content type nightly. The stored procedures return one-to-many data as delimited text in a single field (think Location field, with potentially each record having multiple Cities listed, each City separated by semi-colon character).

We'd like to display each individual city as an individual refiner in a search page, such that, for example, a value of "Miami;New York;Los Angeles;" would appear as 3 separate refinement values. Selecting either refinement value would return this particular entry as a result.

I see 3 possible options to this:

  1. Manipulate the properties using Content Enrichment sort of like this: Refiners for multi-valued managed properties in SharePoint - this is current approach, though there are lots of moving parts and deployment dependencies.

  2. Make use of a custom display template for the refiner. This looks like it may work, but haven't validated (I've done similar approach for customizing multi value selects in refiners before): https://hyankov.wordpress.com/2014/12/15/sharepoint-2013-refiner-multi-value-contains-instead-of-an-equals/

  3. Modify the BCS to call .NET assembly or WCF service instead of going directly against SQL Server stored procedure (flat results). This would more of a bigger change, but might allow for easier future growth.

My questions are:

A. Am I missing an option to solve for this? B. Do you have a preference on approach? C. Can you speak to using any of these with your experience and have any comments/considerations for the approach?

TIA!

Was it helpful?

Solution

You are missing one approach. If you know all the possible values you can use entity extraction using dictionaries to scan that mp with a multi-value, and have it populate to the WordCustomRefinerX managed properties.

If that's not an option, I would go with your options in the order you list them. For the display template option you might want to add logic to keep the counts correct as well.

OTHER TIPS

I would go for option number two as I did something similar in the past. It requires some development, but in the end, it is easier to maintain.

The approach of the article you linked might work, my approach back then was different. In your refiner display template where you loop over all the available refiners, you split the values based on your delimiter.

Here one example how to approach it:

var refinementFilters = [];
var newRefinerArray = [];

// Fill the arrays with refinement values
for (var i = 0; i < listData.length; i++){
    var filter = listData[i];
    if(!$isNull(filter)) {
        if (filter.RefinementValue.indexOf('\n\n') !== -1) {
            var split = filter.RefinementValue.split(';');
            for (var j = 0; j < split.length; j++) {
                if (newRefinerArray.indexOf(split[j]) === -1) {
                    newRefinerArray.push(split[j]);
                }
            }
        } else {
            if (newRefinerArray.indexOf(filter.RefinementValue) === -1) {
                newRefinerArray.push(filter.RefinementValue);
            }
        }
    }
}


for (var i = 0; i < newRefinerArray.length; i++) {
    var filter = [];
    if(!$isNull(filter)) {
        filter.RefinementName = newRefinerArray[i];
        filter.RefinementTokens = "\"" + newRefinerArray[i] + "\"";

        refinementFilters.push(filter);
    }
}

To visualize the refiners I use this code:

ShowRefiner(filter.RefinementName, filter.RefinementTokens, true);

The HTML markup:

<div><input type="checkbox" value="_#= $htmlEncode(tokens) =#_" id="_#= elmId =#_"> <label class="ms-ref-name" for="_#= elmId =#_">_#= $htmlEncode(refinementName) =#_</label></div>

Behind my apply button I have the following code:

// Retrieve all the checkboxes from the control
var checkboxElms = refinerElm.getElementsByTagName('input');        
// Create a new array
var refiners = [];
// Loop over each checkbox
for (var i = 0; i < checkboxElms.length; i++) {
    var elm = checkboxElms[i];
    // Check if the checkbox is checked
    if (elm.checked) {
        // Append the refiner value to the array
        Srch.U.appendArray(refiners, elm.value);
    }
};

// Call the refinement method with the array of refiners
control.updateRefinementFilters(name, refiners, 'OR', false, null);
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top