SharePoint 2013 Search Web Part Missing Results

April 26, 2013

While working on a few SharePoint 2013 projects I have been utilising the SharePoint search in a lot of places to provide dynamic content and there has been quite a few occassions when things don’t seem to work as expected such as CBS Server Side Date Error or Results web part ordering. I suppose this is to be expected when the search engine has been completely replaced with a mix of the old SharePoint search engine and FAST search.

When working on one aspect of a recent project I was using the results script web part to query some events stored in a list. As per you would expect these events had the typical details such as title, date, description, etc. At first everything seemed to be working as expected until the client reported there was a bunch of events missing from the search generated list. My first thought was these were new events and weren’t in the search index so I kicked off a full crawl. Once this was completed I checked the site and the events were still missing.

Next I looked at my query to see if there was something wrong with it. For example I was filtering out all past events and restricting the search results to the events content type but everything seemed ok.

I spent some time looking around and eventually I noticed that the missing items were very similar to other items which were getting displayed in the search list. There would be 4/5 events in the list with the same title and description but with different dates or locations. I had a look at the web part properties and I noticed there was an option to show a link to display duplicate results, see figure 1.

WebPartProperties

Figure 1

I selected this option but it never done anything. From previous experience with some web parts I knew there are sometimes web part properties which are not exposed via the UI which can still be set if you export the web part, a good example of this is to get the Content By Search web part to run server side. I exported the web part and opened this with notepad and searched for “Duplicate”. Wouldn’t you know it there was a boolean flag in the DataProviderJSON property called TrimDuplicates and this is defaulted to true, see snippet below in figure 2.

<property name="DataProviderJSON" type="string">{"QueryGroupName":"Default","QueryPropertiesTemplateUrl":"sitesearch://webroot","IgnoreQueryPropertiesTemplateUrl":false,"SourceID":"8413cd39-2156-4e00-b54d-11efd9abdb89","SourceName":"Local SharePoint Results","SourceLevel":"Ssa","CollapseSpecification":"","QueryTemplate":"ContentTypeId:0x0100440D27D8CDA949078CD3C29F3F3D7012*{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":"","UpdateAjaxNavigate":true,"SummaryLength":180,"DesiredSnippetLength":90,"PersonalizedQuery":false,"FallbackRefinementFilters":null,"IgnoreStaleServerQuery":false,"RenderTemplateId":"DefaultDataProvider","AlternateErrorMessage":null,"Title":""}</property>

Figure 2 – Please note this has the QueryTemplate element remove so the exact query being executed isn’t visible to others 🙂

Most of the snippet above is default settings which you can ignore the import one is TrimDuplicates. In my case as soon as I set the this to false, saved the web part, imported it into the web part gallery and added it to a page all my results started to display correctly.

The key element here is how SharePoint determines if items are duplicates and in my case I suppose it’s fair enough as most of the key information such as the title and description were pretty much exactly the same.

This is another classic example of SharePoint and how its working as expected but because there are 101 configuration options you are not getting the results expected. It’s important to fully understand the product and all the options and what it can offer. This is one I will not forget and hopefully it provides a prompt for others in a similar situation.

Advertisements

Export User Profile Information Using PowerShell

April 5, 2013

While working on my most recent project I have spent a lot of time using the user profile service and while it offers some real benefits I have found it can be difficult to troubleshoot, especially when it comes to synchronizing with external sources like active directory (AD).

After configuring the user profile service on the client’s live environment I noticed that the email and manager fields were missing for some users. I wanted to try and get an idea of the extent of the issue so I went to the manage user profiles screen in central admin (CA) and while its useful for finding individual users its doesn’t really help view certain properties for all users.

Thinking about the options available I knew from previous experience I could write some C# code to get this information but I have been making an effort to brush up on my PowerShell (PS) skills so I decided this would be a perfect opportunity.

As with all PS scripts its essential these are first tested in a development environment before run on live. I began by creating my PS script on my development environment using PowerGUI as I think this is an excellent tool which makes creating PS scripts much easier.

Before starting I worked out what the rough steps would be to get the user profile information and i came up with the following:

  1. Get the site the user profile service is associated with
  2. Get the user profile service
  3. Get all user profiles
  4. Loop through all user profiles outputting the required information
  5. Collate steps and parameterise into a function

I then started working on each step and finally put it all together. Each of the steps is detailed below.

Step 1

From previous experience I knew the easiest way to get the user profile service was to get the service context for the site the user profile service is associated with. The first line gets the site passing a variable holding the site URL, next I check we have the site. I then got the service context associated with the site by calling the Get-SPServiceContext method passing the site

  1. $site = Get-SPSite $SiteURL -ErrorAction SilentlyContinue
  2.     if($site -eq $null)
  3.     {
  4.         Write-Host "Unable to access site " $SiteURL " please ensure the URL is correct and you have access to this"
  5.         return
  6.     }
  7.     
  8.     $serviceContext = Get-SPServiceContext($site) -ErrorAction SilentlyContinue        
  9.     if($serviceContext -eq $null)
  10.     {
  11.         Write-Host "Unable to get the service context for site " $SiteURL
  12.         return
  13.     }

 

