Share via


SharePoint 2013: Configure Search using Powershell (Part 2 of 2)

In the first article , we saw how to create a search service application in SharePoint 2013 using PowerShell. In this article we will see how to configure search 

Get the reference of the search service application and proxy

  $searchApp = Get-SPEnterpriseSearchServiceApplication $SearchServiceAppName$searchAppProxy = Get-SPEnterpriseSearchServiceApplicationProxy $SearchServiceAppName

Remove the web application URL from the default content source

function RemoveWebAppUrlFromLocalSPSites {
    $localSitesContentSource = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $searchApp -Identity "Local SharePoint Sites" -ErrorAction SilentlyContinue
    if (-not $localSitesContentSource) {
        Write-Host "Search Content Source 'Local SharePoint Sites' application not found"
        return
    }
    else {
        Write-Host "Checking if the current '$WebAppUrl' isn't part of the 'Local SharePoint Sites' Content Source"
        if ($localSitesContentSource.StartAddresses.Exists($WebAppUrl)) {
            Write-Host "Removing '$WebAppUrl' from the 'Local SharePoint Sites' Content Source"
            $localSitesContentSource.StartAddresses.Remove($WebAppUrl)
            $localSitesContentSource.Update()
        }
    }
}

RemoveWebAppUrlFromLocalSPSites

Create the content source and configure the incremental and full crawl

function CreateContentSource {
    param ($Name)
    
    $created = $false
    Write-Host "Check if '$Name' Content Source exists"
    $contentSource = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $searchApp -Identity $Name -ErrorAction SilentlyContinue
    if (-not $contentSource) {
        Write-Host "Creating '$Name' Content Source"
        $contentSource = New-SPEnterpriseSearchCrawlContentSource -SearchApplication $searchApp -Name $Name -Type SharePoint -StartAddresses $SiteUrl -SharePointCrawlBehavior CrawlSites
        $created = $true
    }
    
    Write-Host 'Configure incremental search'
    $context=[Microsoft.Office.Server.Search.Administration.SearchContext]::GetContext($SearchServiceAppName)
    $incremental=New-Object Microsoft.Office.Server.Search.Administration.DailySchedule($context)
    $incremental.StartHour="7";
    $incremental.DaysInterval="1";
    $incremental.RepeatInterval="15";
    $incremental.RepeatDuration="1080";

    Write-Host 'Configure full crawl'
    $fullCrawl=New-Object Microsoft.Office.Server.Search.Administration.DailySchedule($context)
    $fullCrawl.StartHour="4";
    $fullCrawl.DaysInterval="1";

    $contentSource.IncrementalCrawlSchedule=$incremental
    $contentSource.FullCrawlSchedule=$fullCrawl
    $contentSource.Update()    
    
    if ($created) { 
        # if the search was just created first do a full crawl to ensure managed properties are avaible
        Write-Host "Starting full crawl for content source $($contentSource.Name)..."
        $contentSource.StartFullCrawl()
        do { 
            Start-Sleep -Seconds 2 
            Write-Host '.' -NoNewLine
        }
        while ($contentSource.CrawlStatus -ne "Idle")
        write-host "`n"
    } 
}

CreateContentSource -Name 'TestContentSource'

Create the custom managed property from the crawled property

function ConfigureMetadataProperty {
    param ($Name, $MetadataPropertyType)

    Write-Host "Check if '$Name' managed property exists - if not, create and configure"
    $managedProp = Get-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $searchApp -Identity $Name -ErrorAction SilentlyContinue
    if (-not $managedProp) {
        Write-Host "Creating '$Name' managed property"
        $managedProp = New-SPEnterpriseSearchMetadataManagedProperty -Name $Name -SearchApplication $searchApp -Type $MetadataPropertyType | Out-Null
        # get the managedProp again, it sometimes is NULL after New-SPEnterpriseSearchMetadataManagedProperty
        $managedProp = Get-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $searchApp -Identity $Name 
    } 
    
    Write-Host "Configure'$Name' managed property"
    $managedProp.MaxCharactersInPropertyStoreIndex = "2048000"
    $managedProp.MaxCharactersInPropertyStoreForRetrieval = "2048000"
       $managedProp.EnabledForScoping = $true
    $managedProp.Update()
    return $managedProp
}

