Wednesday, April 8, 2009

Customize sharepoint CoreResultsWebPart to apply Audience filtering

The below code is to customize the CoreResultsWebPart (Sharepoint OOB web part) to filter based on audience using Reflection. To do so, we need to derive the web part from CoreResultsWebPart class and override the SetPropertiesOnHiddenObject method.

CoreResultsWebPart class internally uses the hiddenobject class to set all the inputs to generate the Search result result. On overriding the method, we can change the input query.

To learn more about sharepoint search queries and Mananaged property Click here

For Example. to filter based on a key word "Sharepoint", MOSS generates a SQL query like this
SELECT WorkId,Path,Title,Write,Author,HitHighlightedSummary, HitHighlightedProperties,CollapsingStatus FROM Scope() WHERE FREETEXT(defaultproperties, 'SharePoint') ORDER BY Rank Desc

To apply audience, query will become
SELECT WorkId,Path,Title,Write,Author,HitHighlightedSummary, HitHighlightedProperties,CollapsingStatusFROM Scope()WHERE FREETEXT(defaultproperties, 'SharePoint') AND CONTAINS(Audience,'TestAud' ) ORDER BY Rank Desc

Audience property added in the where clause of the query is a managed property which is created using Crawled property. Once added the Audience filter in SQL query, the Search result output is automatically generated based on Audience and key word.

Sample code for audience filtering in MOSS Search Result

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.WebControls;
using Microsoft.SharePoint.Administration;
using Microsoft.Office.Server;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Search;
using System.Reflection;

namespace customCoreResultsWebPart
{
public class customCoreResultsWebPart : Microsoft.Office.Server.Search.WebControls.CoreResultsWebPart
{
protected override void SetPropertiesOnHiddenObject()
{
base.SetPropertiesOnHiddenObject();
try
{
Type coreResultsWebPartType = this.GetType();

// get the private field containing the searchResultsHiddenObject srho
FieldInfo searchResultsHiddenObjectField = coreResultsWebPartType.BaseType.GetField("srho", BindingFlags.NonPublic BindingFlags.Instance);

// make sure the field exists
if (searchResultsHiddenObjectField != null)
{
// get the actual internal srho object attached to CoreResultsWebPart
object searchResultsHiddenObject = searchResultsHiddenObjectField.GetValue(this);

// get the type of the srho
Type searchResultsHiddenObjecType = searchResultsHiddenObject.GetType();

// get the keyword query property
PropertyInfo keywordQueryProperty = searchResultsHiddenObjecType.GetProperty("KeywordQuery", BindingFlags.Instance BindingFlags.Public);

// read what the user searched for
string keywordQuery = (string)keywordQueryProperty.GetValue(searchResultsHiddenObject, null);

// use this method to get the keywords as a query string to append to the search
// this doesn't seem to be working yet

MethodInfo keywordQueryMethod = searchResultsHiddenObjecType.GetMethod("GetKeywordsAsQuery", BindingFlags.Instance BindingFlags.NonPublic);
string keywordsAsQuery = (string)keywordQueryMethod.Invoke(searchResultsHiddenObject, null);

// set the keywordProperty to null so we can change it to a fullTextQuery
keywordQueryProperty.SetValue(searchResultsHiddenObject, null, null);

// create a new query and set it
string fullTextQueryString = null;

// get the fullTextQuery field i.e. ADVANCED SEARCH
PropertyInfo fullTextQueryProperty = searchResultsHiddenObjecType.GetProperty("FullTextQuery", BindingFlags.Instance BindingFlags.Public);

// This scenario is called when User used AADVANCED SEARCH
if (fullTextQueryProperty.GetValue(searchResultsHiddenObject, null) != null)
{
// This is in the form of SQL Query
fullTextQueryString = fullTextQueryProperty.GetValue(searchResultsHiddenObject, null).ToString();
}
// For normal Search Scenario, The keyword query is translated into SQL Query
else if (!string.IsNullOrEmpty(keywordQuery))
{
// This scenario, the value is just the Keyword... so modify to a SQL Query
fullTextQueryString = "SELECT WorkId,Path,Title,Write,Author,HitHighlightedSummary, HitHighlightedProperties,CollapsingStatus FROM Scope()WHERE FREETEXT(defaultproperties, '" + keywordQuery + "')";
}
// Not a Valid Search and hence Return
else
{
return;
}
// APPLY AUDIENCE FILTERING
fullTextQueryString = fullTextQueryString + " AND CONTAINS (AUDIENCE,'TestAud')";

// SET the SQL Query to the full Text Query - SET THE HIDDEN OBJECT TO NEW VALUE WITH AUDIENCE FILTER
fullTextQueryProperty.SetValue(searchResultsHiddenObject, fullTextQueryString, null);

// this field needs to be set to true to use a full text query
FieldInfo fullTextQuerySetField = searchResultsHiddenObjecType.GetField("_IsFullTextQuerySetFromForm", BindingFlags.NonPublic BindingFlags.Instance);
fullTextQuerySetField.SetValue(searchResultsHiddenObject, true);

// tell the srho that it is not a keyword query any more
FieldInfo isKeywordQueryField = searchResultsHiddenObjecType.GetField("m_bIsKeywordQuery", BindingFlags.NonPublic BindingFlags.Instance);
isKeywordQueryField.SetValue(searchResultsHiddenObject, false);
}
}
catch (Exception exp)
{
// To do: Exception Handling
}
}
}
}

4 comments:

EdreX said...

Wonderful tutorial. Thank you so much. I needed to use my custom advanced search webpart with various filters and additional components and show the results in the results page with OOTB core results webpart, paging WP and search statistics WP. Setting the hidden search object does the trick.
Keep the good tricks flowing. Thanks!

Anonymous said...

Really, really gooood!!! Thank you so much!!! Solved my problem.

jose said...

It's not working for me the example, when I put the "Audience" column in the return fields or in the filter I'm always getting the same error:

"Property doesn't exist or is used in a manner inconsistent with schema settings"

Thanks for advance!

MS Experts said...

To use the audience targetting feature in SharePoint, the SharePoint web application should be connected to the User Profile Service application.