How to: Query People Search in SharePoint Online (and other SharePoint platforms too!)
Posted
Tuesday, January 3, 2012 10:38 PM
by
CoreyRoth
At my Search talk at SPC11, I demoed how to build a Silverlight application that could query search in SharePoint Online. I also built a separate application that could query people search, but I haven’t posted on it yet until today. To query people search, we have to know a few things about how SharePoint operates. It all starts with understanding the scopes involved. If you take a look at your Search Scopes link in your site collection settings, you’ll see a similar list to the one below.
What’s funny here is that SPO actually returns item counts for the entire (non-partitioned index). This tells me that there are 205,000 items and 96,000 people on my particular Office 365 SharePoint host.
Ultimately, querying people search is just a matter of executing a query against the scope People, but there are a few catches. In SharePoint 2007, you could actually execute the query Scope:”People” on a results.aspx with a CoreResultsWebPart and you would actually get results. It wouldn’t display pictures or additional user information such as department or phone number, but it would work. With the introduction of federated search in SharePoint 2010, people search now gets executed by it’s own federated location named LocalPeopleSearchIndex (see my post on the QueryManager class for more information). This means that a regular CoreResultsWebPart will not return results for Scope:”People” no matter what you do.
However, a PeopleCoreResultsWebPart will.
Issuing the query Scope:”People” will return every user in the user profile store. Including service accounts like you see above. The fact that I couldn’t issue a people search query to the regular CoreResultsWebPart actually threw me off for quite some time. It just didn’t make sense to me what was going on behind the scenes. It didn’t become ultimately clear, until I tried issuing the query to Search.asmx. The Scope:”People” query works just fine when calling the web service. Today you will learn how to query people search using the web service. I’m going to use the exact same application I have been using with my other search demos, we’ll just tweak the input a little bit.
For today’s example, I am going to build off of the Silverlight 4 application I used at SPC. I’m using Silverlight, but you could just as well write a console application or call this web service from some other ASP.NET application. We still want to start by creating a service reference to /_vti_bin/search.asmx. We then create an instance of the web service so that we can use it. We also bind an event handling method to handle the results of the web service call.
QueryServiceSoapClient queryService = new QueryServiceSoapClient();
queryService.QueryExCompleted += new EventHandler<QueryExCompletedEventArgs>(QueryService_QueryExCompleted);
Once we have a reference to the web service, it’s just a matter of constructing the input XML document and sending it to the web service. For SharePoint Online and SharePoint 2010, we actually don’t have to change any of the XML at all. We just need to change the query. That means our input will look like the following. Note, we’re using a type of STRING (even if using FAST Search for SharePoint).
<QueryPacket xmlns="urn:Microsoft.Search.Query" Revision="1000">
<Query domain="QDomain">
<SupportedFormats>
<Format>urn:Microsoft.Search.Response.Document.Document</Format>
</SupportedFormats>
<Context>
<QueryText language="en-US" type="STRING">Scope:"People"</QueryText>
</Context>
</Query>
</QueryPacket>
If we are using FAST Search for SharePoint, we need to add to a ResultProvider element inside the Query element. Remember, People Search is handled by the SharePoint 2010 search engine in FAST Search for SharePoint.
<ResultProvider>SharePointSearch
</ResultProvider> Just like before, I assemble this XML string using a StringBuilder. I allow the user to type in a query and I just automatically append Scope:”People” to the query ensuring we only get people results.
StringBuilder queryXml = new StringBuilder();
queryXml.Append("<QueryPacket xmlns=\"urn:Microsoft.Search.Query\" Revision=\"1000\">");
queryXml.Append("<Query domain=\"QDomain\">");
queryXml.Append("<SupportedFormats>");
queryXml.Append("<Format>");
queryXml.Append("urn:Microsoft.Search.Response.Document.Document");
queryXml.Append("</Format>");
queryXml.Append("</SupportedFormats>");
queryXml.Append("<Context>");
queryXml.Append("<QueryText language=\"en-US\" type=\"STRING\">");
queryXml.AppendFormat("{0} Scope:\"People\"", SearchTextBox.Text);
queryXml.Append("</QueryText>");
queryXml.Append("</Context>");
queryXml.Append("</Query>");
queryXml.Append("</QueryPacket>");
queryService.QueryExAsync(queryXml.ToString());
We’ll then bind the resulting XML to a textbox so that we can look at the results.
ResultsTextBox.Text = e.Result.Nodes[1].ToString();
That’s all that is involved in this first round of code. Compile the code and upload the Silverlight application to a document library and use the Silverlight web part just like before. If you want more details on how the above code works, be sure and check out the original Search with Silverlight 4 post. Let’s take a look at the results. Here’s the application running.
I issues a query and I got results. Let’s look at the XML in the results a bit more though.
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<Results xmlns="">
<RelevantResults diffgr:id="RelevantResults1" msdata:rowOrder="0">
<WorkId>72787</WorkId>
<Rank>100000000</Rank>
<Title>Craig Johnson</Title>
<Size>0</Size>
<Path>https://dotnetmafia-my.sharepoint.com/Person.aspx?accountname=i%3A0%23.f|membership|craig.johnson%40dotnetmafia.onmicrosoft.com</Path>
<Write>2011-09-09T18:20:05-07:00</Write>
<SiteName>https://dotnetmafia-my.sharepoint.com</SiteName>
<CollapsingStatus>0</CollapsingStatus>
<HitHighlightedSummary />
<HitHighlightedProperties><HHTitle>Craig Johnson</HHTitle><HHUrl>https://dotnetmafia-my.sharepoint.com/Person.aspx?accountname=i%3A0%23.f|membership|craig.johnson%40dotnetmafia.onmicrosoft.com</HHUrl></HitHighlightedProperties>
<ContentClass>urn:content-class:SPSPeople</ContentClass>
<IsDocument>false</IsDocument>
<PictureThumbnailURL>https://dotnetmafia-my.sharepoint.com/User Photos/Profile Pictures/i_0_.f_membership_craig.johnson@dotnetmafia.onmicrosoft.com_MThumb.jpg</PictureThumbnailURL>
</RelevantResults>
We have the user’s name in the Title field and we have some links to a picture and the user’s profile. For the most part though, these fields aren’t entirely useful. That’s because SharePoint treats this result as a regular document result instead of a person. If we want useful information about the user, we have to ask for that information in our original query. Specifically, we need to specify the names of the managed properties that we want returned (i.e.: JobTitle and Department). How do we know what the names of those properties are? In SharePoint 2010, we can just go look in the search service application. However, we don’t have that option in SharePoint Online. There’s another way though. Go back to your PeopleResults.aspx page in your Search Center and edit it. Then, you need to modify the PeopleCoreResultsWebPart. Expand Display Properties and then uncheck Use Location Visualization. The contents of the XML in Fetched Properties has the answer.
Here is what the XML looks like.
<Columns>
<Column Name="WorkId"/>
<Column Name="UserProfile_GUID"/>
<Column Name="AccountName"/>
<Column Name="PreferredName" HitHighLight="true"/>
<Column Name="YomiDisplayName" HitHighLight="true"/>
<Column Name="JobTitle" HitHighLight="true"/>
<Column Name="Department" HitHighLight="true"/>
<Column Name="WorkPhone" HitHighLight="true"/>
<Column Name="OfficeNumber" HitHighLight="true"/>
<Column Name="PictureURL"/>
<Column Name="HierarchyUrl"/>
<Column Name="WorkEmail" HitHighLight="true"/>
<Column Name="Path"/>
<Column Name="HitHighlightedSummary"/>
<Column Name="HitHighlightedProperties"/>
<Column Name="Responsibility" HitHighLight="true"/>
<Column Name="Skills" HitHighLight="true"/>
<Column Name="SipAddress" HitHighLight="true"/>
<Column Name="Schools" HitHighLight="true"/>
<Column Name="PastProjects" HitHighLight="true"/>
<Column Name="Interests" HitHighLight="true"/>
<Column Name="OrgNames" HitHighLight="true"/>
<Column Name="OrgUrls"/>
<Column Name="OrgParentNames" HitHighLight="true"/>
<Column Name="OrgParentUrls"/>
<Column Name="Memberships" HitHighLight="true"/>
<Column Name="AboutMe" HitHighLight="true"/>
<Column Name="BaseOfficeLocation" HitHighLight="true"/>
<Column Name="ServiceApplicationID"/>
<Column Name="SocialDistance"/>
</Columns>
This gives you quite a few choices to display in your results. The column names are pretty self explanatory. Now, we just have modify our input XML to specify which managed properties we want. Do you remember how to specify managed properties with the Search web service? If not, we start by adding a Properties element inside the Query element. We then add a Property element for each managed property. One thing to remember is that the managed properties must be specified in lower case. You will get a NotFound exception if they are not. Here is an example:
<Properties>
<Property name="accountname" />
<Property name="preferredname" />
<Property name="jobtitle" />
<Property name="department" />
<Property name="workphone" />
<Property name="officenumber" />
<Property name="pictureurl" />
<Property name="workemail" />
</Properties>
Remember, once you specify any column, you must specify every column you want. The default columns will no longer be returned. When we recompile, upload the new application, and execute a query again, we now get much better results.
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<Results xmlns="">
<RelevantResults diffgr:id="RelevantResults1" msdata:rowOrder="0">
<accountname>i:0#.f|membership|craig.johnson@dotnetmafia.onmicrosoft.com</accountname>
<preferredname>Craig Johnson</preferredname>
<jobtitle>Accountant</jobtitle>
<department>Accounts Receivable</department>
<workphone>123-555-1215</workphone>
<officenumber>123455</officenumber>
<pictureurl>https://dotnetmafia-my.sharepoint.com/User Photos/Profile Pictures/i_0_.f_membership_craig.johnson@dotnetmafia.onmicrosoft.com_MThumb.jpg</pictureurl>
<workemail>craig.johnson@dotnetmafia.onmicrosoft.com</workemail>
</RelevantResults>
You can query with these same managed properties as well. For example, I could use the department managed property to see who is in Accounting with the following query.
department:”accounting”
Maybe, you want to look up who the CEO of the company is. To do that use the jobtitle managed property. Resist the urge to capitalize the words in the managed property. They must be in lower case. Note: that the title managed property is reserved for personal titles such as Jr., Sr. etc.
jobtitle:”CEO”
Now, that we can query with all of these properties, we can make a nice advanced people search application. In my demo at SPC11, I used the Telerik RadGridView control to display the user information along with the picture from the user profile. Here’s a screenshot of what the application looked like.
You can use the same techniques that I detailed in the Advanced Search with Silverlight 4 post to build a people search application like the one above. This code will work on-premises (SharePoint 2010 or FAST Search for SharePoint) or in the cloud with SharePoint Online. I’ve confirmed this works with both the P1 and E3 SKUs of Office 365.