function CreateManagedProperty {
    param ($Name, $CrawledProperty, $MetadataPropertyType)

    $managedProp = ConfigureMetadataProperty -Name $Name -MetadataPropertyType $MetadataPropertyType

    $managedMapping = Get-SPEnterpriseSearchMetadataMapping -SearchApplication $searchapp -ManagedProperty $managedProp
    if (-not $managedMapping -or $managedMapping .CrawledPropertyName -ne $CrawledProperty) {
        Write-Host "Creating mapping for '$Name' managed property"
        $category = Get-SPEnterpriseSearchMetadataCategory -Identity SharePoint -SearchApplication $searchapp 
        $crawledContenttypeProperty = Get-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $searchapp -Category $category -Name $CrawledProperty 
        New-SPEnterpriseSearchMetadataMapping -SearchApplication $searchapp -ManagedProperty $managedProp -CrawledProperty $crawledContenttypeProperty | Out-Null
    }
}

CreateManagedProperty -Name 'CustomManagedProperty' -CrawledProperty 'CrawledProperty' -MetadataPropertyType 1 

Update the existing managed property to refer to the crawled property

function UpdateManagedProperty {
    param ($Name, $CrawledProperty,$SearchApp)
    Write-Host "Fetch the managed metadata mapping for $Name"
    $managedProperty = Get-SPEnterpriseSearchMetadataManagedProperty -SearchApplication $SearchApp -Identity $Name
    $managedProperty.MappingsOverridden = $true
    $managedProperty.DeleteAllMappings()
    $managedProperty.Update()
    $category = Get-SPEnterpriseSearchMetadataCategory  -SearchApplication $SearchApp
    $cat =$category |?{$_.Name -eq "Sharepoint"}
    $crawledContenttypeProperty = Get-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $SearchApp -category $cat -Name $CrawledProperty
    $result = $managedProperty.GetMappings() |? { $_.CrawledPropertyName -eq $CrawledProperty}
    if($result -eq $null)
    {
        Write-Host "Creating the managed mapping for $Name"
        $map = New-SPEnterpriseSearchMetadataMapping -SearchApplication $SearchApp -ManagedProperty $managedProperty -CrawledProperty $crawledContenttypeProperty  
    }
}

UpdateManagedProperty -Name 'ExistingManagedProperty' -CrawledProperty 'NewCrawledProperty' -SearchApp $searchApp

Perform a full crawl after the managed property changes

$contentSource = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $searchApp -Identity 'TestContentSource' -ErrorAction SilentlyContinue
$contentSource.StartFullCrawl()
do { 
    Start-Sleep -Seconds 2 
    Write-Host '.' -NoNewLine
}
while ($contentSource.CrawlStatus -ne "Idle")  

Create result source

function CreateResultSource {
    param
    (
        $ScopeName,$SiteUrl,$SearchOwner,$FederationManager
    )
         
    $searchQuery = "?{searchTerms} "+("ContentSource=TestContentSource (Path:{0}) AND (-ManagedProperty:Value) OR (ManagedProperty1:Value2)" -f $siteURL)
    # create result source
    $resultSource = $federationManager.CreateSource($SearchOwner)
    $resultSource.Name = $ScopeName
    $resultSource.ProviderId = $FederationManager.ListProviders()['Local SharePoint Provider'].Id
    $resultSource.CreateQueryTransform($searchQuery)
    $resultSource.Commit()
    Write-Host "Created search result source $ScopeName"

}

$site = Get-SPSite $SiteUrl
[void] [Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.Search")
$federationManager = New-Object Microsoft.Office.Server.Search.Administration.Query.FederationManager($searchApp)
$searchOwner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPSite, $site.RootWeb)
          
if($federationManager.GetSourceByName("TestResultSource", $searchOwner) -eq $null)
{
   CreateResultSource -ScopeName 'TestResultSource'  -SiteUrl $SiteUrl -SearchOwner $searchOwner -FederationManager $federationManager  | Out-Null      
}
else
{
    Write-Host "The search result source 'TestResultSource' already exists"
}

