Archive

Search

The search settings for SharePoint sites, site-collections or even for the entire farm can be set through PowerShell. In SP 2010, there were 3 properties which had to be set for a site in order to configure the search box setings: “SRCH_ENH_FTR_URL”, “SRCH_SITE_DROPDOWN_MODE”, “SRCH_TRAGET_RESULTS_PAGE” – for more details, refer to this post. In SharePoint 2013, this has slightly changed, and I have also looked in more depth, to see how the search box works, and how it retrieves its settings.

The search box we see by default in all SharePoint sites is a control registered in the master page by some of the standard SharePoint features. The delegate control id is “SmallSearchInputBox” and three SharePoint features register controls here:

  • ContentLightup, with sequence number 100
  • OSearchBasicFeature, with sequence number 50
  • OSearchEnhancedFeature, with sequence number 25

Considering “SmallSearchInputBox” does not all multiple controls, only the registration with the lowest number gets taken into account. Here is how it looks like:


<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Control
 Id="SmallSearchInputBox"
 Sequence="25"
 ControlClass="Microsoft.Office.Server.Search.WebControls.SearchBoxScriptWebPart" ControlAssembly="Microsoft.Office.Server.Search, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
 <Property Name="UseSharedSettings">true</Property>
 <Property Name="RenderTemplateId">~sitecollection/_catalogs/masterpage/Display Templates/Search/Control_SearchBox_Compact.js</Property>
 <Property Name="EmitStyleReference">false</Property>
 <Property Name="ServerInitialRender">true</Property>
 <Property Name="TryInplaceQuery">false</Property>
 <Property Name="QueryGroupNamesJson">[&quot;MasterPage&quot;]</Property>
 <Property Name="ChromeType">None</Property>
 <Property Name="ID">SmallSearchInputBox1</Property>
 </Control>
</Elements>

The search box is rendered by a web part called SearchBoxScriptWebPart , which has several properties that can be changed – i.e. the render template uses a script file “Control_SearchBox_Compact.js”, you can change the property to point to your custom file. In order to change the default way the search box is rendered, or maybe use a different search box control, you would have to register a new delegate control with a lower sequence than 25.

But how does the default web part work, and how can we configure it? The web part takes it settings from a settings object: SharedSearchBoxSettings . This configuration can be set at four different levels:

  • Web: It looks for a property of the current web with key “SRCH_SB_SET_WEB”
  • Site-Collection: It looks for a property of the root web with key “SRCH_SB_SET_SITE”
  • Tenant: it looks for a site subscription property with key “SRCH_SB_SET”
  • Search Service Application: it looks in the search service application for the SharedSearchBoxSettings property.

All these properties contain a serialized string of a SharedSearchBoxSettings object: e.g. “{"Inherit":false,"ResultsPageAddress":"http://spdevel.portal.com/search","ShowNavigation":false}“. In a previous post, I have explained how to use PowerShell to update these web properties. The search box web part starts looking for the settings at 1st level, then goes to 2nd level and so on until it finds a configuration. When it finds a configuration, it will not look at the next levels. Because of this, you can overwrite the search box settings at any level.

The search box results address Url can be set at the following levels:

  • SharedSearchBoxSettings: from ResultsPageAddress property
  • Web: from web property “SRCH_ENH_FTR_URL_WEB”
  • Site collection: from root web property “SRCH_ENH_FTR_URL_SITE”
  • Search Service Application: from Search Center Url property

A simple configuration scenario would be to set up a search center for all sites & site-collections in the farm, and to disable the search drop down scopes. In this scenario, you would also have a centralized place for your search settings.. You only need to set the search box settings at the Service Application level, and leave all sites/site-collections to inherit this configuration:


$ssa = Get-SPEnterpriseSearchServiceApplication
#replace {SearchCenterUrl} with the Url of your search center
#settings ShowNavigation to false will not display the search scopes anymore.
$ssa.SharedSearchBoxSettings = "{'Inherit':false,'ResultsPageAddress':'http://{SearchCenterUrl}/pages/results.aspx','ShowNavigation':false}"
$ssa.Update()

Advertisements

Search has been greatly enhanced in SharePoint 2013, and with this, it has also brought some changes in how managed properties are created, and the scenarios where they are supported.

First of all, it should be noted that managed properties are created automatically only for site columns (documentation here). If you are ever wondering why you don’t see a managed property for your column, this should be the first thing you should check. A scenario like this would be if you add a column directly to your list, instead of first creating it as a site column and only after that adding it to your list.

Crawled properties and managed properties are now getting automatically created once the content is crawled. There are a set of rules after which these properties are generated. Full details and explanation about this can be found here . I have also found a blog post that describes how this has evolved and changed from SharePoint 2010.

