Quantcast
Viewing all articles
Browse latest Browse all 53

Core Results Web Part with configurable Ranking Model

Ranking Models are cool. If you don’t know what they can do for you, here’s a summary:

SharePoint 2010 Enterprise Search uses ranking models to determine how data is weighed and results are ranked. Out of the box SharePoint 2010 provides different models, but you can also create your own. Creating your own ranking model allows you to tailor the results your query returns by:

  • assigning different weights to metadata properties.
  • include hits on custom managed properties in general search (as opposed to searching them through advanced search exclusively).

There are several nice blogs out there that describe in detail how you can add your own custom ranking model. For instance this excellent blog post, which describes the process quite clear: http://calvisblog.wordpress.com/2010/06/21/custom-ranking-models-with-sharepoint-2010-background-value-and-administrative-overview/ (by Shaun O’Callaghan).

Applying your custom ranking model requires you to look up the GUID, export the standard OOB Core Results Web Part, modifying a web part property and uploading it again (see this previous post). Now, that works of course, but I wanted a true end-user solution where you can configure the ranking model through the web part properties. Something like this:

Image may be NSFW.
Clik here to view.
EditorPart

Extending the Core Results Web Part

Note: we need to develop a SharePoint Farm Solution, because the Microsoft.SharePoint.WebPartPages.DataFormWebPart class (referenced
by the CoreResultsWebPart) is not available in sandbox solutions.

To start, we create a new web part deriving from the Core Results Web Part. The web part includes a property that allows setting the RankingModelID. It also inherits from IWebEditable needed for implementing an EditorPart (see further down).

public class EnhancedCoreResults : CoreResultsWebPart, IWebEditable
{
     [WebBrowsable(false)]
     [Personalizable(PersonalizationScope.Shared)]
     public string rankingModelID { get; set; }


 }

I want the web part properties to show the available Ranking Models so the user can select them from a dropdown list. For this to work, we cannot use regular properties and have to implement an EditorPart. The reason for this, is that regular properties don’t allow you to dynamically load values through code behind and I don’t want to hardcode the values for the dropdown list.

Next, create the EditorPart:

The EditorPart needs to get the names and values of the available ranking models. For this to work, we need to connect to our Search Service Application and fetch the available models:

class EnhancedCoreResultsEditorPart:EditorPart
    {
        private DropDownList rankingModelList = new DropDownList(); 

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            RankingModelCollection rankingModels = null;

            SPServiceContext context = SPServiceContext.GetContext(SPContext.Current.Site);
            SearchServiceApplicationProxy searchApplicationProxy = context.GetDefaultProxy(typeof(SearchServiceApplicationProxy)) as SearchServiceApplicationProxy;
            SearchServiceApplicationInfo searchApplictionInfo = searchApplicationProxy.GetSearchServiceApplicationInfo();
            Guid searchApplicationID = searchApplictionInfo.SearchServiceApplicationId;
            SearchServiceApplication searchApplication = SearchService.Service.SearchApplications.GetValue<SearchServiceApplication>(searchApplicationID);

            Ranking ranking = new Ranking(searchApplication);
            rankingModels = ranking.RankingModels;

            foreach (RankingModel rankingModel in rankingModels)
            {
                ListItem item = new ListItem();
                item.Text = rankingModel.Name;
                item.Value = rankingModel.ID.ToString();
                rankingModelList.Items.Add(item);
            }

            this.Controls.Add(new LiteralControl("<div class=\"UserSectionHead\">Ranking Model</div>"));
            this.Controls.Add(new LiteralControl("<div class=\"UserSectionBody\">"));
            this.Controls.Add(rankingModelList);
            this.Controls.Add(new LiteralControl("<br/><br/></div>"));

            this.ChildControlsCreated = true;
        }
}

The EditorPart sets the web part RankingModelID using the value from our dropdown list.

     public override bool ApplyChanges()
        {
            EnsureChildControls();

            EnhancedCoreResults enhancedCoreResultsWebPart = (EnhancedCoreResults)this.WebPartToEdit;
            if (enhancedCoreResultsWebPart != null)
            {
                enhancedCoreResultsWebPart.rankingModelID = rankingModelList.SelectedValue; 
            }
            else
            {
                return false;
            }
            return true;
        }

        public override void SyncChanges()
        {
            EnsureChildControls();

            EnhancedCoreResults enhancedCoreResultsWebPart = (EnhancedCoreResults)this.WebPartToEdit;
            if (enhancedCoreResultsWebPart != null)
            {
                rankingModelList.SelectedValue = enhancedCoreResultsWebPart.rankingModelID;
            }
        }

Wire up the EditorPart in our custom Core Results class:

        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            List<EditorPart> editors = new List<EditorPart>();
            EnhancedCoreResultsEditorPart editorPart = new EnhancedCoreResultsEditorPart();
            editorPart.ID = "EnhancedCoreResults_editorPart";
            editors.Add(editorPart);
            return new EditorPartCollection(editors);
        }

        object IWebEditable.WebBrowsableObject
        {
            get { return this; }
        }

Create the logic that actually sets the RankingModelID by overriding the ConfigureDataSourceProperties() method:

     protected override void ConfigureDataSourceProperties()
        {
            if (this.ShowSearchResults)
            {
                base.ConfigureDataSourceProperties();

                if (!string.IsNullOrEmpty(rankingModelID))
                {
                    CoreResultsDatasource dataSource = this.DataSource as CoreResultsDatasource;
                    dataSource.RankingModelID = rankingModelID;
                }
            }

        }

Note: I found several articles on the web showing similar functionality by overriding the GetXPathNavigator() method and setting the ranking model through the SharedQueryManager instance. Somehow I couldn’t get this to work the way I wanted to so I decided to do it differently.

That’s it, build, package and deploy.

You can download the Visual Studio solution here.


Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 53

Trending Articles