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:

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.

Advertisements

19 comments

  • Do you know how to get the social distance element into a ranking model? You see this in the OOTB MS ranking models that are used for people searches. The schema definition does not seem to support it though.

  • “Note: in SharePoint 2010 all search web parts can be extended. Even the People Core Results Web Part is no longer sealed!”

    I highly disagree, the People Core Results Web Part is most definitely (and unfortunately) sealed.

  • Great example! Can this project be extended to mimic People Core Result web-part. I failed to find any information information about subject.

    • Glad you liked it. If you are just looking at using the People Core Results ranking, you could select the MainPeopleModel. There are some things that probably won’t work that well, like the Social Distance relevance (although I haven’t tested this). And you would also have to put some effort in the XSLT to mimic the rendering.

      Hope this helps,

      Yuri

  • First off, thank you for your hard work with this. I’ve tried to deploy in a couple environments. After deploying I’m able to search on all my custom firleds that we have in UPS, however, the results do not come over looking nicely formatted. I figured it’d just be a matter of modifying the xslt, but when ever I try to edit the web part it returns a 403. I’m sure it’s something dumb I’m missing (after searching through SOOOO many other solutions and finally finding yours) but I’m hitting a wall. Any thoughts?

    Thanks again

    • Sorry to hear you run into issues. Does the 403 report any substatus code (.xx)?

      • Hello

        Thanks for this post, this is exactly what I’m looking for ! Unfortunately, I’m running into the same issue as spomaski : 403 whenever I try to edit my webpart. Does one of you have any solution ?

      • Looks like the line on which I get the 403 error is :
        Ranking ranking = new Ranking(searchApplication);

      • Checked the SPS logs, I get this:
        dka2 High SearchServiceApplicationProxy::GetSearchServiceApplicationInfo–Id: Elapsed Time: 31.259 Proxy Name/ID: Application de service de recherche/38174f49-ee1f-49b4-ba8d-765b921ae333 EndPoint: http://cgdlyovm0045:32843/f08d5c4da0bf4021b5646d7d1701f6a7/SearchService.svc a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Server Search Administration dk3w Verbose SearchService.GetService: Getting the farm the server is joined to. a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Server Search Administration dk3y Verbose SearchService.GetService: Getting the search service from the farm. a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Foundation Topology 88b9 Verbose Determining if the current user is a SharePoint Farm Administrator a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Foundation Topology fh2j Verbose Farm Admin check disabled for this process. a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Server Search Administration dmg7 Medium The user does not have permission to perform this operation. a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Foundation General 0000 High UnauthorizedAccessException for the request. 403 Forbidden will be returned. Error=Une exception de type ‘System.Web.HttpUnhandledException’ a été levée. à System.Web.UI.Page.HandleError(Exception e) à System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) à System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) à System.Web.UI.Page.ProcessRequest() à System.Web.UI.Page.ProcessRequest(HttpContext context) à ASP.SPSTD2_ASPX__1766047539.ProcessRequest(HttpContext context) dans c:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\12bdf5f1\a2323c29\App_Web_spstd2.aspx_-1766047539.to3yc59w.0.cs:ligne 0 à System.Web.HttpApplication…. a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44* w3wp.exe (0x0724) 0x25E8 SharePoint Foundation General 0000 High …CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() à System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Foundation General 8nca Verbose Application error when access /SiteCollectionDocuments/TestRech.aspx, Error=Tentative d’exécution d’une opération non autorisée. à Microsoft.Office.Server.Search.Query.UserVerification.ThrowIfNotSearchAdmin(SearchServiceApplication searchApp) à Microsoft.Office.Server.Search.Administration.Ranking.Initialize(SearchServiceApplication searchApplication, Boolean fQueryMode) à Cegid.Recherche.WPSearchCustomEditorPart.CreateChildControls() à System.Web.UI.Control.EnsureChildControls() à Cegid.Recherche.WPSearchCustomEditorPart.SyncChanges() à Microsoft.SharePoint.WebPartPages.ToolPane.OnSelectedWebPartChanged(Object sender, WebPartEventArgs e) à System.Web.UI.WebControls.WebParts.WebPartEventHandler.Invoke(Object sender, WebPartEventArgs e) à Microsoft.SharePoint.W… a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44* w3wp.exe (0x0724) 0x25E8 SharePoint Foundation General 8nca Verbose …ebPartPages.SPWebPartManager.BeginWebPartEditing(WebPart webPart) à Microsoft.SharePoint.WebPartPages.SPWebPartManager.ShowToolPaneIfNecessary() à Microsoft.SharePoint.WebPartPages.SPWebPartManager.OnPageInitComplete(Object sender, EventArgs e) à System.EventHandler.Invoke(Object sender, EventArgs e) à System.Web.UI.Page.OnInitComplete(EventArgs e) à System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb
        02/20/2013 11:41:06.44 w3wp.exe (0x0724) 0x25E8 SharePoint Foundation Monitoring nasq Verbose Entering monitored scope (EndRequestHandler SharePointEndRequest) a35c1ba2-495b-4a3b-94a7-25eeab7fe4fb

        I’m completely stuck, do I need some particular security settings that I missed ?

      • It seems that the user is getting this Forbidden error while accessing the Search Administration endpoint. Can you try to wrap them in a “SPSecurity.RunWithElevatedPrivileges(delegate() {});” ? I will post an updated version, probably later today.

        Y.

      • Hello

        Many thanks for your quick reply ! I have tried the runwithelevatedprivileges option but it did not change the result. Here is my code in case I missed something in it:
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
        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(searchApplicationID);

        Ranking myranking = null;
        myranking = new Ranking(searchApplication);
        });

        I checked that I get the ID of the SearchServiceApplication, and the error is raised at the last line of this code : if I comment the last line, the new Ranking() call, the editor part is displayed.

        There are some messages in the logs above that I don’t understand : “Farm Admin check disabled for this process” particularly.

        Thanks again for your help, I really appreciate it !

      • I also tried to add the application pool account to the administrators of my search service application : no luck.
        It seems that for some reason I can’t access some part of the search schema but I don’t understand why, as I’m connected with the farm admin account, the app pool runs under an account who is admin of the search service app, etc…

      • Last but not least : when I add all domain users to the search service application administrators, it works. So the problem seems to be there. Now, I need to find if there is some service account, app pool account or whatever that I must add there for all this to work ! 🙂 Progressing…

      • Last post for today : it seems that I need to add each user account who will use my page to the search service application administrators. Thought the app pool account was used. Do you know another way ? Many thanks !

  • If you create a custom RankingModel – can you use that for changing the weights of results in your web service call (Search.asmx) ? Best I can tell, you can only apply these custom RankingModels to the web parts?

    • Unfortunately you are right, there is no way to pass in the desired RankingModel with your web service query.

      • Booo. So, what options do you have for increasing the relevancy of a web site crawled by 2010 SP? Say, for instance, it’s way more important to have the term in the path or Title or H1?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s