Things get interesting once you have columns of more complex type, not just text or numbers. I’ll explain this for a simple HTML column (multiple lines of text field with full html support enabled, or publishing HTML). In this case, you should have crawled properties for raw text of this column, but also for the html content of the column. For this scenario, after a full crawl, you should get something like:

  • Crawled property ows_r_MTXT_SiteColumnName mapped to managed property SiteColumnNameOWSMTXT (this contains HTML)
  • Crawled property ows_SiteColumnName unmapped to any managed property (this contains column value as text)

The same logic applies to Url columns, User columns, Lookup columns etc. Depending on what you are trying to achieve, you might use the crawled property with column value as text, or the crawled property that contains the unformatted column value.

This sounds great so far, right? And it is, except for one really usual scenario! The common way of provisioning site columns in a solution when building a custom application, with custom columns, content types, lists etc. is declarative (xml and CAML schema). There are other ways to do this (server-side API, client-side API, SharePoint UI) but declarative would be the most common way of doing it in 2007 and 2010.

The strange thing that happens when you provision your custom site columns declarative in a farm solution in SharePoint 2013 is that the managed properties don’t get automatically created. It’s not only the managed properties that don’t get automatically created, but the crawled property that contains the unformatted column value isn’t created also. What you get is only a single crawled property for you column – ows_SiteColumnName. It contains only the column value as text, and it doesn’t have a managed property pair. i have found a blog post on this topic that contains a nice summary which shows when the managed properties are created depending on your creation approach.

Even if you don’t plan to use your custom site columns in search in the beginning, maybe in the future you will also want to support this. Therefore, for all scenarios, I would currently drop the declarative provisioning of site columns in SharePoint 2013 and go for server-side/client-side API. You can still use the xml and CAML schema, and take advantage of the SPFieldCollection.AddFieldAsXml method.

The new refinement panel for SharePoint 2013 search is build completely different from its predecessor in 2010, and follows the new pattern of showing data using display templates. This makes it really easy to customize and can be done by anyone with html/js/css knowledge, without any previously required xsl knowledge.

The default display templates for refinement panels (Filter_Default.js, Filter_MultiValue.js) use a method for retrieving the title of a search managed property.

Srch.Refinement.getRefinementTitle(ctx.RefinementControl);

This goes through the following steps to find the title for the refinement:

  • It looks in currently loaded js resource files. If the managed property key is found, the result is returned. I have described here how to use js resource files for display templates in SharePoint 2013.
  • It looks in the Search default resource files – these can be found in {15}/Resources with the name pattern Srch.resources.{culture}.resx
  • if nothing is found, returns the name of the managed property

The managed property key it looks for is in the format “rf_RefinementTitle_{ManagedProperty}”. Therefore, if the managed property it currently displays is “LastModifiedTime” it will look for a resource with key “rf_RefinementTitle_LastModifiedTime”.

Standard title for last managed property "LastModifiedTime"                                                                                       Custom title for the same managed property

Considering these steps, you can localize new managed properties in your search center, but you can also change the titles for the existing ones. You can use the default “CustomStrings.js” resource file for the new managed properties titles, or you can create your own js resource file, but you must make sure that the file is loaded in the page. Some display templates automatically load the default resource file, but otherwise you can add a Content Editor web part to the page to load your resource file.

$includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/MyCustomResourceFile.js");

In many scenarios where you have to build up a complex Information Architecture in SharePoint, you will also have to set the Search Settings for each site or site collection to point to your search center and your custom results page.

Search Settings Page

Search Settings for a site in SharePoint 2013 UI

These settings are stored in the property bag of the site. For SharePoint 2010, this blog post shows the property names, and how they can be set using Powershell. In SharePoint 2013 the search settings have become a little bit more complex, therefore we need a new script to set them. Below you will find two powershell functions: one that sets the search settings for the entire site collection and one just for a specific web.

function SetSearchSettingsSite($siteUrl,$searchCenterUrl,$resultPagesUrl)
{
 $site = Get-SPSite $siteUrl
 $site.AllWebs | ForEach-Object {
 $web = $_
 $web.AllProperties["SRCH_SB_SET_WEB"] = '{"Inherit":false,"ResultsPageAddress":"'+$resultPagesUrl+'","ShowNavigation":false}'
 $web.AllProperties["SRCH_ENH_FTR_URL_WEB"] = $searchCenterUrl
 $web.Update()
 }
}
function SetSearchSettingsWeb($siteUrl,$searchCenterUrl,$resultPagesUrl)
{
 $web = Get-SPWeb $siteUrl
 $web.AllProperties["SRCH_SB_SET_WEB"] = '{"Inherit":false,"ResultsPageAddress":"'+$resultPagesUrl+'","ShowNavigation":false}'
 $web.AllProperties["SRCH_ENH_FTR_URL_WEB"] = $searchCenterUrl
 $web.Update()
}
SetSearchSettingsSite http://spdevel.portal.com/ http://spdevel.portal.com/search http://spdevel.portal.com/search/Pages/Results.aspx

