Update Search Result Web Part: Query Text and Result Source

I had to automate update of result source and query of a search result web part in a search page as part of deployment script to move changes from DEV to TEST, UAT and PROD.

The XML definition of a search result web part is

<webParts>
<webPart xmlns=”http://schemas.microsoft.com/WebPart/v3″&gt;
<metaData>
<type name=”Microsoft.Office.Server.Search.WebControls.ResultScriptWebPart, Microsoft.Office.Server.Search, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” />
<importErrorMessage>Cannot import this Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name=”MaxPagesBeforeCurrent” type=”int”>4</property>
<property name=”ShowBestBets” type=”bool”>True</property>
<property name=”Height” type=”string” />
<property name=”AdvancedSearchPageAddress” type=”string”>advanced.aspx</property>
<property name=”AllowZoneChange” type=”bool”>True</property>
<property name=”UseSharedDataProvider” type=”bool”>False</property>
<property name=”ShowPreferencesLink” type=”bool”>True</property>
<property name=”EmptyMessage” type=”string” />
<property name=”ScrollToTopOnRedraw” type=”bool”>True</property>
<property name=”ShowDefinitions” type=”bool”>True</property>
<property name=”SelectedPropertiesJson” type=”string”>null</property>
<property name=”ShowViewDuplicates” type=”bool”>False</property>
<property name=”CatalogIconImageUrl” type=”string” />
<property name=”RepositionLanguageDropDown” type=”bool”>True</property>
<property name=”PreloadedItemTemplateIdsJson” type=”string”>[“~sitecollection/_catalogs/masterpage/Display Templates/Search/Group_Default.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Site.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Word.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_PowerPoint.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Person_CompactHorizontal.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_BestBet.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_WebPage.js”]</property>
<property name=”ResultsPerPage” type=”int”>10</property>
<property name=”EmitStyleReference” type=”bool”>True</property>
<property name=”ShowPaging” type=”bool”>True</property>
<property name=”ResultTypeId” type=”string” />
<property name=”ShowUpScopeMessage” type=”bool”>False</property>
<property name=”AllowMinimize” type=”bool”>True</property>
<property name=”AllowClose” type=”bool”>True</property>
<property name=”HelpMode” type=”helpmode”>Modeless</property>
<property name=”Title” type=”string”>Search Results</property>
<property name=”ShowResults” type=”bool”>True</property>
<property name=”MaxPagesAfterCurrent” type=”int”>1</property>
<property name=”ShowResultCount” type=”bool”>True</property>
<property name=”Hidden” type=”bool”>False</property>
<property name=”ItemTemplateId” type=”string” />
<property name=”ItemBodyTemplateId” type=”string”>~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_CommonItem_Body.js</property>
<property name=”UseSimplifiedQueryBuilder” type=”bool”>False</property>
<property name=”HitHighlightedPropertiesJson” type=”string”>[“Title”,”Path”,”Author”,”SectionNames”,”SiteDescription”]</property>
<property name=”AvailableSortsJson” type=”string”>[{“name”:”Relevance”,”sorts”:[]},{“name”:”Date(Newest)”,”sorts”:[{“p”:”Write”,”d”:1}]},{“name”:”Date(Oldest)”,”sorts”:[{“p”:”Write”,”d”:0}]},{“name”:”Lifetime Views”,”sorts”:[{“p”:”ViewsLifeTime”,”d”:1}]},{“name”:”Recent Views”,”sorts”:[{“p”:”ViewsRecent”,”d”:1}]}]</property>
<property name=”DataProviderJSON” type=”string”>{“QueryGroupName”:”Default”,”QueryPropertiesTemplateUrl”:”sitesearch://webroot”,”IgnoreQueryPropertiesTemplateUrl”:false,”SourceID”:null,”SourceName”:null,”SourceLevel”:null,”CollapseSpecification”:””,”QueryTemplate”:”{searchboxquery}”,”FallbackSort”:null,”FallbackSortJson”:”null”,”RankRules”:null,”RankRulesJson”:”null”,”AsynchronousResultRetrieval”:false,”SendContentBeforeQuery”:true,”BatchClientQuery”:true,”FallbackLanguage”:-1,”FallbackRankingModelID”:””,”EnableStemming”:true,”EnablePhonetic”:false,”EnableNicknames”:false,”EnableInterleaving”:true,”EnableQueryRules”:true,”EnableOrderingHitHighlightedProperty”:false,”HitHighlightedMultivaluePropertyLimit”:-1,”IgnoreContextualScope”:false,”ScopeResultsToCurrentSite”:false,”TrimDuplicates”:true,”Properties”:{},”PropertiesJson”:”{}”,”ClientType”:”AllResultsQuery”,”UpdateAjaxNavigate”:true,”SummaryLength”:180,”DesiredSnippetLength”:90,”PersonalizedQuery”:false,”FallbackRefinementFilters”:null,”IgnoreStaleServerQuery”:true,”RenderTemplateId”:””,”AlternateErrorMessage”:null,”Title”:””}</property>
<property name=”ChromeState” type=”chromestate”>Normal</property>
<property name=”AllowHide” type=”bool”>True</property>
<property name=”AllowEdit” type=”bool”>True</property>
<property name=”StatesJson” type=”string”>{}</property>
<property name=”RenderTemplateId” type=”string” />
<property name=”ShowPersonalFavorites” type=”bool”>True</property>
<property name=”ShowSortOptions” type=”bool”>False</property>
<property name=”ChromeType” type=”chrometype”>None</property>
<property name=”AllowConnect” type=”bool”>True</property>
<property name=”HelpUrl” type=”string” />
<property name=”ShowLanguageOptions” type=”bool”>True</property>
<property name=”Description” type=”string”>Displays the search results and the properties associated with them.</property>
<property name=”ServerIncludeScriptsJson” type=”string”>null</property>
<property name=”TitleUrl” type=”string” />
<property name=”AlternateErrorMessage” type=”string” null=”true” />
<property name=”ShowAlertMe” type=”bool”>True</property>
<property name=”ShowDidYouMean” type=”bool”>True</property>
<property name=”QueryGroupName” type=”string”>Default</property>
<property name=”Width” type=”string” />
<property name=”ShowAdvancedLink” type=”bool”>True</property>
<property name=”ExportMode” type=”exportmode”>All</property>
<property name=”Direction” type=”direction”>NotSet</property>
<property name=”BypassResultTypes” type=”bool”>False</property>
<property name=”GroupTemplateId” type=”string” />
<property name=”TitleIconImageUrl” type=”string” />
<property name=”MissingAssembly” type=”string”>Cannot import this Web Part.</property>
</properties>
</data>
</webPart>
</webParts>

 

The webpart property DataProviderJSON needs to be updated. The property is  in Json format which is converted to JSON using PowerShell ConvertFrom-Json method. The PowerShell ConvertTo-Json method is used to convert back to JSON before updating the “DataProviderJSON ” property.

The properties SourceId and SourceName refer to the result source.

On SharePoint On Premises environment , the SourceId can be found from navigating to central administration>Manage Service Applications>Search Service application>Result Sources.editresultsource

The format of the URL is http:///_admin/search/EditResultSourceSSA.aspx?level=service&appid={c21b4115-4e78-4d93-a4e7-24a9472c9b19}&sourceid=886883b4%2Db44c%2D4870%2Da0ff%2D1c0f81b2d9c8

In SharePoint Online, navigate to SharePoint Admin>Search>Manage Result sources and click on the result source you wish to find Id. The URL format is

https://-admin.sharepoint.com/_layouts/15/searchadmin/EditResultSource.aspx?level=tenant&sourceid=203fba36-2763-4060-9931-911ac8c0583b&view=1

Extract and decode the sourceid parameter from the url.

To update the query text, the QueryTemplate  needs to be changed. In the example below it is set to value “contentclass:STS_Web contentclass:STS_Site -SPSiteURL:personal {searchboxquery}” which means returns all sites or web based on the search criteria.

Referring to my post “Add Refiners to Refinement web part“, I had to write a script in PowerShell using CSOM SharePoint assemblies to update the result web part property DataProviderJSON.


function GetWebPartByTitle()
{
param(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[Microsoft.SharePoint.Client.WebParts.WebPartDefinitionCollection] $webParts,
[Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
[string] $webPartTitle
)
if($webparts.Count -gt 0){
Write-Host "Looping through all webparts"
foreach($wp in $webparts){
$ctx.Load($wp.WebPart.Properties)
$ctx.ExecuteQuery()
$propValues = $wp.WebPart.Properties.FieldValues;
if($propValues["Title"] -eq $webPartTitle)
{
return $wp;
}
}
}
return $null;
}
#return webpart based on title passed
#return webpart based on title passed
function UpdateSearchResultsDataProviderWebPart
{
param(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[Microsoft.SharePoint.Client.ClientContext]$ctx,
[Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
[string]$pageUrl
)
$web = $ctx.Web;
$page = $web.GetFileByServerRelativeUrl($pageUrl);
$ctx.Load($web);
$ctx.Load($page);
$ctx.ExecuteQuery();
#Check if page already checked out. If so, undo check out
if($page.CheckOutType -eq [Microsoft.SharePoint.Client.CheckOutType]::Online){
try{
Write-Host "Undo Checkout"
$page.UndoCheckOut()
$ctx.load($page)
$ctx.ExecuteQuery()
}
catch{
write-host "Error in checkout.. $($_.Exception.Message)" -foregroundcolor red
}
}
#If page is not checked out, then check out
if($page.CheckOutType -eq [Microsoft.SharePoint.Client.CheckOutType]::None){
Write-Host "Checkout"
$page.CheckOut()
$ctx.Load($page)
$ctx.ExecuteQuery()
}
# Find the Search Results web part
$lwpm =$page.GetLimitedWebPartManager([Microsoft.SharePoint.Client.WebParts.PersonalizationScope]::Shared);
$webParts = $lwpm.WebParts;
$ctx.Load($webParts);
$ctx.ExecuteQuery();
$rwp = GetWebPartByTitle -webParts $webParts -webPartTitle 'Search Results'
$dataProviderjson = $rwp.WebPart.Properties["DataProviderJSON"] | ConvertFrom-Json
$dataProviderjson.SourceId = "886883b4-b44c-4870%2Da0ff-1c0f81b2d9c8"
$dataProviderjson.SourceName = "Local SharePoint Results"
$dataProviderjson.QueryTemplate = "contentclass:STS_Web contentclass:STS_Site -SPSiteURL:personal {searchboxquery}"
$rwp.WebPart.Properties["DataProviderJSON"] = ConvertTo-Json $dataProviderjson -Compress
# Save the changes
$rwp.SaveWebPartChanges();
$page.CheckIn('Changed search result web part', [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
$page.Publish('Changed search result web part');
$ctx.load($page);
$ctx.ExecuteQuery();
}

searchresultsquerytextselectaquery

Use the following script to call function UpdateSearchResultsDataProviderWebPart .

$ctx=New-Object Microsoft.SharePoint.Client.ClientContext($SiteCollectionUrl);
$ctx.Credentials = New-Object System.Net.NetworkCredential($Username, $password);
$rootWeb = $ctx.Web
$ctx.Load($rootWeb)
$ctx.ExecuteQuery();
UpdateSearchResultsDataProviderWebPart -ctx $ctx -pageUrl "/Search/Pages/SiteDirectory.aspx"