Friday, August 5, 2011

Creating refinement query parameter for FS4SP by code

[Update: 2011-12-09]
Someone from somewhere took the time to use Reflector and pull out the appropriate code from the FS4SP dlls. Download the code below.

When using the Search Center with FAST Search for SharePoint you will see the query parameters being modified when you interact with the page.

The k= parameter holds your search query. This is useful if you create a link somewhere on your site which redirects you to the search page with a pre-set query. An example is creating a link which displays all the documents from a particular employee containing the word contract.

This can be accomplished by the query:


contract author:”Mikael Svenson”

which in the k= parameter looks like:


k=contract%20author%3A%22Mikael%20Svenson%22

And this works just fine. What doesn’t work is if you have specified a synonym for the word contract. When tacking on the author parameter to the query, it will no longer match in the synonym list, as the synonyms match the whole query term, not just a single word.

Another approach would be to add the author filter as a refiner instead in the r= query parameter, leaving only the word in the k= parameter which will match the synonym definition.

However, if you look at the r= parameter for a query refining on me as an author it looks like this:


r=author%3D%22AQ5NaWthZWwgU3ZlbnNvbgZhdXRob3IBAl4iAiIk%22

The quick ones out there spot this as being a base64 encoded string. If you decode the string and with some investigation you can find out how it’s being built up.

The structure looks like this:
(char)1
(char)length of property value
property value
(char)length of property name
property name
(char)1
(char)2 //length of the next two bytes
^”
(char)2 //length of the next two bytes
“$


For your convenience I have created a small class which does the heavy lifting for you.

The usage is like this:

BuildRefinerParameter builder = new BuildRefinerParameter();
builder.AddRefiner("format", "Adobe PDF");
builder.AddRefiner("companies", "Microsoft");
string url = builder.GetRefinerQueryParameter();

And the class itself:

class BuildRefinerParameter
{
    Dictionary<string,string> _refiners = new Dictionary<string, string>();
    public void AddRefiner(string key, string value)
    {
        _refiners[key] = value;
    }
    public string GetRefinerQueryParameter()
    {
        StringBuilder sb = new StringBuilder();
        foreach (var refiner in _refiners)
        {
            sb.Append(refiner.Key);
            sb.Append('=');
            sb.Append('"');
            string base64encodedRefiner = CodeRefinerParameter(refiner);
            sb.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(base64encodedRefiner)));
            sb.Append('"');
            sb.Append(' ');
        }
        string url = HttpUtility.UrlEncode(sb.ToString().Trim());
        return url;
    }
    private static string CodeRefinerParameter(KeyValuePair<string, string> refiner)
    {
        var refkey = refiner.Key;
        var refval = refiner.Value;
        StringBuilder coded = new StringBuilder();
        coded.Append((char) 1);
        coded.Append((char) refval.Length);
        coded.Append(refval);
        coded.Append((char) refkey.Length);
        coded.Append(refkey);
        coded.Append((char) 1);
        coded.Append((char) 2);
        coded.Append('^');
        coded.Append('"');
        coded.Append((char) 2);
        coded.Append('"');
        coded.Append('$');
        return coded.ToString();
    }
}
}