In Blogs, Techblog

There are a lot of blogs out there which touch on how to export SharePoint search results to an Excel CSV file. However my requirement was to export the results AFTER using the refinement panel to filter deep down into the people search. Fortunately I had previously dabbled a bit with extracting search parameters after refining search results.

A short recap of my previous blog:
A simple people search url can look like this:
https://{host}/search/Pages/peopleresults.aspx?k=test

After using the filters the query transforms to this:

https://{host}/search/Pages/peopleresults.aspx?k=test#Default=%7B%22k%22%3A%22test%22%2C%22r%22%3A%5B%7B%22n%22%3A%22JobTitle%22%2C%22t%22%3A%5B%22%5C%22%C7%82%C7%824d617374657220546573746572%5C%22%22%5D%2C%22o%22%3A%22and%22%2C%22k%22%3Afalse%2C%22m%22%3Anull%7D%2C%7B%22n%22%3A%22Department%22%2C%22t%22%3A%5B%22%5C%22%C7%82%C7%8254657374696e672044657074%5C%22%22%5D%2C%22o%22%3A%22and%22%2C%22k%22%3Afalse%2C%22m%22%3Anull%7D%5D%7D

You can still spot some of our keywords but mostly the result behind the hash is encoded gibberish.

Get the Query

First step is to pull the query from current querystring to get the information behind the hash  (#), after filtering results with the refiners the query gets transformed and added after the hash. If there is no hash, no transformation is needed, then collect the original query after the keyword ‘k’ parameter and return..

function changesearchquery() {
    var hash = "";
    var fullUrl = window.location.href;
    var i = fullUrl.indexOf("#");
    if (i > -1) {
        hash = fullUrl.substring(i);
    }
    else {
        var currentSearch = (location.href.indexOf("k=") > 0) ? location.href.split("k=")[1] : "";
        //if page count exists
        currentSearch = (currentSearch.indexOf("#s=") > 0) ? currentSearch.split("#s=")[0] : currentSearch;
        return currentSearch;
    }

Transform the Gibberish

Next step is to transform the query gibberish to usable search properties. Parse the result to JSON so we can start extracting the data.

    var hashQuery = [];
    var queryGroups = hash.split("#");
    for (var i = 1; i < queryGroups.length; i++) { if (queryGroups[i].length > 0) {
            var keyValue = queryGroups[i].split("=", 2);
            var key = keyValue[0];
            var encodedValue = keyValue[1];

            if (key === "Default") { // json string format
                var jsonStringValue = decodeURIComponent(encodedValue);
                var safejsonStringValue = jsonStringValue.replace("’", "\’").replace("'", "\'");
                var queryObject = JSON.parse(safejsonStringValue);
                hashQuery[key] = queryObject;
            }
            else if (key === "k") { // simple format
                hashQuery[key] = encodedValue;
            }
        }
    }

Rebuild your Query

Extract the search properties and rebuild the query as a usable search api call.

    var filterprops = '';
    if (hashQuery["Default"] !== undefined) { // json string format
        if (hashQuery["Default"].k !== undefined && hashQuery["Default"].k.length > 0) {
            filterprops += "\"" + hashQuery["Default"].k + "\"";
        }
        for (var i = 0; i < hashQuery["Default"].r.length; i++) {
            if (hashQuery["Default"].r[i].m !== null) {  // check if 'm' contains data
                for (var n = 0; n < hashQuery["Default"].r[i].t.length; n++) {
                    var keywordkey = hashQuery["Default"].r[i].n;
                    var keywordvalue = hashQuery["Default"].r[i].m[hashQuery["Default"].r[i].t[n]];
                    filterprops = addFilterProp(keywordkey, keywordvalue, filterprops);
                }
            }
            else {
                for (var n = 0; n < hashQuery["Default"].r[i].t.length; n++) {
                    var tvalue = hashQuery["Default"].r[i].t[n];
                    if (tvalue.indexOf('ǂ') !== -1) {
                        // value is HEX type
                        var keywordkey = hashQuery["Default"].r[i].n;
                        var keywordvalue = hex2a(tvalue);
                        filterprops = addFilterProp(keywordkey, keywordvalue, filterprops);
                    }
                    else {  // simple format
                        var keywordkey = hashQuery["Default"].r[i].n;
                        var keywordvalue = tvalue;
                        filterprops = addFilterProp(keywordkey, keywordvalue, filterprops);
                    }
                }
            }
        }
    }
    else if (hashQuery["k"] !== undefined) { // simple value (no json)
        filterprops += "\"" + hashQuery["k"] + "\"";
    }
    return filterprops;
}

function addFilterProp(keywordKey, keywordValue, filterProps) {
    if (!filterProps === '') {
        filterProps += " AND ";
    }
    filterProps += "\"" + keywordKey + "\":\"" + keywordValue + "\" ";
    return filterProps;
}
function hex2a(hexx) {
    var hex = hexx.toString();
    hex = hex.replace('"', '').replace('"', '').replace('ǂ', '').replace('ǂ', '');
    var str = '';
    for (var i = 0; i <= (hex.length - 2); i += 2) {
        str += String.fromCharCode(parseInt(hex.substring(i, i + 2), 16));
    }
    return str;
}

Query through REST

Run the query through the REST api (note: Max 500 items can be retrieved through the rest api)

function getSearchResultsUsingREST(queryText) {
    Results = {
        element: '',
        url: '',
        init: function (element) {
            Results.element = element;
            Results.url = _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext='" + queryText + "'&sourceid='bbb05724-f652-4b86-beff-af1284e8e789'&selectproperties='PersonellID,PreferredName,JobTitle,WorkEmail,WorkPhone,MobilePhone,Office1,Room,Office2,Room2,Practice,Team,KnowledgeGroup,ProfessionGroup,Jurisdiction,Secretary'";
        },
        load: function () {
            $.ajax(
                {
                    url: Results.url,
                    method: "GET",
                    headers: {
                        "accept": "application/json;odata=verbose"
                    },
                    success: Results.onSuccess,
                    error: Results.onError
                }
            );
        },

        onSuccess: function (data) {
            var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
            var propArray2 = ["Personell ID", "Name", "Job Title", "Work e-mail", "Work phone", "Mobile phone", "Primary office", "Primary room number", "Secondary office", "Secondary room number", "Practice group", "Team", "Knowledge group", "Profession group", "Jurisdiction", "Secretary"];
            var exportedResult2 = new Array();

            // Get only the required the managed properties.
            for (var j = 0; j < results.length; j++) {
                var obj = new Object;
                for (var i = 0; i < propArray2.length; i++) {
                    if (results[j].Cells.results[i + 2].Value !== null) {
                        if (results[j].Cells.results[i + 2].Value.match(/"|,/)) {
                            results[j].Cells.results[i + 2].Value = '"' + results[j].Cells.results[i + 2].Value + '"';
                        }
                    }
                    obj[propArray2[i]] = results[j].Cells.results[i + 2].Value ? results[j].Cells.results[i + 2].Value : "";
                }
                exportedResult2.push(obj);
            }
            showSave(ConvertToCSV(JSON.stringify(exportedResult2), propArray2), "ExportedSearchResult.csv", "text/csv; charset=UTF-8");
        },
        onError: function (err) {
            alert(JSON.stringify(err));
        }
    };
    Results.init($('#resultsDiv'));
    Results.load();
}

Save it!

And save and format as CSV:

function showSave(data, name, mimeType) {
    resultBlob = new Blob([data], { type: mimeType });
    if (window.navigator.userAgent.indexOf("MSIE ") > 0 || !!window.navigator.userAgent.match(/Trident.*rv:11./)) {
        navigator.msSaveBlob(resultBlob, name);
    }
    else //other browsers : Chrome/FireFox (Supported Data URIs)
    {
        //creating a temporary HTML link element (they support setting file names)
        var link = document.createElement('a');
        var url = URL.createObjectURL(resultBlob);
        link.setAttribute("href", url);
        link.setAttribute("download", "ExportedSearchResult.csv");
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
}
function ConvertToCSV(objArray, headerArray) {
    var array = typeof objArray !== 'object' ? JSON.parse(objArray) : objArray;
    var str = '';

    // Create header row.
    var headerLine = '';
    for (var i = 0; i < headerArray.length; i++) {
        if (headerLine !== "") {
            headerLine += ',';
        }
        headerLine += headerArray[i];
    }
    str += headerLine + '\r\n';
    // Create CSV body.
    for (var l = 0; l < array.length; l++) {
        var line = '';
        for (var index in array[l]) {
            if (line !== '') {
                line += ',';
            }
            var isnum = /^\+?\d+$/.test(array[l][index]);
            if (isnum) {
                line += '"=""' + array[l][index] + '"""';
            }
            else {
                line += array[l][index];
            }
        }
        str += line + '\r\n';
    }
    return str;
}

All this can come together by adding a button to your page:

<div style="display: table-cell;"><a id="idExportSearchResults" href="#">Export Result</a></div>

which calls:

function getExcel(ctx) {
    var a = document.getElementById("idExportSearchResults");
    var currentSearch = changesearchquery();
    var decodedCurrentSearch = decodeURIComponent(currentSearch);
    getSearchResultsUsingREST(decodedCurrentSearch);
}

Download the full js file here.

Vul je zoekopdracht in.

PowerApps screen styleguide