Blogger

  • This email address is being protected from spambots. You need JavaScript enabled to view it.

    Recent items

Login

Luca Costante - Items filtered by date: December 2014

In these days, me and This email address is being protected from spambots. You need JavaScript enabled to view it. had working on an easy requirement but that to realized it, we we thought too much.

What we need to realize it is a simple Item type refinement panel that works with a multiple selection, easy.

A first idea was to change the settings on the refinement from single selection to multiple selection. But it not produced what we expected:

  1. The label was changed from Type to extentions. Example: from Web Pages to html, etc.
  2. some items that were found they not had the respective item in the refinement panel and some associations were wrong: example the videos are grouped with the html filter, etc.

After some analysis phases, we were able to make these assumptions:

  • a lot of items were crawled with the dispform.aspx, so they were grouped as html. They had another Managed Property (MP) "SecondaryFileExtention" that contained the correct file extention of the item
  • Some items, for example the video, are managed from SharePoint with a particular Content Type (ContentTypeId=). These items appears in the search both FileType MP and SecondaryFileExtention MP empty
  • to complete the work, the File Type filter need to group on:
    • for some items on the FileType MP
    • for some items on the SecondaryFileExtention MP
    • for some items on the ContentType MP
  • The logic of the refinement panel is not able to apply OR rules (written by himself by the respective refinement panel display template) on different Managed Properties.

At the end of a long week of tests, we had the solution:

  • We created a new Managed Property with the name LC.ResultType that contains the follow crawled properties (all of the single MP)
    • ows_ContentTypeId
    • ows_FileType
    • ows_File_x0020_Type
  • After a full crawl to allow the Managed Property to be filled, we modified the OOB Multi Selection Display Template to point to our Custom Managed Property in each occurrences
  • We creted the follow mapping of the item types, save and publish the new Display Template

Custom Multi selection Display Template example that use the custom Managed Property LC..ResultType:

Show/Hidden sql code

View source
 
 
 
<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:A29FA29F-C1488-11d1-1010-C2F400AA002"> 
<head>
<title>Multi-value</title>
 
