In this blog I thought I’d show how to use some of the cool new features in SharePoint 2010 with some of our old favorites from SharePoint 2007 to create an interesting search results experience. We’re going to do all of this WITHOUT WRITING ANY CODE! Even though Steve is a developer, he empathizes with those that don’t have the time or resources to do knob twisting in the object model. Good Steve, good boy…<woof!> A very common feature request with SharePoint 2007 was to let users rate content. Well in SharePoint 2010 there is a new feature called Ratings that does exactly this. With the Ratings feature activated, you can enable support for it in individual document libraries. When it’s turned on, users will see an interface like this:
As you can see in the Rating column, as you hover over the ratings stars they light up, and then you just click to select your rating and record your vote. This is pretty cool and a great new feature to have in the box. Now, what we want to do demonstrate in this blog is integrating this feature with search so we can use these ratings in our queries and search results.
It’s worth pointing out that I did all of this in a site collection based on the publishing site template, which does not activate the Ratings feature by default. To start using it just activate the feature like you would any other – i.e. stsadm –o activatefeature –name Ratings –url http://myPublishingSite (just typed that off the top of my head, accuracy is not guaranteed but should be close enough for you to figure out). Also, to implement the search integration I deleted the search subweb that came out of the box with the publishing site because it was only a simple search site. I created a new subweb based on the Enterprise Search Center site template. You’ll want to make sure all of these pieces are in place if you are going to try and follow along step by step with this blog.
One other thing to note: if you are clicking on stars to rate items and wondering why the rating isn’t immediately updated – it’s because there is a timer job that runs once an hour by default. It’s responsible for taking all of the ratings that have been received since the last time it ran and aggregating them into the totals and average rating values. You can manually run the job, which is the User Profile Service Application - Social Rating Synchronization Job, to speed things up and get your values calculated. NOTE: “User Profile Service Application” is the display name of my User Profile service application. Just plug in the name of your User Profile service application to find the job in the list of timer jobs.
So, to get things going here, the first thing we need to do is create a managed property, just like we did in SharePoint 2007. Start in Central Admin, select Manage Service Applications, click on the Search Service Application, and then click on the Manage button in the ribbon. That brings up the search dashboard. Now click on the Metadata Properties link in the left pane. When that screen comes up, click on the New Managed Property button near the top of the page.
We’re going to call our new managed property “Rating”, and the data type is decimal – that’s the format in which the Ratings feature stores the average rating for items. Click on the Add Mapping button in the middle of the page and it brings up a picker dialog in which you can select the crawled property that should be mapped to this managed property. It automatically filters the list to only show crawled properties that are of the same data type as the data type you selected (decimal) for this managed property. In the technical preview the crawled property to which you want to map has kind of a funky name, so I don’t know if it will change in subsequent releases. For now though it is called ows_Rating_x0020__x0028_0_x002d_5_x0029_. Select that property and click the OK button, then scroll to the bottom of the new managed property page and click the OK button there to save your new managed property.
Just like in SharePoint 2007, you now need to do a full crawl of the index to capture your new managed property. Go ahead and kick that off, then go grab a Coke and relax for a few minutes. I’ll wait here and you let me know when it’s done. <whistling…picking at finger nails…playing a game of solitaire…checking out nba.com…>
Okay, all done? Good, now we have the data we need to work with. The first thing we’re going to do is to modify the properties in the Advanced Search page. Edit the page, then modify the properties of the Advanced Search Box web part. Expand the Properties section in the web part properties panel, then edit the value in the Properties edit field. There are various ways to do this; I prefer to use CTRL-A to select it all, then copy, then paste into an empty Xml file in Visual Studio .NET.
We need to do two things to modify the Xml so we can use the Rating managed property in our queries. The first thing we need to do is add a new PropertyDef element in the PropertyDefs section of the document. This new element looks like this:
<PropertyDef Name="Rating" DataType="decimal" DisplayName="Rating"/>
That defines the property, now we need to add it to a Result Type. Adding it to a result type gets our property to show up in the property drop down list when a user selects that result type. What the heck does all that mean? Well I’ll show you what to paste in, and then I’ll start showing pictures rather than trying to explain it. So in this case I want the Rating property to be a searchable property in the All Results result type because it’s the default result type. So I simply add a reference to the PropertyDef I created above by adding this with the other PropertyRef elements in the All Results result type:
If you’ve been following step by step then at this point your Xml should look like this (NOTE: this is not the whole file, it’s only showing the parts I’ve modified):
Okay, now we need to copy our entire chunk of Xml and paste it back into the Properties property for our web part. Do that and then click the OK button to save your changes. Optionally you can also check in the page so others can see the results. Now when a user uses advanced search, not only can they enter all of their keywords and other criteria, they can also say, for example, only show me documents that have been rated higher than 3.5 (or whatever number you want). Here’s a picture of the Advanced Search page after we’ve made the Xml changes above. Note that the Result type that is selected is All Results, which is where we added the reference to our Rating property.
Pretty cool, yeah? Okay, well I think it is and hopefully you do too. Now let’s take the next and final customization step for this blog. So we got these ratings. And we got these stars images. Hmmm, what can we do with that? Hey, why don’t we incorporate that into our search results? Here’s what we’re going to do. We’re going to modify the core results search part. We’re going to configure it to retrieve the Rating property for each item, and then we’re going to modify the XSLT used to render the search results to show the rating for each item, using stars. Since stars don’t really have a good way to represent more than a coarse decimal amount, we’ll also add a tooltip so you can hover over the stars and see the exact rating for an item. I like where this is going, let’s get started.
Go to your search home page and run any search to quickly get to the results.aspx page. Edit the page and open the properties for the Search Core Results web part (I’m assuming you can figure out how to do this without my assistance). Expand the Display Properties section; if the Use Location Visualization checkbox is selected, then unselect it. That allows you to edit the Fetched Properties property. Go ahead and select it all and then paste it into your favorite Xml editor. All we want to do here is add a new Rating element, since that’s the name of our managed property. The new element should look like this:
Now copy all of your Xml and past it back into the Fetched Properties property; click the Apply button to save your changes. The next part is slightly more complicated, only because most of us humans don’t spend a lot of time writing XSLT. I’m going to show you what changes I made and briefly talk through it at a high level, but I’m not going to try and teach you XSLT in this little blog entry. Hopefully what doesn’t make sense from an XSLT perspective, you can find out more about from your buddy Bing.
To get started, click on the XSL Editor button. I edit all of this in a blank Xml file in Visual Studio.NET as well, but you can obviously use whatever editor makes you happy. So copy all of this XSL and paste it into your favorite editor. In this case I want the rating to show up right after the snippet that describes the item. To do that I’m going to look for a div element called srch-Metadata2. You will find a chunk of XSL that looks something like this:
I want to insert my rating stuff below the div element and above that first xsl:call-template element. What I want to do first is check to see if the rating property is greater than zero. If it isn’t then I know the item hasn’t been rated at all; technically you can rate an item 0 but as a practical matter it’s virtually impossible to get the click off right, at least in the tech preview build. Since XSL doesn’t have an if…elseIf kind of syntax we need to use the xsl:choose function. So our first choice will be to see if the Rating is greater than 0; if it is then we’ll figure out how many stars to throw in there – if it isn’t then we’re just going to add some text that says “No Rating”.
This will probably make more sense with a picture, so I’ll show you the first chunk of XSL now and then explain the rest of the logic behind it:
<xsl:when test="rating > 0">
<xsl:with-param name="starCount" select="rating"/>
<xsl:if test="round(rating) > rating">
Remember that one of the things we wanted was to have the exact rating display as a tooltip. The way I’m doing that is with a span element. It has a title attribute, and when you hover over a span the title attribute is displayed as a tooltip. So I add a span element, and via the magical mystery way of XSL I set the title attribute for it equal to the value from the Rating managed property.
Next I need to draw out my stars. To do that, I’m going to use an XSL template; I will show the details behind it next. In the XSL above though I’m just calling my template, and I’m passing to it the value from the Rating managed property. Finally, if the decimal portion of the Rating value is over .49 I want to show a half-filled star. So I check to see if the rounded value of my rating is greater than the rating itself. For example, if my rating is 3.4 then 3.4 rounded is 3, which isn’t greater than 3.4. However, if my rating is 3.5, the rounded value is 4, which is greater than 3.5. So in that case I’m going to add my half filled star. The final xsl:otherwise is the case where there is no rating for an item, so I’m just going to show the “No Rating” message.
Now, let’s look at the XSL template where we actually draw the stars out, and then walk through it.
<xsl:param name="value" select="1"/>
<xsl:if test="$value <= $starCount">
<xsl:with-param name="starCount" select="$starCount"/>
<xsl:with-param name="value" select="$value + 1"/>
It’s actually pretty short, and the main complexity is again a result of the awkwardness of XSLT. It is using two parameters – one is the value from the Rating managed property, and another is a counter like you would use in a for x=0 to 5 kind of loop. XSL doesn’t have support for a for…next type loop though so we have to fake it, which we do here.
So we have our parameters and look to see if our counter number is still less than or equal to the number of stars we need to draw. If it is then we just add a new image of a filled star. Note that the image I’m using is the one that comes with the Ratings feature, and the path shown above is the same one it uses in the document library when displaying the ratings. So I add my image for the star and then I call my template recursively. Note that I increment my counter by 1 each time, which is what the “$value + 1” means in the template above.
Now the XSL is in place so we can go ahead and copy it all and paste it back into the XSL Editor in the web part properties panel. Do that and click the Save button to save your changes, then click the OK button to save the web part property changes. You can optionally also check in the page at this point. You need to run the query again to see the changes in your web part, but here’s an example:
You can see the item at top isn’t rated and so it says “No Rating”. The other items all have been rated and so they show stars for their rated value. If you look at the juice bar.docx file too you can see the tooltip displaying the rating for that document, which is 3.655.
I think this is some pretty slick functionality and hopefully you will as well. One other very cool implementation of this would be to create a refinement that uses the rating value so you can just look at all your search results and use the refinement panel to show only documents that are rated higher than 4 stars for example. Unfortunately modify the refinement panel is not working in the build I currently have, but once that gets going I will probably add another blog that shows an example for doing that.
Hope you found this useful, it was actually pretty fun putting it together and I think there’s some real value in what it does. It’s also another great example of marrying together documents, people, rating and search in SharePoint 2010. These are great features and I’m sure you’ll discover lots of other ways to take advantage of them.