Remove Item from the Search Index in SharePoint using PowerShell

While working at a client site this week, we encountered an issue where they had duplicate entries for the same Document ID, meaning that when they tried to access that document via its DocId Url, they were presented with a search page showing the two items instead. Without going into the details as to what was actually causing this issue to surface, the easy fix for this was to go and remove one of the duplicate from the Search Index. Through this interface, this can be done as follow:

1 – Navigate to the Search Service Application Page in Central Administration.

Search Service Application page in Central Administration

Search Service Application page in Central Administration

2 – Click on Crawl Log in the left navigation.

SharePoint Search Crawl Log

SharePoint Search Crawl Log

3 – In the top navigation, click on URL View.

URL View in Search Crawl Log

URL View in Search Crawl Log

4 – Search for the URL (or pattern) you want to remove.

Search Crawl Log for URL

Search Crawl Log for URL

5 – Find the item in the list and click on it to expand the contextual menu.

Crawl Log Entry Options

Crawl Log Entry Options

6 – From the list of options, select Remove the item from the Index.

The PowerShell Equivalent

In order for my clients to automate this process for the URLs they want to remove from the Search Index, I created a quick cmdlet called Remove-SPEnterpriseSearchURLFromIndex which simply takes in a URL pattern. Upon detecting URL entries in the Crawl Log that match the provided URL, the cmdlet will prompt the user to remove the item from the index or not.

[CmdletBinding]
function Remove-SPEnterpriseSearchURLFromIndex
{
    param( 
        [System.String]
        $Url = "Default"
    )

    $ssas = Get-SPEnterpriseSearchServiceApplication

    foreach($ssa in $ssas)
    {
        $cl = New-Object Microsoft.Office.Server.Search.Administration.CrawlLog $ssa
        $logEntries = $cl.GetCrawledUrls($false,100,$Url,$true,-1,-1,-1,[System.DateTime]::MinValue, [System.DateTime]::MaxValue)

        foreach($logEntry in $logEntries.Rows)
        {
            Write-Host "You are about to remove " -NoNewline
            Write-Host $logEntry.FullUrl -ForegroundColor Green

            do{
                $deletionAnswer = Read-Host "Do you confirm the deletion (y/n)"
            }while($deletionAnswer.ToLower() -ne 'n' -and $deletionAnswer.ToLower() -ne 'y')

            switch($deletionAnswer)
            {
                'y'
                {
                    $catch = $cl.RemoveDocumentFromSearchResults($logEntry.FullUrl)
                    if($catch)
                    {
                        Write-Host "Deleted" -ForegroundColor Yellow
                    }
                    else
                    {
                        Write-Host "Could not delete the item" -ForegroundColor Red
                    }
                }
                'n'
                {
                    break
                }
            }
        }
    }
}

Get Search Results in SharePoint 2013 using PowerShell

I’ve started playing with some of the search APIs in SharePoint 2013 lately, and wanted to transpose my experiments into PowerShell to see just how easy it was to do. I have been developping a C# client that runs directly on the server, prompts the user to enter a keyword and lists all search results received back from the SharePoint Search Service Application. I managed to replicate this client application in PowerShell by creating a 10 lines script. I basically converted over the C# code into PowerShell.

Before getting started, we need to make sure we load the Microsoft.Office.Server.Search.Query Assembly within our PowerShell session. Our code will by using objects from that namespace.

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.Office.Server.Search.Query”)

My code uses the Microsoft.Office.Server.Search.Query.KeywordQuery object to retrieve search results back. This object takes in a reference to a Site Collection to perform the search against.

$site = Get-SPSite http://localhost

$keywordQuery = New-Object Microsoft.Office.Server.Search.Query.KeywordQuery($site)

Next step is to prompt the user to input a keyword for the search results. Once obtained, we will assign this keyword to QueryText property our newly created KeywordQuery object.

$keyword = Read-Host “Search Term”

$keywordQuery.QueryText = $keyword

We now have an object that represent our query to send to the server, but in order to execute this Query, we need to instantiate a second object, the Microsoft.Office.Server.Search.Query.SearchExecutor. This object exposes a method name ExecuteQuery() to which we wil pass our query object in order to get search results back.

$searchExec = New-Object Microsoft.Office.Server.Search.Query.SearchExecutor

$searchResults = $searchExec.ExecuteQuery($keywordQuery)

The last step for us is to look at the Table property of the returned object in order to get a list of items that have been identified as matching our search criterias. For clarity’s sake, we will only be exposing the Title, Path, Author, LastModifiedTime and IsDocument property of each search result.

$table = $searchResults.Table

$table | Select Title, Path, Author, LastModifiedTime, IsDocument

So to prove that my code works, I will execute a search using the SharePoint Search interface, search for the word “Gadgets”. Here is what the interface returns for results.

20150110.png

We can see from the screenshot above that search for the word “Gadgets” returns 2 results. Now, let us put our PowerShell script together:

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.Office.Server.Search.Query”)
Write-Host “`r`n”

$site = Get-SPSite http://localhost
$keywordQuery = New-Object Microsoft.Office.Server.Search.Query.KeywordQuery($site)
$keyword = Read-Host “Search Term”
$keywordQuery.QueryText = $keyword
$searchExec = New-Object Microsoft.Office.Server.Search.Query.SearchExecutor
$searchResults = $searchExec.ExecuteQuery($keywordQuery)

Write-Host “`r`n”
$table = $searchResults.Table
Write-Host $table.Length” Results Found” -BackgroundColor “Green” -ForegroundColor “Black”
$table | select Title, Path, Author, LastModifiedTime, IsDocument

Now, if we were to execute the above PowerShell script against my local SharePoint farm, here is what the result would look like:

2015011002.png

We have therefore been able to obtain the exact same results for our SharePoint 2013 Search Web interface compared with our PowerShell version. This is a neat little script that could prove to be very useful in many scenarios.

SharePoint 2013 Search Capacity Planner

So you want to build a SharePoint 2013 Search environment and you’re having a hard time figuring out what servers you’ll need to build you farm? Well I’ve got just the thing you need. Based off Microsoft’s Technet article about capacity planning for SharePoint 2013 Search comes the Search Capacity Planner app! This is a custom SharePoint-Hosted app I built, and that lets you figure out details about what is the recommended architecture for your search farm based on the number of items you are trying to index. Not only does it automatically tells you how many servers you need, but it tells you what there role should be, what their specifications are, and it estimates the time taken for a Full crawl to complete. I will be submitting the app for free in the coming week, so that you can all deploy it in your organizations. All I have to say is enjoy!

 

 

SCPA.png