<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:CompatibleManagedProperties msdt:dt="string"></mso:CompatibleManagedProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:CompatibleSearchDataTypes msdt:dt="string"></mso:CompatibleSearchDataTypes>
<mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106604</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#Refinement;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:CrawlerXSLFile msdt:dt="string"></mso:CrawlerXSLFile>
<mso:HtmlDesignPreviewUrl msdt:dt="string"></mso:HtmlDesignPreviewUrl>
<mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
<mso:HtmlDesignStatusAndPreview msdt:dt="string">http://SEARCH.lucacostante.com/_catalogs/masterpage/Display Templates/Filters/MultiValue_ResultType.html, Conversion successful.</mso:HtmlDesignStatusAndPreview>
</mso:CustomDocumentProperties></xml><![endif]-->
</head>
<body>
<div id="Filter_MultiValue">
<!--#_
    var Options = {
        ShowCounts: TRUE
    };
 
    var listData = ctx.ListData;
    var hasControl = !$isNull(ctx.RefinementControl) && !$isNull(ctx.ClientControl);
 
    IF(hasControl) {
        var hasNoListData = ($isEmptyArray(listData));
        var propertyName = ctx.RefinementControl.propertyName;
        var displayTitle = Srch.Refinement.getRefinementTitle(ctx.RefinementControl);
        var isExpanded = Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);
        var useContains = FALSE;
        var useKQL = FALSE;
        var refiners = [];
        var refinersAll = [];
        
        IF (hasNoListData) {
            listData = [];
        }
        
        // TODO: unify same FileType
        listData = mapResultType(listData);
        
        IF (ctx.RefinementControl.csr_filters) {
            FOR(var t = 0; t < ctx.RefinementControl.csr_filters.LENGTH; t++) {
                refinersAll.push({
                    PropertyName: ctx.RefinementControl.csr_filters[t].PropertyName,
                    RefinementName: ctx.RefinementControl.csr_filters[t].RefinementName,
                    RefinementTokens: ctx.RefinementControl.csr_filters[t].RefinementTokens,
                    RefinementTokensMap: ctx.RefinementControl.csr_filters[t].RefinementTokensMap,
                    RefinementCount: ctx.RefinementControl.csr_filters[t].RefinementCount,
                    IsSelected: FALSE
                });
            }
        }
 
        var currentRefinementCategory = ctx.ClientControl.getCurrentRefinementCategory(ctx.RefinementControl.propertyName);
        var hasAnyFilterTokens = (!$isNull(currentRefinementCategory) && currentRefinementCategory.get_tokenCount() > 0);
        var renderEmptyContainer = hasControl && (hasNoListData && !hasAnyFilterTokens);
        
        IF(!renderEmptyContainer) {
            var listDataTokenToDisplayMap = {};
            var listDataTokenToCountMap = {};
            IF(!hasNoListData) {
                FOR (var i = 0; i < listData.LENGTH; i++) {
                    var FILTER = listData[i];
                    IF(!$isNull(FILTER)) {
                        FOR (var j = 0; j < FILTER.RefinementTokens.LENGTH; j++) {
                            listDataTokenToDisplayMap[FILTER.RefinementTokens[j]] = FILTER.RefinementName;
                            listDataTokenToCountMap[FILTER.RefinementTokens[j]] = FILTER.RefinementCount;
                        }
                        
                        IF(!hasAnyFilterTokens && !$isEmptyString(FILTER.RefinementName) && !$isEmptyString(FILTER.RefinementTokens)) {
                            refiners.push(
                            {
                                PropertyName: FILTER.RefinerName,
                                RefinementName: FILTER.RefinementName,
                                RefinementTokens: FILTER.RefinementTokens,
                                RefinementTokensMap: FILTER.RefinementTokensMap,
                                RefinementCount: FILTER.RefinementCount,
                                IsSelected: FALSE
                            });
                        }
                    }
                }
            }
            
            IF(hasAnyFilterTokens) {
                FOR(var j = 0; j < currentRefinementCategory.get_tokenCount(); j++) {
                    var token = currentRefinementCategory.t[j];
                    var displayValue = listDataTokenToDisplayMap[token];
                    IF($isEmptyString(displayValue) && !$isNull(currentRefinementCategory.m)) {
                        displayValue = currentRefinementCategory.m[token];
                    }
                    IF(!$isEmptyString(displayValue) && !$isEmptyString(token)) {
                        IF (refinersAll.LENGTH > 0) {
                            FOR (var k = 0; k < refinersAll.LENGTH; k++) {
                                FOR (var t = 0; t < refinersAll[k].RefinementTokens.LENGTH; t++) {
                                    IF (refinersAll[k].RefinementTokens[t] == token) {
                                        refinersAll[k].IsSelected = TRUE;
                                        break;
                                    }
                                }
                            }
                        }
                        ELSE {
                            refiners.push(
                            {  
                                PropertyName: FILTER.RefinerName,
                                RefinementName: displayValue,
                                RefinementTokens: token,
                                RefinementCount: !$isNull(listDataTokenToCountMap[token]) ? listDataTokenToCountMap[token] : 0,
                                IsSelected: TRUE
                            });
                        }
                    }
                }
            }
        }
        
        ctx.RefinementControl["csr_propertyName"] = propertyName;
        ctx.RefinementControl["csr_displayTitle"] = displayTitle;
        //ctx.RefinementControl["csr_filters"] = refiners;
        ctx.RefinementControl["csr_filters"] = hasAnyFilterTokens && refinersAll.LENGTH > 0 ? refinersAll : refiners;
        ctx.RefinementControl["csr_isExpanded"] = isExpanded;
        ctx.RefinementControl["csr_renderEmptyContainer"] = renderEmptyContainer;
        ctx.RefinementControl["csr_useContains"] = useContains;
        ctx.RefinementControl["csr_useKQL"] = useKQL;
        ctx.RefinementControl["csr_showCounts"] = Options.ShowCounts;
 
        // SHOW Item
        renderRefinerItems(ctx);
    }
_#-->
 
