Wednesday, July 10, 2013

Working with refiners in CSOM–SharePoint 2013

Topics covered in this post:
  • CSOM Search Queries
  • Working with refiners in your queries
  • ModifiedBy and EditorOWSUSER managed properties
I’m playing around using CSOM search in an App these days and find that I am using a lot more time than usual writing my code. The reason is two-fold. One: no IntelliSense, and two: not that much documentation and samples out there.

Also the quirks of setting properties in JSOM takes some learning as you have to know if the property is a collection or not. Writing C"# code, spotting collections is much more intuitive.
I’ll be using Microsoft.SharePoint.Client.Search.Query.KeywordQuery for my samples.

Setting properties on the KeywordQuery object is done in this manner in JSOM:

var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
keywordQuery.set_queryText('my query');
keywordQuery.set_rowLimit(25);
keywordQuery.set_trimDuplicates(false);
keywordQuery.set_enableQueryRules(false);
keywordQuery.set_refiners('DisplayAuthor');


All properties are documented at http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.search.query.keywordquery_properties.aspx using the CSOM API. The JavaScript API is not listed explicitly which it is for some other namespaces at http://msdn.microsoft.com/en-us/library/jj193034.aspx

Next you might want to specify which managed properties you want returned with your results. There is however no set_selectProperties method. The reason is that SelectProperties is a StringCollection and you must first get the collection, then use Add() to add your values.

var properties = ['Title', 'FileType', 'Path', 'Write', 'ModifiedBy'];
var kwqProperties = keywordQuery.get_selectProperties();
for (var i = 0; i < properties.length; i++) {
    kwqProperties.add(properties[i]);
}

Once you have specified your properties it’s time to execute the query and handle the result.

There’s a couple of points to notice in the code below. First of all, ModifiedBy will return the the proper ModifiedBy value as seen in a list or library, and not the Author which can also be extracted from inside the document metadata. ModifiedBy is also a multi-value property, and you want the first value. The others can come from inside the document itself as with Author.


If you want more information on the ModifiedBy user, use the EditorOWSUSER instead. This property is on the format: miksvenson@contoso.com | Mikael Svenson | 693A30232E667C6D656D626572736869707C6D696B7376656E736F6E40636F6E746F736F2E636F6D i:0#.f|membership|miksvenson@contoso.com which you can decode and pick the parts you need. The hex part is the claim in encoded format.

The second point to note is regarding the refiners. To create a filter which you can later use in subsequent queries you construct a property query using Modified and RefinementToken. Modifier will be refinername you passed in the beginning, and token will have an encoded value.

var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
var results = searchExecutor.executeQuery(keywordQuery);

context.executeQueryAsync(onSuccess, onError);

function onSuccess() {
    var tables = results.m_value.ResultTables;
    // Results
    if (tables.length > 0 && tables[0].ResultRows.length > 0) {
        $.each(tables[0].ResultRows, function () {
            var d = new Date(this.Write);
            var ext = this.FileType;
            var title = this.Title;
            var url = this.Path;
            if(this.ModifiedBy)
                var modifiedBy = this.ModifiedBy.split('\n\n')[0]; // can include multiple values
        });
    }

    // Refiners
    if (tables.length > 1 && tables[1].ResultRows.length > 0) {
        $.each(tables[1].ResultRows, function () {
            var refinerName = this.RefinementName;
            var count = this.RefinementCount;
            var filter = this.RefinerName+ ':"' + this.RefinementToken + '"';
        });
    }
}


If you later want to refine on your query with a refinement value you have to use the same approach as for adding properties, as the RefinementFilters is also a StringCollection.

var filterCollection = keywordQuery.get_refinementFilters();
var filter = 'DisplayAuthor:"ǂǂ4d696b61656c205376656e736f6e"';
filterCollection.add(filter);

The TokenValue is nothing really special. The value 4d696b61656c205376656e736f6e above if you decode it from Hex to Ascii will read “Mikael Svenson”. It’s also valid to add filters using the RefinementValue instead RefinementToken.