Query Refinement in SharePoint

Learn how to use SharePoint query refinement features programmatically when you are working with search queries and results.

You can use query refinement features to provide end users with refinement options that are relevant for their queries. These features let the end user drill down into search results by using refinement data computed for the results. Refinement data is calculated by the index component, based on the aggregation of managed property statistics for all of the results of a search query.

Typically, query refinement is used for metadata associated with the indexed items, such as creation date, author, or file types that appear in the item. By using the refinement options, you can refine your query to display only items created during a certain time period, or display only items of a specific file type.

Using refiners in the Query Object Model

There are two queries involved in query refinement:

  1. You can request for a set of refiners to be returned in the search results by adding a refiner spec to the end user's query. A refiner spec is the input for the Refiners property. This query is run against the search index. The search results will consist of relevant results and refinement data.
  2. You can use the refinement data to drill down into the search results by, creating a refined query. Add the RefinementFilters property to the query so that the final search results will meet the requirements of both the original query text from the end user and the selected refinement option from the refinement data.

The following sections describe these steps in detail, and provide code examples.

Adding refiners to the end-user's query by using refiner specs

You can specify the requested query refiners by using the Refiners property of the KeywordQuery class. Use the following syntax to specify the requested query refiners:

<refiner>[,<refiner>]*

Each refiner has the following format:

<refiner-name>[(parameter=value[,parameter=value]*)]?

Where:

  • <refiner-name> is the name of the managed property associated with the refiner. This managed property must be set to Refinable or Sortable in the search schema.
  • The optional list of parameter=value pairs specifies non-default configuration values for the named refiner. If a parameter for a refiner is not listed inside the parentheses, the search schema configuration gives you the default setting. Table 1 lists possible values for the parameter=value pairs.

Note

When you specify refiners, the minimum requirement is to specify a refiner-name, which is a managed property.

Example

Refiners = "FileType"

Or, you can also use the advanced syntax to adjust the refiner settings:

Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22),companies"

Table 1: List of parameters for refiners

Parameter Description
deephits
Overrides the default number of hits that is used as the basis for refinement computation. When refiners are produced, all results for the query will be evaluated. Normally, using this parameter will improve search performance.
Syntax
deephits=<integer value>
Example
price(deephits=1000)
Note: This limit applies within each index partition. The actual number of hits that are evaluated will be larger than this value due to the aggregation across search partitions.
discretize
Specifies custom intervals (refinement bins) for numeric refiners.
Syntax
discretize=manual/<threshold>/<threshold>[/<threshold>]*
Example
write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22)
The <threshold> attribute specifies the threshold for each refinement bin.
There is one interval for everything below the first threshold specified, one interval between each consecutive threshold, and one interval for everything above the last threshold.
For a refiner of type DateTime, specify the threshold according to one of the following ISO 8601-compatible formats:
  • YYYY-MM-DD
  • YYYY-MM-DDThh:mm:ss
  • YYYY-MM-DDThh:mm:ss:Z
The format values specify the following:
  • YYYY - Four-digit year. Only four-digit years are supported.
  • MM - Two-digit month. 01 = January.
  • DD - Two-digit day of month (01 through 31).
  • T - The letter "T".
  • hh - Two-digit hour (00 through 23). A.M. or P.M. indication is not allowed.
  • mm - Two-digit minute (00 through 59).
  • ss - Two-digit second (00 through 59).
Important: All date/time values must be specified according to the Coordinated Universal Time (UTC), also known as Greenwich Mean Time (GMT) zone. The UTC zone identifier (a trailing "Z" character) is optional.
sort
Defines how to sort the bins within a string refiner.
Syntax
sort=<property>/<direction>
The attributes perform the following:
  • <property> - Specifies the sorting algorithm. These lowercase values are valid:
    • frequency - Orders by occurrence within the bins.
    • name - Orders by label name.
    • number - Treats the strings as numeric and uses numeric sorting. This value can be useful to specify discrete values, for example, to perform numeric sorting of a value that is contained in a managed property of type String.
  • <direction> - Specifies the sorting direction. These lowercase values are valid :
    • descending
    • ascending