The second part of the search settings has been integrated in a single web property – “SRCH_SB_SET_WEB”, as a representation of a SharedSearchBoxSettings object. This script only sets the search settings at site-level. If you want to configure the search settings at site-collection level or at farm-level, it would be slightly different. At site-collection level, you would need to set the root web property “SRCH_SB_SET_SITE”, and for farm-level, you would need to set the SharedSearchBoxSettings and SearchCenterUrl properties of the Search Service Application. For more details, please see this post.

Display Templates can also use resource files to favor a multilingual user interface for your SharePoint 2013 site. By looking at the default display templates ( e.g. : Item_Picture3Lines.html, Control_List.html etc. ), I have noticed how language files are used.

The first step would be to deploy a resource file to the master page gallery. This should be a JavaScript file which registers a resource dictionary.

$registerResourceDictionary("en-us", 
{"resourceKey1": "resourceValue1",
"resourceKey2": "resourceValue2",
"resourceKey3": "resourceValue3"});

The resource file should be deployed to the master page gallery of the site in the Language files folder. Here is an example for a display template used in content search web parts : “_catalogs/masterpage/Display Templates/Language Files/en-US/LocalizedFile_Strings.js” . You have to deploy such a file for each culture you plan to support.

From your display template, you would have to reference the above resource file, using

$includeLanguageScript(this.url,"~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/LocalizedFile_Strings.js");

In order to reference a key from your file, you can use :

var localizedResource = $resource("resourceKey1");

That;s it! Now you have localized your display template.

In SharePoint 2010 it was possible to obtain reports from web analytics programmatically using Microsoft.Office.Server.WebAnalytics.Reporting.AnalyticsReportFunction class.However, in SharePoint 2013 the Web Analytics Service Application was merged into the Search Service Application. It seems now that the old assemblies used for this (Microsoft.Office.Server.WebAnalytics.dll and Microsoft.Office.Server.WebAnalytics.UI.dll) are not available any more.

In SP 2013 UI, the reports are available at these locations, as “Usage Reports”

  • CA->Application Management->Manage Service Applications->Search Service Application->Usage Reports
  • Site Settings -> Site Administarion ->Popularity Trends -> View Usage Reports

These reports can now be obtained programmatically using SearchServiceApplication class, from Microsoft.Office.Server.Search.dll. There are two methods available for these:

Although there are more reports that can be obtained from the SSA, i will only explain these two options.

Search analytics data is kept in a SQL database – AnalyticsReportingStore. GetSearchReport is based on SQL stored procedure ar_procGetTopSearchReport and GetRollupAnalyticsItemData is based on ar_procGetAnalyticsItemData.

GetSearchReport has the following parameters:

  • reportType – int : type of report, e.g. 1 is for top search terms (top queries)
  • tenantId – guid : id of the SharePoint tenant. I haven’t been able to figure this one out yet, but without a specific setting for this in the SharePoint farm installation, it would be an Empty Guid.
  • siteId – guid : Site Collection id. Empty guid for all site collections.
  • reportDate – DateTime : The date of the report.
  • bDaily – bool : true brings the result for that day, false for the entire month
  • maxRows – uint : maximum number of results.

Here is a sample, which brings the most searched terms for the Search Service Application in a specific month.


SPSecurity.RunWithElevatedPrivileges(delegate
 {

// You can use SPContext.Current.Site.ID if you have HttpContext

using (var site = new SPSite(siteId))
     {
        var context = SPServiceContext.GetContext(site);
        var searchProxy = context.GetDefaultProxy(typeof(SearchServiceApplicationProxy)) as SearchServiceApplicationProxy;
        var topQueries = searchProxy.GetSearchReport(1, Guid.Empty, Guid.Empty, startDate, false, maxRows);
        foreach (var query in topQueries)
          {
               //process top search term
           }
     }
  });

GetRollupAnalyticsItemData has the following parameters:

  • eventType – int : type of event, e.g. 1 for Site Usage Reports
  • tenantId – guid : same as for GetSearchReport
  • siteId – guid : site collection Id
  • scopeId – guid : sub-site id, empty guid for the entire site collection

The following code retrieves the daily and monthly usage reports of Hits and Users count:

   

SPSecurity.RunWithElevatedPrivileges(delegate
 {
    // You can use SPContext.Current.Site.ID if you have HttpContext
    using (var site = new SPSite(siteId))
       {
         var context = SPServiceContext.GetContext(site);
         var searchProxy = context.GetDefaultProxy(typeof(SearchServiceApplicationProxy)) as       SearchServiceApplicationProxy;
         var usageData= searchProxy.GetRollupAnalyticsItemData(1,Guid.Empty,site.ID,Guid.Empty);

usageData.GetHitCountForDay(date);
         usageData.GetHitCountForMonth(date);
        }
 });

Stay tuned for other posts on Search Analytics!