Create result type

function CreateResultType {
    param ($Name, $ResultSource,$ItemTemplateURL,$SiteUrl, $SearchApp)
    
    $web = Get-SPWeb $SiteUrl
    $tenantOwner = Get-SPEnterpriseSearchOwner -Level SPSite -SPWeb $web
    $resultType = Get-SPEnterpriseSearchResultItemType -Owner $tenantOwner -SearchApplication $SearchApp |? { $_.Name -eq $Name  }
    if($resultType -eq $null){
        Write-Host "Creating result type $Name."
        $svcAppProxy = Get-SPEnterpriseSearchServiceApplicationProxy
        $ssa = Get-SPEnterpriseSearchServiceApplication $searchApp
        $displayProperties = "WorkId,Rank,Title,Size,Path,Description,SiteName,HitHighlightedSummary,HitHighlightedProperties,ViewsLifeTime,CustomManagedProperty"
        $ruleCollection = Get-SPEnterpriseSearchPropertyRuleCollection
        $resultSource = Get-SPEnterpriseSearchResultSource -Identity $ResultSource -SearchApplication $ssa -Owner $tenantOwner
        New-SPEnterpriseSearchResultItemType -SearchApplicationProxy $svcAppProxy -Name $Name -Rules $ruleCollection -RulePriority 1 -DisplayProperties $displayProperties -DisplayTemplateUrl $ItemTemplateURL -Owner $tenantOwner -SourceID  $resultSource.Id | Out-Null
        Write-Host "Successfully created result type $Name."
    }
    else{

        Write-Host "Result type $Name already exists."
    }
}

$displayTemplateUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Custom.js"
CreateResultType -Name 'TestResultType' -ResultSource "TestResultSource" -ItemTemplateURL $displayTemplateUrl -SiteUrl $SiteUrl -SearchApp $searchApp    

Create query rules as best bet

function CreateQueryRules {
    param
    (
        $SiteUrl,$Name,$Synonyms,$LinkTitle,$LinkDescription,$LinkUrl
    )

    $searchServiceApplication = Get-SPEnterpriseSearchServiceApplication -ErrorAction SilentlyContinue
    if($searchServiceApplication -ne $null)
    {
        $site = Get-SPSite $SiteUrl
        $searchOwner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner -ArgumentList  @([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPSite,$site.RootWeb)
        $searchObjectFilter = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectFilter($searchOwner)
        $queryRuleManager = New-Object Microsoft.Office.Server.Search.Query.Rules.QueryRuleManager($searchServiceApplication)
        $queryRules = $queryRuleManager.GetQueryRules($searchObjectFilter)
               
        if($queryRules.DisplayName -notcontains $LinkTitle)
        {
            Write-Host "Creating Query Rule $LinkTitle" 
            $queryRule = $queryRules.CreateQueryRule($LinkTitle,$null,$null,$true)
            [string[]]$keywords = $Name
            foreach ($synonym in $Synonyms) {
                if (-not [String]::IsNullOrEmpty($synonym)) {
                    $keywords+= $synonym
                }
            }
        
            $keywordCondition = $queryRule.QueryConditions.CreateKeywordCondition($keywords,$false)
            $queryAction = $queryRule.CreateQueryAction([Microsoft.Office.Server.Search.Query.Rules.QueryActionType]::AssignBestBet)
            $promotedResultCollection = $searchServiceApplication.GetBestBets($searchObjectFilter)
            $promotedResult = $promotedResultCollection.CreateBestBet($LinkTitle, $LinkUrl, $LinkDescription, $false)
            $queryAction.BestBetIds.Add($promotedResult.Id);
            $queryRule.Update()
            $searchServiceApplication.Update()
            Write-Host "Created Promoted Result for" $LinkTitle 
  
        }
        else
        {
            Write-Host "The search query rule $LinkTitle already exists"
        }
    }
}


CreateQueryRules -SiteUrl $SiteUrl -Name 'BestBetKeyword' -Synonyms ('Synonym1','Synonym2','Synonym3') -LinkTitle 'Title' -LinkDescription 'Description' -LinkUrl "$($SiteUrl)"