Example
sort=name/ascending
Default: frequency/descending
filter
Defines how bins within a refiner of type String are filtered before they are returned to the client.
Syntax
filter=<bins>/<freq>/<prefix>[<levels>]
The attributes perform the following:
  • <bins> - Specifies the maximum number of returned bins.
    After sorting the bins within the refiner, use this attribute to truncate any trailing bins. For example, if bins=10, only the first 10 bins are returned, according to the specified sorting algorithm.
  • <freq> - Limits the number of returned bins.
    Use this attribute to remove bins that have low frequency counts. For example, freq=2 indicates that only the bins with 2 or more members are returned.
  • <prefix> - Specifies that only bins with a name that begins with this prefix are returned.
    The wildcard character "*" will match all names.
    Example
    filter=30/2/*
  • <levels> - Specifies the levels of taxonomy.
    Use this attribute to filter hierarchical refiner output based on taxonomy levels. The attribute should be a positive integer n. The default value is n=0. If n>0, only refiner entries that contain less than n taxonomy path separator symbols ("/") will be returned. If n=0, no level filtering is performed. The level separator is the forward slash character ("/").
    Be aware of the performance cost of using a large taxonomy navigator. The filtering is performed as part of the result processing.
Note: This parameter applies application-specific result-side filtering. This differs from the cutoff parameter, which applies limitations in every index partition for performance reasons.
cutoff
Limits the data that must be transferred and processed for deep string refiners. You can configure the refiners to return only the most relevant values (bins).
Note: This cutoff filtering is performed within each index partition. This differs from the filter parameter, which performs result-side filtering only. You can combine the two parameters.
Syntax
cutoff=<frequency>/<minbins>/<maxbins>
The attributes perform the following:
  • <maxbins> - Limits the number of bins.
    This parameter limits the number of unique values (bins) that will be returned for a refiner. It is the preferred way to increase search performance when string refiners with large number of bins are returned. "0" implies that the default value specified in the search schema is used.
  • <frequency> - Limits the number of bins by frequency.
    If the number of occurrences of a refiner value in a result set is less than or equal to the frequency value, the refiner value is not returned. "0" implies that the default value according to the search schema is used.
  • <minbins> - Specifies the minimum value for frequency cutoff.
    If the number of unique refiner values for the query is less than this value, no frequency cutoff is performed, and all refiner values are returned from that search partition. When cutoff by frequency is used, this parameter can be used to specify a minimum number of unique refiner values that will be returned regardless of the number of occurrences. The default value of this attribute is "0", indicating that frequency cutoff is performed regardless of the number of unique navigator values. "0" implies that the default value specified in the index schema is used.
Note: Use <frequency> and <minbins> with care. We recommend that you use only <maxbins>.

Example: Adding refiners

The following CSOM example shows how to programmatically request three refiners: FileType, Write, and Companies. Write represents the last modified date for the item, and uses the advanced syntax to return fixed-size date/time bins.

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    var query = new KeywordQuery(context)
    {
        QueryText = "home",
        Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-
            15/2013-09-21/2013-09-22),companies"
    };

    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);

    context.ExecuteQuery();

    ResultTable relevantResultsTable = results.Value[0];
    ResultTable refinerResultsTable = results.Value[1];
    Console.WriteLine(refinerResultsTable.RowCount + " refinement options:");
    foreach (var refinementOption in refinerResultsTable.ResultRows)
    {
        Console.WriteLine("RefinerName: '{0}' RefinementName: '{1}'
            RefinementValue: '{2}' RefinementToken: '{3}' RefinementCount: '{4}'",
            refinementOption["RefinerName"],
            refinementOption["RefinementName"],
            refinementOption["RefinementValue"],
            refinementOption["RefinementToken"],
            refinementOption["RefinementCount"]
        );
    }
}

Understanding the refinement data in the search result

If you have enabled query refinement for a managed property in your query, the query result contains refinement data split into refinement bins. This data is located in the RefinementResults table ( RefinementResults ) within the ResultTableCollection . One refinement bin represents a specific value or value range for the managed property.The RefinementResults table contains one row per refinement bin, and contains the columns, as specified in Table 2.

Table 2: Data returned for refinement bins

Parameter Description
RefinerName The name of the query refiner.
RefinementName The string that represents the refinement bin. This string is typically used when presenting the refinement options to the users on a search result page.
RefinementValue An implementation-specific formatted string that represents refinement. This data is returned for debugging and is typically not required for the client.
RefinementToken A string that represents the refinement bin to use with RefinerName when you perform a refined query.
RefinementCount The result count for this refinement bin. This data represents the number of items (including duplicates) in the search result with a value for the given managed property that falls into this refinement bin.

Example: Refinement data

Table 3 below contains two rows of refinement data. The first row holds refinement data for indexed items, where the file type is HTML. The second row holds refinement data for indexed items, where the last modified time is from 2013/09/21 until 2013/09/22.

Table 3: Format and contents of refinement data

RefinerName RefinementName RefinementValue RefinementToken RefinementCount
FileType Html Html "????68746d6c" 50553
Write From 2013-09-21T00:00:00Z up to 2013-09-22T00:00:00Z From 2013-09-21T00:00:00Z up to 2013-09-22T00:00:00Z range(2013-09-21T00:00:00Z, 2013-09-22T00:00:00Z) 37

Creating a refined query

A search result presents refinement options in the form of string values or value ranges. Each string value or numeric value range is called a refinement bin, and each refinement bin has an associated RefinementToken value. A refinement option is associated with a managed property, which is provided by the RefinerName value.

Both the RefinementToken and RefinerName values are concatenated to create a refinement filter string. This string represents a filter that can be used to limit search result items to include only items where a managed property has a value within a refinement bin. In short:

refinement filter = <RefinerName>:<RefinementToken>

You can provide one or more refinement filters for a refined query by adding refinement filters to the RefinementFilters property of the KeywordQuery class. Multiple refinement filters enable you to provide multilevel drill-down into the search results, and to apply refinement on multivalued properties. For example, you can refine the query to items that have two authors - each represented by a refinement bin - but exclude items that have only one of the authors.

Example 1: Creating a refined query for HTML file types

The following CSOM example shows how to programmatically perform a refinement, to limit the search results to those of HTML file type only. As mentioned in Example: Refinement data, refinement data related to this refinement option has RefinerName set to Filetype and RefinementToken set to "????68746d6c".

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    var query = new KeywordQuery(context)
    {
        QueryText = "home"
    };

    query.RefinementFilters.Add("FileType:\\"????68746d6c\\"");
    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);

    context.ExecuteQuery();

    ResultTable relevantResultsTable = results.Value[0];
    var resultCount = 1;
    foreach (var relevantResult in relevantResultsTable.ResultRows)
    {
        Console.WriteLine("Relevant result number {0} has file type {1}.",
            resultCount, relevantResult["FileType"]);
            resultCount++;
    }
}

Example 2: Creating a refined query by using previously obtained refinement data

The following CSOM example shows how to run a query with a refiner spec to create refinement data which is subsequently used to perform refinement. This example simulates the process of an end user selecting the first refinement option.

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    // Step 1: Run the query with refiner spec to provide refinement data in search result
    var query = new KeywordQuery(context)
    {
        QueryText = "home",
        Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22),companies"
    };

    Console.WriteLine("Run query '{0}' with refiner spec '{1}'.", query.QueryText, query.Refiners);
    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);
    context.ExecuteQuery();

    // The query has been run and we can now look at the refinement data, to view the
    // refinement options
    ResultTable relevantResultsTable = results.Value[0];
    ResultTable refinerResultsTable = results.Value[1];
    Console.WriteLine("Got back {0} refinement options in the result:",
        refinerResultsTable.RowCount);

    foreach (var refinementOption in refinerResultsTable.ResultRows)
    {
        Console.WriteLine("RefinerName: '{0}' RefinementName: '{1}'
            RefinementValue: '{2}' RefinementToken: '{3}' RefinementCount: '{4}'",
            refinementOption["RefinerName"],
            refinementOption["RefinementName"],
            refinementOption["RefinementValue"],
            refinementOption["RefinementToken"],
            refinementOption["RefinementCount"]
        );
    }

    // Step 2: Run the refined query with refinement filter to drill down into
    // the search results. This example uses the first refinement option in the refinement
    // data, if available. This simulates an end user selecting this refinement option.
    var refinementOptionArray = refinerResultsTable.ResultRows.ToArray();

    if (refinementOptionArray.Length > 0)
    {
        var firstRefinementOption = refinementOptionArray[6];
        // Construct the refinement filter by concatenation
        var refinementFilter = firstRefinementOption["RefinerName"] + ":" +
            firstRefinementOption["RefinementToken"];
        var refinedQuery = new KeywordQuery(context)
        {
            QueryText = query.QueryText
        };
        refinedQuery.RefinementFilters.Add(refinementFilter);
        refinedQuery.SelectProperties.Add("FileType");
        refinedQuery.SelectProperties.Add("Write");
        refinedQuery.SelectProperties.Add("Companies");
        Console.WriteLine("Run query '{0}' with refinement filter '{1}'",
            refinedQuery.QueryText, refinementFilter);
        var refinedResults = executor.ExecuteQuery(refinedQuery);
        context.ExecuteQuery();
        ResultTable refinedRelevantResultsTable = refinedResults.Value[0];
        var resultCount = 1;
        foreach (var relevantResult in refinedRelevantResultsTable.ResultRows)
        {
            Console.WriteLine("Relevant result number {0} has FileType='{1}',
                Write='{2}', Companies='{3}'",
                resultCount,
                relevantResult["FileType"],
                relevantResult["Write"],
                relevantResult["Companies"]
            );
            resultCount++;
        }
    }
}

See also

Other resources