<!--#_
 
    FUNCTION mapResultType(listData) {
        var map = { };
        // Standard ResultType...
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSPowerPoint")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["ppt", "pptx", "pps", "ppsx", "odp", "pptm", "potm", "potx", "ppam", "ppsm"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSWord")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["docx", "doc", "dot", "rtf", "txt", "docm", "nws", "dotx"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSExcel")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["xls", "xlsx", "csv", "odc", "ods", "xlsb", "xlsm", "xltm", "xltx", "xlam"]
        }; 
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_AdobePDF")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["pdf"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Video")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x0120D520A8*", "wmv", "avi", "flv", "mov", "mpeg"]
        };
        map["Audio File"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["mp4", "mp3", "wma", "ra", "Podcast"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Archive")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["zip", "rar"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Webpage")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["HTML", "xml", "MHTML"]
        };
 
 
 
        // Specific ResultType 
        map["News"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039E7E22DFD724B12B47BFBC3F5AA086D000580873f397b481ab8c1115a1cfc37a8*", "0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039E7E22DFD724B12B47BFBC3F5AA086D000580873F397B481AB8C1115A1CFC37A800a3b3a6c651184e438510779fe223a2a6*"]
        };
        map["Tool"] ={
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x01000268E795EAEA4ABB8E5C2E8D0AB2485C*"]
        };
        map["PhotoGallery"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039e7e22dfd724b12b47bfbc3f5aa086d00b1d929c83a1a4f37ae818af558a4898200116ce8a6eddf43c9af19c678407d4166*"]
        };
        
        var retListData = NEW Array();
        var assocListData = NEW Array();
        FOR (var i = 0; i < listData.LENGTH; i++) {
            var FILTER = listData[i];
            var mappedRefinementName = NULL;
            FOR(var KEY IN map) {
                IF (map[KEY].RefinerName == FILTER.RefinerName) {
                    FOR (var j = 0; j < map[KEY].RefinementValues.LENGTH; j++) {
                        var actualValue = FILTER.RefinementValue.toLowerCase();
                        var candidateValue = map[KEY].RefinementValues[j].toLowerCase();
 
                        IF (actualValue == candidateValue ||
                            (actualValue.startsWith("0x") && actualValue.startsWith(candidateValue.SUBSTRING(0, candidateValue.LENGTH - 1))))
                        {
                            mappedRefinementName = KEY;
                            break;
                        }
                    }
                    IF (!$isNull(mappedRefinementName)) {
                        break;
                    }
                }
            }
 
            var mappedFilter = NEW Object();
            
            IF (!$isNull(mappedRefinementName)) {
                mappedFilter.RefinerName = map[mappedRefinementName].RefinerName;
                mappedFilter.RefinementCount = FILTER.RefinementCount;
                mappedFilter.RefinementName = mappedRefinementName;
                mappedFilter.RefinementTokens = [];
                //var resultTypeTokenWrapper = (mappedFilter.RefinerName.toLowerCase() == "contenttypeid") ? FUNCTION (x) {RETURN x;} : 
                //                                                                                           Srch.RefinementUtil.stringValueToEqualsToken;
                var resultTypeTokenWrapper = FUNCTION(token) {
                    IF (token.startsWith("0x")) {
                        RETURN token;
                    }
                    
                    RETURN Srch.RefinementUtil.stringValueToEqualsToken(token);
                };
                FOR (var j IN map[mappedRefinementName].RefinementValues) {
                    mappedFilter.RefinementTokens.push(resultTypeTokenWrapper(map[mappedRefinementName].RefinementValues[j]));
                }
 
                IF ($isNull(assocListData[mappedFilter.RefinementName])) {
                    assocListData[mappedFilter.RefinementName] = mappedFilter;
                }
                ELSE {
                    assocListData[mappedFilter.RefinementName].RefinementCount += mappedFilter.RefinementCount;
                }
            }
        }
 
        FOR (var KEY IN assocListData) {
            retListData[retListData.LENGTH] = assocListData[KEY];
        }
 
        RETURN retListData;
    }
_#-->
 
<!--#_
    AddPostRenderCallback(ctx, FUNCTION() {
        $('#Container input[type=checkbox]').ON('click', FUNCTION() {
            IF ($(this).IS(':checked')) {
                $(this).parent().find('label').css("font-weight", "bold").css("color", "#2D2D2D");
            } ELSE {
                $(this).parent().find('label').css("font-weight", "").css("color", "");
            }
        });
    });
_#-->
 