Step 2

Once I had the service context the next step is to get the user profile manager which is done by creating a new UserProfileManager instance passing in the associated service context.

  1. $userProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)    
  2.     if($userProfileManager -eq $null)
  3.     {
  4.         Write-Host "User Profile Manager does NOT exist"
  5.         return
  6.     }

Step 3

With the instance of the user profile manager I then got all users by calling the GetEnumerator method.

  1. $users = $userProfileManager.GetEnumerator()

Step 4

In my situation there were hundreds of users so I decided outputting the information to the PS console would be difficult to read of I decided to output the details to a text file instead.

I looped though each user profile outputting the users preferred name. In addition so I could get additional user profile property values I looped through an array containing the names of user profile properties I wanted to get and output a string containing the property name and the property value for the current user profile

  1. foreach ($user in $users)
  2.     {        
  3.         $userName = $user.Item("PreferredName")
  4.         Write-Output "User Profile $userName" | Out-File $FileLocation -Append
  5.         if($UserProperties.Count -gt 0)
  6.         {        
  7.             for ($i = 0; $i -lt $UserProperties.Count;$i++)
  8.             {
  9.                 $UserProperty = $UserProperties[$i]                
  10.                 if($UserProperty -ne "" -and $UserProperty -ne $null)
  11.                 {
  12.                     try
  13.                     {
  14.                         $userPropertyValue = $user.Item($UserProperty)
  15.                         Write-Output "User Profile Property $UserProperty value is $userPropertyValue" | Out-File $FileLocation -Append
  16.                     }
  17.                     catch
  18.                     {
  19.                         Write-Host "Property $UserProperty doesn't exist"
  20.                     }    
  21.                 }
  22.             }
  23.         }
  24.     }

Step 5

Putting all the above steps together adding additional logging, variable checks and ensuring all settings can be passed in I ended up with the function below.

  1. function GetAllUserProfiles([string]$SiteURL, [string]$FileLocation, [array]$UserProperties)
  2. {
  3.     if($siteURL -eq $null -or $FileLocation -eq $null)
  4.     {
  5.         Write-Host "Please ensure all parameters are supplied"
  6.         return
  7.     }        
  8.     
  9.     Write-Host "Starting to get all user profiles"
  10.     
  11.     if($SiteURL -eq $null)
  12.     {
  13.         Write-Host "Please supply the site URL the User Profile service application is assocaited with"
  14.         return
  15.     }
  16.     
  17.     $site = Get-SPSite $SiteURL -ErrorAction SilentlyContinue
  18.     if($site -eq $null)
  19.     {
  20.         Write-Host "Unable to access site " $SiteURL " please ensure the URL is correct and you have access to this"
  21.         return
  22.     }
  23.     
  24.     Write-Host "Got site " $SiteURL
  25.     
  26.     $serviceContext = Get-SPServiceContext($site) -ErrorAction SilentlyContinue
  27.         
  28.     if($serviceContext -eq $null)
  29.     {
  30.         Write-Host "Unable to get the service context for site " $SiteURL
  31.         return
  32.     }
  33.     
  34.     $userProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)
  35.     
  36.     if($userProfileManager -eq $null)
  37.     {
  38.         Write-Host "User Profile Manager does NOT exist"
  39.         return
  40.     }
  41.     
  42.     Write-Host "Got User Profile Manager "
  43.     
  44.     Write-Host "There are " $userProfileManager.Count " user profiles starting to loop through them. The results will be output to specified text file"
  45.     
  46.     $users = $userProfileManager.GetEnumerator()
  47.     foreach ($user in $users)
  48.     {        
  49.         $userName = $user.Item("PreferredName")
  50.         Write-Output "User Profile $userName" | Out-File $FileLocation -Append
  51.         if($UserProperties.Count -gt 0)
  52.         {        
  53.             for ($i = 0; $i -lt $UserProperties.Count;$i++)
  54.             {
  55.                 $UserProperty = $UserProperties[$i]                
  56.                 if($UserProperty -ne "" -and $UserProperty -ne $null)
  57.                 {
  58.                     try
  59.                     {
  60.                         $userPropertyValue = $user.Item($UserProperty)
  61.                         Write-Output "User Profile Property $UserProperty value is $userPropertyValue" | Out-File $FileLocation -Append
  62.                     }
  63.                     catch
  64.                     {
  65.                         Write-Host "Property $UserProperty doesn't exist"
  66.                     }    
  67.                 }
  68.             }
  69.         }
  70.     }
  71.     
  72.     Write-Host "Function completed"
  73. }

Conclusion

I have already used this function several times without any issues and I have found it a very easy and flexible way to pull information from the user profile store.

I hope this is helpful for others too.


%d bloggers like this: