Search Engine Optimization in SharePoint 2013 – SEO Properties

The web content management infrastructure in SharePoint 2013 includes a number of significant improvements targeted at search engine optimization for publishing sites. Major features such as cross-site publishing and managed navigation have been definitely getting a lot of attention but there are also smaller and less known features that can also be very useful.

Page SEO Properties

The Page content type in SharePoint 2013 has a number of fields dedicated to search engine optimization. You can populate these fields by selecting the Edit SEO Properties menu item in the SharePoint ribbon while editing a page.
SharePoint 2013 Edit SEO Properties ribbon menu item

On the Edit SEO Properties page, you can set the following field values:

  • Name – the page name to appear in search results. Defines the “canonical” url of the page. (Note: for term-driven pages, this maps to the Friendly Url Segment term property)
  • Title – the page title to appear in search results. Defines the HTML title tag value of the page. (Note: for term-driven pages, this maps to the Navigation Node Title term property)
  • Browser Title – if set, overrides the browser page title and HTML title tag value above.
  • Meta Description – short summary of page content. Search engines may display this in search results. Defines the “description” meta tag content of the page.
  • Meta Keywords – keywords that describe the content of the page. Defines the “keywords” meta tag content of the page.
  • Exclude from Internet Search Engines – indicates to search engines if this page content should be indexed or not. If the page is to be excluded, adds a noindex robots meta tag to the page.

SharePoint Cross-Site Publishing and Search Engine Optimization

The SEO Properties above work great for standard publishing pages but what if you are using cross-site publishing to display content on the publishing site? It turns out that you can also control the Browser Title, Meta Description and Meta Keywords tag content through search. The Catalog-Item Reuse web part that is typically used to display information on catalog item pages will use the following managed property values to generate meta tags for the page:

  • SeoBrowserTitleOWSTEXT – value will be used to populated the <title> tag
  • SeoKeywordsOWSTEXT – will populate the “keywords” meta tag
  • SeoDescriptionOWSTEXT – will set the “description” meta tag content

Basically, all you need to do is to map the crawled properties associated with your site columns to the managed properties above, run a full crawl and the meta tag will magically appear on your catalog item pages!

References

Canonicalization: https://support.google.com/webmasters/answer/139066?hl=en
Site title and description: https://support.google.com/webmasters/answer/35624?hl=en
Using meta tags to block access to your site: https://support.google.com/webmasters/answer/93710?hl=en

Business Connectivity Services, External Content Types and Content By Search in SharePoint 2013 – Part 2

In this blog post I’ll show how to surface data from external systems in SharePoint 2013 using Managed Navigation and Content By Search web parts. For instructions on how to crawl external systems using BCS, External Content Types and SharePoint Search, refer to my previous blog post: Business Connectivity Services, External Content Types and Content By Search in SharePoint 2013 – Part 1.

Managed Properties

In order for us to be able to use different Product and ProductModel external content type fields, we need to create a number of managed properties. For this example, the following managed properties need to be created:

  1. Navigate to Central Administration > Manage service applications > Search Service Application
  2. Click the Search Schema link in the Queries and Results side navigation section
  3. Click New Managed Property to create a new managed property for each of the items below
  4. ProductModelSummary
            • Name: ProductModelSummary
            • Type: Text
            • Searchable: True
            • Retrievable: True
            • Safe: True
            • Crawled property mapping: vProductModelCatalogDescriptionRead
              ListElement.Summary
  5. ProductModel
    • Name: ProductModel
    • Type: Text
    • Searchable: True
    • Queryable: True
    • Retrievable: True
    • Safe: True
    • Crawled property mapping: vProductAndDescriptionRead
      ListElement.ProductModel
  6. ProductDescription
    • Name: ProductDescription
    • Type: Text
    • Searchable: True
    • Retrievable: True
    • Safe: True
    • Crawled property mapping: vProductAndDescriptionRead
      ListElement.Description
  7. CultureID
    • Name: CultureID
    • Type: Text
    • Queryable: True
    • Safe: True
    • Crawled property mapping: vProductAndDescriptionRead
      ListElement.CultureID
  8. ProductID
    • Name: ProductID
    • Type: Integer
    • Queryable: True
    • Safe: True
    • Crawled property mapping: vProductAndDescriptionRead
      ListElement.ProductID
  9. Click the Content Sources link in the Crawling side navigation section
  10. Start Full Crawl for the AdventureWorks2012 content source

Result Sources

The next step is to create two new result sources on the publishing site that we can use later to configure content search web parts.

  1. On the publishing site, navigate to Site Settings > Search Result Sources
  2. Click New Result Source to create each of the result sources below
  3. Product
    • Name: Product
    • Query Transform: {searchTerms} contentsource:AdventureWorks2012 entityname:Product cultureid:en
  4. ProductModel
    • Name: ProductModel
    • Query Transform: {searchTerms} contentsource:AdventureWorks2012 entityname:ProductModel

Site Navigation

Now let’s confirm that managed navigation is enabled and configured on the SharePoint site. It is enabled for new publishing sites by default in SharePoint 2013.

  1. Navigate to Site Settings > Look and Feel > Navigation
  2. Make sure that Managed Navigation is selected for both Global Navigation and Current Navigation

Pages

We’ll need to create 3 new pages on the site – one top-level page listing all product models, one page that will list all products for a product model, and one page to display product details.

The first page has to be created by using the Site Actions > Add a page option so that SharePoint automatically creates and configures the navigation term.

  1. Create a new page called Products by going to Site Actions > Add a page
  2. Navigate to the Pages document library on the site
  3. Create a new page called Product by using the New Document option in the ribbon
  4. Create a new page called Product-Model by using the New Document option in the ribbon

Managed Navigation

Now is the time to configure the managed navigation to use the pages created earlier.

  1. Navigate to Site SettingsSite Administration > Term store management
  2. Expand the Site Collection node
  3. Expand the Site Navigation node
  4. Select the Products term
  5. Select the Term-Driven Pages tab
  6. Change target page for children of this term
  7. Change Catalog Item Page for this category and Change Catalog Item Page for children of this category to use the Product.aspx page
  8. Press Save to commit the changes
  9. Add a child term to the Products navigation term for each of the product model. No settings need to be customized for the child terms.
    • Mountain-100
    • Mountain-500
    • Road-150
    • Road-450
    • Touring-1000
    • Touring-2000

The navigation term set should now looks similar to this:
SiteNavigation

Content By Search

The final steps is to add and configure content search web parts to the pages we created earlier.

  1. Click the Products link in the global navigation to navigate to the Products.aspx page
  2. Edit the page and add a Content Search web part from the Content Rollup category
  3. Edit web part properties
  4. Press Change Query to bring up the Query Builder user interface
    1. On the Basics tab, switch to Advanced Mode, select ProductModel result source in the dropdown and clear the Query text
    2. Press OK to close the query builder
  5. Change the Number of items to show to 6
  6. In the Display Templates section, select Two lines as the Item display template
  7. In the Property Mappings section, select ProductModelSummary as Line 2
  8. Press OK to apply changes and save the page

The Products page should now look like this:
Products

Next, click one of the links on the page to navigate to the product model page.

  1. Edit Product-Model.aspx page
  2. Add a Content Search web part from the Content Rollup category
  3. Edit web part properties
  4. Press Change Query to bring up the Query Builder user interface
    1. On the Basics tab, switch to Advanced Mode, select Product result source in the dropdown
    2. Set Query text to productmodel:{Term.Name}
    3. Press OK to close the query builder
  5. Change the Number of items to show to 10
  6. In the Display Templates section, select Two lines as the Item display template
  7. Press OK to apply changes and save the page

Your Product Model page should now look similar to this screenshot:
Product-Model

Now follow one of the links on the page to navigate to the product detail page.

  1. Edit Product.aspx page
  2. Add Catalog-Item Reuse web part from the Search-Driven Content category
  3. Edit web part properties
  4. Press Change Query to bring up the Query Builder user interface
    1. On the Basics tab, switch to Advanced Mode, select Product result source in the dropdown
    2. Set Query text to productid:{URLToken.1}
    3. Press OK to close the query builder
  5. In the Property Mappings section, select ProductDescription managed property
  6. Press OK to apply changes and save the page

Finally, the Product page should look like this:
Product

Business Connectivity Services, External Content Types and Content By Search in SharePoint 2013 – Part 1

SharePoint 2013 makes it very easy to index data from external systems using Business Connectivity Services (BCS) and then to surface that data in SharePoint by taking advantage of the new Content By Search web part and Managed Navigation. In this blog post you’ll find step-by-step instructions on how to create External Content Types optimized for search and index data from external systems. My next blog post will build on top of that and will show how to configure Managed Navigation and Content By Search web parts to retrieve and display the external system data on a SharePoint site.

External System

In this example I’ll be using a copy of the AdventureWorks 2012 database from Codeplex. You can download the SQL Server 2012 OLTP version of the database here: http://msftdbprodsamples.codeplex.com/

External Content Types

We’ll need to create two external content types based on the database entities – Product and ProductModel.

  1. Launch Microsoft SharePoint Designer 2013.
  2. Open the SharePoint site where would you like to create the External Content Types.
  3. Select External Content Types in the left Site Objects pane and press the New External Content Type button in the ribbon.
  4. Set the Name to Product.
  5. Click the link next to the External System to bring up the Operation Designer.
  6. Press Add Connection, select SQL Server and configure the Connection Properties.
  7. In the Data Source Explorer, expand AdventureWorks2012 > Views.
  8. Generate the New Read Item Operation and New Read List Operation for vProductAndDescription view by right-clicking it. Map the ProductID column to Identifier.
  9. Save changes.
  10. Navigate back to the External Content Types screen and click the Product name to bring the content type back up.
  11. In the Fields section, select the Name field and press Set as Title in the ribbon. This will ensure that the Name field will appear as the title of the record in search results.
  12. Save changes.
  13. Repeat the steps above to create the ProductModel external content type. Use the vProductModelCatalogDescription view, ProductModelID as identifier and set the Name field as title.

Once all of the steps above are complete you’ll need to configure some additional settings in Central Administration. First we need to configure permissions.

  1. Open Central Administration.
  2. Navigate to Manage service applications and select the Business Data Connectivity Service Application then press Manage in the ribbon or simply click the service application name.
  3. Grant your search default content access account permissions to the metadata store or individual objects by using the Set Object Permissions and Set Metadata Store Permissions in the ribbon.

Now let’s add default actions to the ProductModel and Product external content types. The default action

  1. Click the ProductModel external content type.
  2. Press the Add Action button in the ribbon.
  3. Set the URL to something like http://www.contoso.com/products/{0} – this is going to be the location to the product rollup page on the publishing site.
  4. Press Add Parameter and select the Name field.
  5. Check the Default action checkbox and press OK.
  6. Repeat the steps above for the Product external content type but use http://www.contoso.com/products/{0}/{1} as the url, ProductModel field as the first parameter and ProductID field as the second parameter.

Search

At this point we are almost done and are ready to crawl and index the data.

  1. Navigate to the Search Service Application in Central Administration.
  2. Click Content Sources link in the left navigation section under Crawling.
  3. Click New Content Source.
  4. Set the Name to AdventureWorks2012.
  5. Select Line of Business Data as the Content Source Type.
  6. Select the Business Data Connectivity Service Application in the dropdown.
  7. Select the Crawl selected external data source option and check the checkbox next to AdventureWorks2012.
  8. Press OK.
  9. Start Full Crawl for the newly added AdventureWorks2012 content source.

When the crawl is done, navigate to the Search Center site and run a search query for contentsource:AdventureWorks2012. You should now be getting search results back. In my next blog post I’ll show how to surface these search results on the SharePoint site using Managed Navigation and Content By Search web parts.

Search-Driven Twitter Bootstrap Carousel in SharePoint 2013

Content Search web part introduced in SharePoint 2013 is a powerful tool that lets you easily retrieve and customize the appearance of search results without ever writing a single line of server-side code. In this post I’ll show how to display items from a SharePoint list in a Twitter Bootstrap Carousel on any page of your site using a Content Search web part and a custom control and item display templates.

  1. First thing we need to do is to create a SharePoint list that will contain the items to be displayed in the carousel. Let’s start by creating the site columns below.
    1. Navigate to Site Settings > Site columns > Create
    2. CarouselBody site column is going to hold the text to be displayed within each carousel slide.
      1. Name: CarouselBody
      2. Type: Full HTML content with formatting and constraints for publishing
      3. Group: Contoso
      4. Require that this column contains information: Yes
    3. CarouselImage site column will contain the image to be displayed as the slide background.
      1. Name: CarouselImage
      2. Type: Image with formatting and constraints for publishing
      3. Group: Contoso
      4. Require that this column contains information: Yes
    4. At this point the site columns should look something like this:
  2. Next, we need to create a content type
    1. Navigate to Site Settings > Site content types > Create
    2. The Carousel content type will group the site columns together
      1. Name: Carousel
      2. Parent Content Type: Item
      3. Group: Contoso
    3. Add the site columns
      1. Add from existing site columns
      2. Select columns from: Contoso
      3. Columns to add: CarouselBody, CarouselImage
    4. The content type now should look similar to this:
  3. Now it’s time to create the SharePoint list.
    1. Site Contents > add an app > Custom List
      1. Name: Carousel
    2. Enable content type management for the list.
      1. List Settings > Advanced settings
      2. Allow management of content types?: Yes
    3. Add the Carousel content type to the list content types
      1. List Settings > Content Types > Add from existing site content types
      2. Group: Contoso
      3. Content types to add: Carousel
    4. Remove the default Item content type from the list
      1. List Settings > Content Types > Item > Delete this content type
    5. The list settings should now be as below:
  4. Finally, we are ready to add some carousel items to the list.
  5.  Once the items are added, let’s go ahead and run a full crawl. The managed properties for each column in the list will be created automatically.
    1. Central Administration > Application Management > Manage service applications
    2. Search Service Application > Content Sources > Local SharePoint Sites > Start Full Crawl
  6. When the crawl is complete we can verify that all of the information we need is in the search index by running a REST API search query.
    1. http://intranet.contoso.com/_api/search/query?querytext=’contenttype:carousel’&selectproperties=’Title,CarouselBodyOWSHTML,CarouselImageOWSIMGE’
    2. The results should match the screenshot below:
  7. Before we move on to the next step, we need to download the jQuery library and Twitter Bootstrap Carousel package and upload those to the Site Assets document library.
    1. Download jQuery: http://code.jquery.com/jquery-1.8.2.min.js
    2. Download Bootstrap: http://twitter.github.com/bootstrap/customize.html
      1. Choose components: Carousel
      2. Select jQuery plugins: Carousel
    3. Site Contents > Site Assets
    4. Upload Document: jquery-1.8.2.min.js
    5. Upload Document: bootstrap.min.css
    6. Upload Document: bootstrap.min.js
    7. The contents of the Site Assets document gallery should look something like this:
  8. At this point we have the content we need available and can now start developing the custom display templates. We’ll do that by copying and editing some of the standard display templates that come with SharePoint 2013 and located in the master pages gallery.
    1. Site Settings > Master pages > Display Templates > Content Web Parts
    2. Control_List.html > Download a Copy
    3. Save As: Control_CarouselList.html
    4. Replace the content of the file with the following:
      <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
      
      <head>
      
      <title>Carousel List</title>
      
      <!--[if gte mso 9]><xml>
      
      <mso:CustomDocumentProperties>
      
      <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
      
      <mso:MasterPageDescription msdt:dt="string">This is a Carousel Control Display Template that will list the items.</mso:MasterPageDescription>
      
      <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106601</mso:ContentTypeId>
      
      <mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
      
      <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
      
      </mso:CustomDocumentProperties>
      
      </xml><![endif]-->
      
      </head>
      
      <body>
      
          <!--
      
                  Warning: Do not try to add HTML to this section. Only the contents of the first <div>
      
                  inside the <body> tag will be used while executing Display Template code. Any HTML that
      
                  you add to this section will NOT become part of your Display Template.
      
          -->
      
          <script>
      
              $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
      
      		$includeCSS(this.url, "~sitecollection/SiteAssets/bootstrap.min.css");
      
      		$includeScript(this.url, "~sitecollection/SiteAssets/jquery-1.8.2.min.js");
      
          </script>
      
          <!--
      
              Use the div below to author your Display Template. Here are some things to keep in mind:
      
              * Surround any JavaScript logic as shown below using a "pound underscore" (#_ ... _#) token
      
              inside a comment.
      
              * Use the values assigned to your variables using an "underscore pound equals"
      
              (_#= ... =#_) token.
      
          -->
      
          <div id="Control_CarouselList">
      
      <!--#_
      
      if (!$isNull(ctx.ClientControl) &&
      
          !$isNull(ctx.ClientControl.shouldRenderControl) &&
      
          !ctx.ClientControl.shouldRenderControl())
      
      {
      
          return "";
      
      }
      
      ctx.ListDataJSONGroupsKey = "ResultTables";
      
      var $noResults = Srch.ContentBySearch.getControlTemplateEncodedNoResultsMessage(ctx.ClientControl);
      
      var noResultsClassName = "ms-srch-result-noResults";
      
      var ListRenderRenderWrapper = function(itemRenderResult, inCtx, tpl)
      
      {
      
          var iStr = [];
      
          iStr.push(itemRenderResult);
      
          return iStr.join('');
      
      }
      
      ctx['ItemRenderWrapper'] = ListRenderRenderWrapper;
      
      ctx.OnPostRender = function() {
      
      	$("div.item").first().addClass("active");
      
      	$.getScript(SP.PageContextInfo.get_siteServerRelativeUrl() + "SiteAssets/bootstrap.min.js", function() {
      
      		$(".carousel").carousel();
      
      	});
      
      };
      
      _#-->
      
        <div id="myCarousel" class="carousel slide">
      
      	<div class="carousel-inner">
      
                  _#= ctx.RenderGroups(ctx) =#_
      
      	</div>
      
      	<a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a>
      
      	<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>
      
        </div>
      
      <!--#_
      
      if (ctx.ClientControl.get_shouldShowNoResultMessage())
      
      {
      
      _#-->
      
              <div class="_#= noResultsClassName =#_">_#= $noResults =#_</div>
      
      <!--#_
      
      }
      
      _#-->
      
          </div>
      
      </body>
      
      </html>
      
      
    5. The carousel control display template is ready so we can upload it to the same location in the master pages gallery.
      1. Site Settings > Master pages > Display Templates > Content Web Parts
      2. Upload Document: Control_CarouselList.html
    6. You should see the Control_CarouselList.js file automatically generated by SharePoint 2013:
  9. The next step is to create an item display template for the carousel slides.
    1. Similar to the control display template created earlier, we are going to copy and edit a standard item display template.
      1. Site Settings > Master pages > Display Templates > Content Web Parts
      2. Item_PictureOnTop.html > Download a Copy
      3. Save As: Item_Carousel.html
    2. Replace the file content with the following:
      <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
      <head>
      
      <title>Carousel item</title>
      
      <!--[if gte mso 9]><xml>
      
      <mso:CustomDocumentProperties>
      
      <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
      
      <mso:ManagedPropertyMapping msdt:dt="string">'Image'{Image}:'CarouselImageOWSIMGE','Heading'{Heading}:'Title','Body'{Body}:'CarouselBodyOWSHTML'</mso:ManagedPropertyMapping>
      
      <mso:MasterPageDescription msdt:dt="string">This Item Display Template will show a carousel item.</mso:MasterPageDescription>
      
      <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
      
      <mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
      
      <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
      
      </mso:CustomDocumentProperties>
      
      </xml><![endif]-->
      
      </head>
      
      <body>
      
          <!--
      
                  Warning: Do not try to add HTML to this section. Only the contents of the first <div>
      
                  inside the <body> tag will be used while executing Display Template code. Any HTML that
      
                  you add to this section will NOT become part of your Display Template.
      
          -->
      
          <script>
      
              $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
      
          </script>
      
          <!--
      
              Use the div below to author your Display Template. Here are some things to keep in mind:
      
              * Surround any JavaScript logic as shown below using a "pound underscore" (#_ ... _#) token
      
              inside a comment.
      
              * Use the values assigned to your variables using an "underscore pound equals"
      
              (_#= ... =#_) token.
      
          -->
      
          <div id="Item_Carousel">
      
      <!--#_
      
      var carouselImage = $getItemValue(ctx, "Image");
      
      var carouselHeading = $getItemValue(ctx, "Heading");
      
      var carouselBody = $getItemValue(ctx, "Body");
      
      _#-->
      
      	  <div class="item">
      
      		_#= carouselImage.value =#_
      
      		<div class="carousel-caption">
      
      		  <h4>_#= carouselHeading =#_</h4>
      
      		  <p>_#= carouselBody =#_</p>
      
      		</div>
      
      	  </div>
      
          </div>
      
      </body>
      
      </html>
      
      
    3. Upload the custom item display template to the master pages gallery.
      1. Site Settings > Master pages > Display Templates > Content Web Parts
      2. Upload Document: Item_Carousel.html
    4. Verify that the associated Item_Carousel.js was successfully generated:
  10. At last, everything is in place and we can now add a Content Search web part to the page.
    1. Page > Edit
    2. Insert > Web Part > Content Rollup > Content Search
    3. Configure the web part to fetch carousel items from the search index and use the custom display templates created earlier.
      1. Edit Web Part
      2. Change Query
        1. Select a query: Items matching a content type
        2. Restrict by content type: Carousel
      3. Display Templates
        1. Control: Carousel List
        2. Item: Carousel Item
      4. Appearance
        1. Width: 830px (set to the width of your carousel images)
    4. Page > Save

At this point you should see the Twitter Bootstrap Carousel on the page displaying items from the Carousel SharePoint list.

Quickly view any managed property value in SharePoint 2013 using Search REST API

Search REST API in SharePoint 2013 makes it very easy to quickly check the value stored in the search index for any managed property. For example, let’s say you have a SharePoint list and one of the custom columns in that list is a publishing image field. You need to display the image in search results but are not sure what type of value is stored in the search index. Is it an absolute url to the image? A relative url? Maybe an entire image HTML tag?

A quick way to check exactly what it is by using the Search REST API. First, let’s run a simple Search REST API query and see what managed property values are returned.

http://intranet.contoso.com/_api/search/query?querytext='contenttype:banner'

Since only standard managed properties are included by default, add the selectproperties parameter to the query.

http://intranet.contoso.com/_api/search/query?querytext='contenttype:banner'&selectproperties='BannerImageOWSIMGE'

Similar to querytext and selectproperties, many other KeywordQuery object model properties can be used as parameters for Search REST API calls.

Using Content Enrichment Web Service Callout in SharePoint 2013 Preview

SharePoint 2013 Preview release intoduced a new functionality called content enrichment web service callout. It provides the ability to inspect and manipulate managed property values for each item before it’s added to the search index. Prior to SharePoint 2013, the only way to accomplish something similar was in FAST Search for SharePoint by extending the item processing pipeline. Clients using SharePoint server search were out of luck as the functionality was not available to them.

The process of building and configuring a web service callout is relatively straight forward. These are the high-level steps to follow:

  1. Build a web service by implementing the IContentProcessingEnrichmentService interface. Add logic to manipulate managed property values.
  2. Run PowerShell commands to configure the callout web service endpoint address, input and output managed properties, trigger condition and a few other things.
  3. Execute a full crawl.

In this blog post I’ll show an example of developing a web service that populates a new managed property value which is then used as a refiner on the search results page. Let’s say we have a number of project sites in SharePoint where each site contains information about a specific bike model.

Each bike model belongs to a product category such as Mountain Bikes, Road Bikes and Touring Bikes. We’d like to be able to refine search results by product category but unfortunately that metadata is not available in SharePoint at this point. What we are going to do next is create a new managed property called ProductCategory and build a web service to populate the managed property values based on our custom business logic. The ProductCategory managed property can then be used as a refiner on the search results page.

To create the managed property, navigate to Central Administration > Search Service Application > Search Schema > New Managed Property.

  • Property name: ProductCategory
  • Type: Text
  • Searchable: checked
  • Queryable: checked
  • Retrievable: checked
  • Refinable: Yes – active
  • Token Normalization: checked

In Visual Studio 2012, create the web service: New Project > Visual C# > WCF > WCF Service Application.

Delete the Service1 created by default or rename it to EnrichmentService. Delete the IService1 or IEnrichmentService interface.

Add an assembly reference to C:\Program Files\Microsoft Office Servers\15.0\Search\Applications\External\microsoft.office.server.search.contentprocessingenrichment.dll.

Open EnrichmentService.svc.cs, add the following using statements:

using Microsoft.Office.Server.Search.ContentProcessingEnrichment;
using Microsoft.Office.Server.Search.ContentProcessingEnrichment.PropertyTypes;

Replace the class implementation:

public class EnrichmentService : IContentProcessingEnrichmentService
{
    private Dictionary<string, string> productModels = new Dictionary<string, string>()
    {
        {"mountain-100", "Mountain Bikes"},
        {"mountain-500", "Mountain Bikes"},
        {"road-150", "Road Bikes"},
        {"road-450", "Road Bikes"},
        {"touring-1000", "Touring Bikes"},
        {"touring-2000", "Touring Bikes"}
    };

    public ProcessedItem ProcessItem(Item item)
    {
        ProcessedItem processedItem = new ProcessedItem();
        processedItem.ItemProperties = new List<AbstractProperty>();

        AbstractProperty pathProperty = item.ItemProperties.Where(p => p.Name == "Path").FirstOrDefault();
        if (pathProperty != null)
        {
            Property<string> pathProp = pathProperty as Property<string>;
            if (pathProp != null)
            {
                foreach (var productModel in productModels)
                {
                    if (pathProp.Value.Contains(productModel.Key))
                    {
                        Property<string> modelProp = new Property<string>()
                        {
                            Name = "ProductCategory",
                            Value = productModel.Value
                        };
                        processedItem.ItemProperties.Add(modelProp);
                    }
                }
            }
        }

        return processedItem;
    }
}

Now the web service is ready and the next step is to configure SharePoint to call the web service during the crawl. That is done using PowerShell. To minimize the performance impact of the web service callout, we only want it to be called under a certain condition – this condition is defined in the Trigger property. More information about the syntax can be found in the Trigger expression syntax article on MSDN. The expected input and output managed properties are configured via the InputProperties and OutputProperties. When debugging the web service, the DebugMode property value can be set to $true in which case SharePoint will ignore the InputProperties value and will send all available managed properties for each item to the service. Any managed property values returned by the web service in debug mode are ignored by SharePoint.

$ssa = Get-SPEnterpriseSearchServiceApplication
$config = New-SPEnterpriseSearchContentEnrichmentConfiguration
$config.DebugMode = $false
$config.Endpoint = "http://localhost:64401/EnrichmentService.svc"
$config.FailureMode = "WARNING"
$config.InputProperties = "Path"
$config.OutputProperties = "ProductCategory"
$config.SendRawData = $false
$config.Trigger = 'StartsWith(Path,"http://intranet.contoso.com/adventureworks/models/")'
Set-SPEnterpriseSearchContentEnrichmentConfiguration –SearchApplication $ssa –ContentEnrichmentConfiguration $config

Finally, launch the web EnrichmentService created earlier and start a new full crawl. Once the crawl is complete, the ProductCategory managed property should be populated and searchable:

The final step is to add a Product Category search refiner. Edit the search results page, edit the Refinement web part, click the Choose Refiners… button within the Properties for Search Refinement section, select the ProductCategory managed property in the Available refiners list and press the Add > button. Move the ProductCategory to the top of the Selected refiners list, then scroll down and set the Display name to Product Category and save your changes.

Run a search for “bike” and you should now be able to refine the search results by the product categories:

References: