<Standard Disclaimer: Blog formatting sucks. Do your eyes a favor - download the attachment, which is the Word document version of this.)
UPDATE: a few things have changed for RTM so I am updating this post and adding the artifacts I used to create everything. Okay, a long title for today’s topic but we’re going to cover a lot of good material. SharePoint 2013 has a number of outstanding features that allow you to really use and customize search results like never before. I’m not going to try and give a comprehensive overview of all of them, or even significant details on the components we’re using in this posting because they are capably covered elsewhere. However I will give you a starting set of search results, give a brief overview of each of these features, and then walk through the process of using them to create a very cool and customized search result that demonstrates how all of these pieces work together.
First, let’s start with a view of what the search results look like when I run a query for “sales reports” in my farm (NOTE: this is a beta 2 build and the final release may look differently):
This is the out of the box experience with search results, and it looks good. However, we’ve added these features to help you really enrich the end user searching experience in SharePoint 2013, so let’s add a bit to our scenario here. We have several divisions throughout our company, and a number of them are responsible for managing sales within their division. Each division uses a standard Excel template to report their sales activity over time. In addition each division has one person that is responsible for managing the customer relationships and reporting their sales information. So how can we use these features in SharePoint to help standardize and bring out important information about the division and its customers? We’re going to take a multi-prong approach to solving this.
To begin with, we’re going to create a custom content type for everyone that uploads sales reports into SharePoint. I begin by creating the site columns that I’m going to use – these are the fields that I want to be able to surface in my search sales report. For our scenario I’ve defined the following site columns that we’ll be using:
Site Column Name
Type
Account Manager
Single line of text
Direct Reports
Number
Sales Region
Choice: Northamerica, EMEA, Asia
Top Accounts
Total Accounts
Next I created a content type that includes all of these site columns. I created the content type in the publishing hub site associated with my Managed Metadata Service so that it gets pushed out to all my subscribing sites in the farm, which in my case is all sites in all web applications.
The next step, which was manual in my case, was to add this content type to the list of available content types for each division’s site collection, in the document library where they store sales reports (among other things). Obviously there are ways to automate this, but for brevity’s sake I did it manually. Okay, so our first step is done – we have a custom content type, it’s deployed, and as sales reports are added to these document libraries, the Account Managers can just identify a sales report when they upload it and fill out the metadata.
Now that we have these sales reports populating our sites, we need to create managed properties from the sales report custom site columns. UPDATE: In the example included in the .zip file with this post, I just added site columns and used the managed properties in a single site collection created automatically by SharePoint. There’s nothing really new here in SharePoint 2013 compared to SharePoint 2010 for purposes of this scenario – you need to do a full crawl to create the crawled properties, then create managed properties that are mapped to those crawled properties, then do another full crawl. I say there’s nothing different in this scenario because we are implementing a farm-wide solution. However SharePoint 2013 does have a fabulous feature that lets site collection admins mark their site collection, or site, or even list for a full crawl. This saves you from having to do an enterprise full crawl when you want to do this at a more narrowly scoped level, which is really awesome. I’ll try and cover that feature in another post – I just wanted to point out that it exists, but isn’t applicable in this case because our solution scope is for the whole farm.
At this point we have sales reports that are using a custom content type, and we have managed properties that are populated with the metadata values for those sales reports. The next thing we want to do is to make sure that these items get “noticed” when someone is doing a search for something that should likely include one of these sales reports. SharePoint 2013 has the perfect feature for this and it’s called Query Rules. Query rules have three main components:
Okay, that was the “brief” rundown on what query rules are, so how will we use them here?
UPDATE: this query rule was in beta 2 but did not make it into RTM. You can manually create a rule like it by using an Advanced Text Match and selecting Query contains one of these phrases:, then adding this: analysis;cube;dashboard;dashboards;data;database;report;reports;sales. Check Start of and End Of query matches checkboxes, and uncheck Entire query matches exactly. Select the last radio button to Assign match to {actionTerms}, unmatched items to {subjectTerms}. For the Action, add a result block that issues a query for the {subjectTerms} against the Local Reports and Data Results result source. Create the rule for the Local SharePoint Results result source.
Well it turns out that there is a query rule that ships out of the box that is going to take care of this for us. I actually made the rule inactive when I took the screenshot above so you could understand the impact of it better. In our case, we want queries in which someone might want to see a sales report to have our division sales report pop up and be noticed. So, go into your Search site collection, Site Settings, and click on the Query Rules link. In the Select a source dropdown, click on it and select Local Reports and Data Results; you will see a query rule called Reports and Data. UPDATE: Select the Local SharePoint Results result source if you are creating your own rule as described above. Click on it and select View from the drop down menu to see how the rule is constructed. It works like this:
I re-enabled that query rule, so now this is what the results look like when I do a query for sales reports:
Cool, this is getting better – the query rule has kicked in and now we’re getting our documents based on our custom sales report content type showing up in the top of the results – that’s great! However, we still aren’t showing any of the metadata about the documents and that’s what really brings this all together.
UPDATE: To simplify this process, you should do the following:
When you’re done you have one active query rule that retrieves only items that are based on our custom Sales Report content type.
In SharePoint 2010 if you wanted to render a particular item differently, it was a fairly arduous process of going in and modifying one HUGE chunk of XSLT in the core results web part. You got to practice your awesome XSLT skills and try and find the right place in that enormous document where to insert your custom code. Overall, it was a less than joyful experience.
In SharePoint 2013 – queue the music – we have display templates and what an improvement they are. No longer do you need to carry your XSLT Zen about you, now you can create your custom display code as straight HTML – yahoo! In fact, for this posting I actually used Adobe’s Dreamweaver CS6 to create the “code” for my custom display template. So, how do display templates work?
With a display template, you have a few different things to keep track of:
For our display template, we are going to pull together the attributes from the custom sales report content type we created so that we can have it shown in the search results like this:
Let’s get started – to begin with, you will pretty much always want to start with an existing display template when you are building a new one. If you go into your search center site collection, Site Settings, then click on Master pages and page layouts link. When you get into the library, click on the Display Templates folder, then click on the Search folder. In there you will find all of the display templates that we ship out of the box. You will likely see a number of files that have the same name, but either a .html or .js suffix. All you care about are the .html files – the .js files are generated automatically when you upload an .html file. NOTE: If you do not see the HTML files, then you are probably not working in a search center site. If you want to do this in a non-search site for some reason, enable the Publishing feature in the site collection. In this case, since the display template is for Excel files I started out with the Item_Excel.htm file. I downloaded a copy locally, renamed it SalesReport.html, and then opened it in Dreamweaver.
Next I add my managed properties. There is a tag called mso:ManagedPropertyMapping where you put in the managed properties your display template requires. I just added mine to the end of the list, using the same format as the rest of the managed properties – it looks like this:
UPDATE: Just an FYI – these managed property names assume you create them yourself in the Search Service Application. In the example display template included in the .zip file with this post, it uses the managed property namesyou get when you just add site columns and let SharePoint created the managed properties in a single site collection for you automatically.
<mso:ManagedPropertyMapping msdt:dt="string">'Title':'Title','Author':'Author','Size':'Size','Path':'Path','Description':'Description','LastModifiedTime':'LastModifiedTime','CollapsingStatus':'CollapsingStatus','DocId':'DocId','HitHighlightedSummary':'HitHighlightedSummary','HitHighlightedProperties':'HitHighlightedProperties','FileExtension':'FileExtension','ViewsLifeTime':'ViewsLifeTime','ParentLink':'ParentLink','ViewsRecent':'ViewsRecent','FileType':'FileType','IsContainer':'IsContainer','ServerRedirectedURL':'ServerRedirectedURL','ServerRedirectedEmbedURL':'ServerRedirectedEmbedURL','ServerRedirectedPreviewURL':'ServerRedirectedPreviewURL', 'AccountManager':'AccountManager', 'SalesRegion':'SalesRegion', 'TotalAccounts':'TotalAccounts', 'TopAccounts':'TopAccounts', 'DirectReports':'DirectReports', 'ContentType':'ContentType'</mso:ManagedPropertyMapping>
As you can see, I added AccountManager, SalesRegion, etc. – all of the properties I had created for my custom content type. After that I scrolled down to where it says this:
<div id="_#= $htmlEncode(itemId) =#_" name="Item" data-displaytemplate="ExcelItem" class="ms-srch-item" onmouseover="_#= ctx.CurrentItem.csr_ShowHoverPanelCallback =#_" onmouseout="_#= ctx.CurrentItem.csr_HideHoverPanelCallback =#_">
_#=ctx.RenderBody(ctx)=#_
<div id="_#= $htmlEncode(hoverId) =#_" class="ms-srch-hover-outerContainer"></div>
</div>
And I deleted everything between the outer DIV tags (which I’ve highlighted in red above). Now I can get down to writing my HTML. Before I do that, there are three things you should know before you start:
Okay, now that you understand the mechanisms for creating the HTML, here is what I used to create the display template I showed above; in the head tag I added these style attributes:
<style>
.ReportDiv
{
float:left;
}
.ReportText
font-family: Verdana, Geneva, sans-serif;
font-size: 12px;
.ReportHeading
font-weight: bold;
.LogoImg
margin-top: 15px;
width: 118px;
height: 101px;
.MasterDiv
height: 215px;
width: 342px;
background-repeat: no-repeat;
background-image: url(http://sps/sites/search/PublishingImages/SalesReportBackground.PNG);
padding: 15px;
.DetailsContainer
background-color:#CCC;
</style>
The main thing worth noting here is really just that the image I use for a background in the account manager details is one that I’ve already uploaded to my site; everything else should be fairly self-explanatory. UPDATE: I’ve highlighted these elements in the post to make sure you understand that in order to get this to work in your environment, you need to a) upload the image and b) update the SalesReport.css and SalesReport.html files with the location where you added them. Again, by having this style tag in my <head> section I can see exactly what the layout is going to like while I’m designing it in Dreamweaver.
Next let’s look at the HTML itself that is used to create the display template:
<div>
<div class="ReportDiv">
<img class="LogoImg" src="http://sps/sites/search/PublishingImages/SalesReportLogo.PNG" />
<div class="MasterDiv">
<!-- Title and link to item -->
<a href="_#= ctx.CurrentItem.Path =#_" class="ReportText">_#= ctx.CurrentItem.Title =#_</a>
<br/><br/>
<!-- Account Manager -->
<span class="ReportHeading ReportText">
Account Manager:
</span>
<span class="ReportText">
_#= ctx.CurrentItem.AccountManager =#_
</span><br/>
<!-- Sales Region -->
Sales Region:
_#= ctx.CurrentItem.SalesRegion =#_
<!-- Total Accounts -->
Total Accounts:
_#= ctx.CurrentItem.TotalAccounts =#_
<!-- Top Accounts -->
Top Accounts:
_#= ctx.CurrentItem.TopAccounts =#_
<!-- Direct Reports -->
Direct Reports:
_#= ctx.CurrentItem.DirectReports =#_
<!-- Hit Highlighted Text -->
<br/>
Details:
<span class="DetailsContainer ReportText">
_#= Srch.U.processHHXML(ctx.CurrentItem.HitHighlightedSummary) =#_
I’ll skip talking about the formatting features of this content and just focus on the data. You can see where I’ve plugged in the managed properties throughout, using the _#= and =#_ tags. You’ll see that it is pure token substitution so I can even use it directly in my <a> href attribute above (as opposed to doing this with XSLT, which is far more involved). The rest of the fields are just plugged into the appropriate DIV or SPAN tag for formatting. The one “special” case shown here is for the HitHighlightedSummary, which is using a built in processing component that comes with search. At this time I don’t have a complete list of components and methods and what they do, but I add this one only because if you don’t use it, your summary will not be hit highlighted. So make sure you use this method for that purpose.
Now that I’ve got everything working, there is one other thing I need to do to my markup. I copy everything between my <style> tags and put them in a new CSS document called SalesReport.css. In my master page gallery, in the /Display Templates/Search folder, I created another folder called styles. I uploaded my SalesReport.css file into that directory. To use it now in my display template I have to add a new script tag. It should be below the <body> tag, and above the first <div> tag. I use a SharePoint javascript function called $includeCSS to get my CSS file pulled down for the display template. So my opening markup looks like this:
<body>
<script>
$includeCSS(this.url, "./styles/SalesReport.css");
</script>
<div id="Item_SalesReport">
UPDATE: Once you’ve made these changes, you need to save your display template and upload it to the /Display Templates/Search folder. If you are uploading the SalesReport.html that is included with this post, you MUST be sure that when you check the file in you change the content type from Document to Item Display Template; otherwise it cannot be used.
All right, so we are code complete now, but how do we get our custom display template to be used?
Result Types are the way in which you get display templates invoked, based on a set of rules. The rules are pretty straightforward to use – you can have them fire for a specific pre-defined type of content, like an email, a PDF, a Word document, a SharePoint Wiki, etc. In addition to that you can only also only have them used when the query is for a specific result source. Finally, you can also choose any managed property as criteria for the rule, with any common comparison. For example, you could have a result type rule only fire when the AccountManager field contains “Vice President” if you wanted to have a different display type used for your VPs. In our case, we want our result type to fire only when the content type equals “Sales Report”. So we add a condition for our result type that says when ContentType equals “Sales Report”.
UPDATE: I was hoping this would work for RTM but in fact it does not. You have two options here: 1) you can change the equality condition to “Contains” instead of “Equals”. This is because the content type string actually ends up being a long string, the last part of which is “Sales Report” in our case. Alternatively, if you only wanted it used with a single query rule, then in the query rule properties you can select the display template to be used fordisplaying items.
As you can see we could even add multiple values for the match. We can also add multiple properties, that get AND’d together. For this scenario ContentType is all we need. Once we’ve defined the conditions, we can configure the Action for the rule. For a result type, the Action is just a drop down with all of the available display templates. All I need to do is to pick my Sales Report display template from the dropdown, and click the Save button to create my result type. Again, if you’re not sure how to create result types then take a look at all of the result types that come out of the box in SharePoint. You can pick up lots of good ideas for new result types by looking at some of the existing ones.
Now we have all the pieces in place – a custom content type that captures the metadata we want, some managed properties to extract it out and put it in the index, a query rule that ensures our sales report content pops up to the top of the list when someone queries for “sales report”, a custom display template that shows off the metadata we captured in a really unique and useful format that doesn’t look remotely close to a typical search result, and a result type that makes sure our display template is used when our custom content type is returned in search results. The final results when we’re finished now look like this:
That concludes this introduction to some of the very cool features in SharePoint 2013 for presenting search results. Hopefully you have a feel for the power in these features and can find many useful and interesting ways to use them.