<!--#_
    FUNCTION renderRefinerItems(ctx) {
        var propertyName = ctx.RefinementControl["csr_propertyName"];
        var displayTitle = ctx.RefinementControl["csr_displayTitle"];
        var filters = ctx.RefinementControl["csr_filters"];
        var isExpanded = BOOLEAN(ctx.RefinementControl["csr_isExpanded"]);
        var renderEmptyContainer = BOOLEAN(ctx.RefinementControl["csr_renderEmptyContainer"]);
        var useContains = BOOLEAN(ctx.RefinementControl["csr_useContains"]);
        var useKQL = BOOLEAN(ctx.RefinementControl["csr_useKQL"]);
        var showCounts = BOOLEAN(ctx.RefinementControl["csr_showCounts"]);
 
        IF($isEmptyString(propertyName) || (!$isNull(renderEmptyContainer) && renderEmptyContainer)) { 
_#-->
            <div id="EmptyContainer"></div>
<!--#_
        }
        ELSE IF(!$isNull(filters) && Srch.U.isArray(filters) && !$isEmptyArray(filters)) { 
            var expandedStatus = !$isNull(isExpanded) ? isExpanded : TRUE;
            var iconClass = "ms-core-listMenu-item ";
 
            iconClass += expandedStatus ? " ms-ref-uparrow " : " ms-ref-downarrow ";
_#-->
            <div id="_#= propertyName =#_-MultiRefiner">
            <div id="Container">
                _#= Srch.U.collapsibleRefinerTitle(propertyName, ctx.ClientControl.get_id(), displayTitle, iconClass) =#_
                <div class="ms-ref-unselSec" id="UnselectedSection">
                    <div id="unselShortList" class="ms-ref-unsel-shortList">
<!--#_ 
            FOR (var i = 0; i < filters.LENGTH; i++) {
                var FILTER = filters[i];
                IF(!$isNull(FILTER)) {
                    var inputValues = "";
                    var isSelected = BOOLEAN(FILTER.IsSelected);
                    var inputName = propertyName + '_ChkGroup';
                    var inputId = inputName + "_" + FILTER.RefinementName;
                    var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");
                    var selectClass = " custom-ref-fullwidth " + ((i == (filters.LENGTH - 1)) ? "" : " bordered-bottom ") + (isSelected ? " bold-text " : "");
                    
                    IF (FILTER.RefinementTokensMap) {
                        // multi typed refinement CASE
                        FOR(var KEY IN FILTER.RefinementTokensMap) {
                            inputValues += KEY + ":" + FILTER.RefinementTokensMap[KEY] + "|";
                        }
                    }
                    ELSE {
                        inputValues = FILTER.RefinementTokens;
                    }
_#-->
                        <div id="Value" class="_#= selectClass =#_">
<!--#_
                    IF(isSelected) {
_#-->
                            <div class="cust-filter-ck">
                                <INPUT TYPE="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" VALUE="_#= $htmlEncode(inputValues) =#_" checked="" onclick="MultiRefinement.SubmitRefinement('_#= $scriptEncode(filter.PropertyName) =#_', $getClientControl(this), '_#= $scriptEncode(inputId) =#_', _#= isSelected =#_);" />
                            </div>
<!--#_
                    } ELSE {
_#-->
                            <div class="cust-filter-ck">
                                <INPUT TYPE="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" VALUE="_#= $htmlEncode(inputValues) =#_" onclick="MultiRefinement.SubmitRefinement('_#= $scriptEncode(filter.PropertyName) =#_', $getClientControl(this), '_#= $scriptEncode(inputId) =#_', _#= isSelected =#_);" />
                            </div>
<!--#_
                    }
_#-->
                            <label FOR="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_'>
                                _#= $htmlEncode(FILTER.RefinementName) =#_
                            </label>
<!--#_
                    IF (showCounts) {
_#-->
                            <label FOR="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_ cust-ref-count'>
                                <span id='RefinementCount' class='ms-ref-count ms-textSmall'> (_#= $htmlEncode(Srch.U.toFormattedNumber(FILTER.RefinementCount)) =#_) </span>
                            </label>
<!--#_
                    }
_#-->
                        </div>
<!--#_
                }
            }
_#-->
                    </div>
                </div>
<!--#_
        }
_#-->
            </div>
            </div>
<!--#_
    }
_#-->
</div>
</body>
</html>
 
 
 
 
 

Results:

How expected, now we have a custom refiner that is able to filter on two property (in this case, but you can also add more property): the extention and the content type.

 

 

Published in SharePoint