This post is a contribution from Paul Chan, an engineer with the SharePoint Developer Support team
A customer brought an issue to my attention recently that using the server-side object model Microsoft.Office.Server.Search.Query.SearchExecutor class with Query Rules defined in SharePoint could get inconsistent relevant results.
There is a popular code example about the use of the server-side SearchExecutor class to search with relevant results. The example can also be found in this MSDN article: http://msdn.microsoft.com/en-us/library/office/dn423226(v=office.15).aspx as follow:
using (SPSite siteCollection = new SPSite("<serverRelativeUrl>"))
KeywordQuery keywordQuery = new KeywordQuery(siteCollection);
keywordQuery.QueryText = "SharePoint";
SearchExecutor searchExecutor = new SearchExecutor();
ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
resultTableCollection = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
ResultTable resultTable = resultTableCollection.FirstOrDefault();
DataTable dataTable = resultTable.Table;
It is found that when checking the DataTable (dataTable), the number of results is different sometimes.
The focus of the issue here is resultTableCollection.FirstOrDefault(). What happens is that without any query rules defined in SharePoint, the resultTableCollection only contains one ResultTable. In this case FirstOrDefault doesn't matter since there is only one table going to return anyways. However, adding a Query Rule (can be done in Central Administration\Search Service Application\QueryRule) will lead to an additional result table in the resultTableCollection. Now FirstOrDefault matters because it depends on which table is actually default or first. The order actually is not determinable because it's up to SharePoint search to return which result table first.
Under such condition, using resultTableCollection.FirstOrDefault() is not necessary going to give us the search results from the keywordQuery in the code. It could be the results from the Query Rule.
One way to handle this situation is to stop making assumption that resultTableCollection.FirstOrDefault() always return the results from the SearchExecutor keywordQuery, and try to identify the result table that holds the result you wanted. Sometimes you might want the results from the query rule instead. What you can do is to get all the results tables and check their QueryRuleId property value:
- If the results table is from a QueryRule, then the QueryRuleId property will return the Guid of the QueryRule.
- If the results table is from the SearchExecutor keywordQuery results, then the QueryRuleId property will return an empty Guid; i.e. 00000000-0000-0000-0000-000000000000.