January 2013 - Posts
As someone who has been a member, presenter, officer and board member of various user groups, I've come up with some observations on what makes a great user group. I would never call myself an expert on the subject (I reserve that for @Victor_Chat), but I think this is some good advice.
DO encourage and participate in a SharePint
Yes, I'm actually listing this first. More of the connections and relationships happen here than when mingling before the group. Attendees get the opportunity to let loose and it's a great way to get to know people. If you have questions, this is where you get them answered. The Houston SharePoint Users Group (HSPUG) does this right. It's not uncommon to see 40+ people at one of our SharePints. We actually have sponsors for it. :)
DO delegate
Some of the smaller groups tend to have one or two people that do everything. Ask for help and you will likely get it. There are usually people willing to help out. People can help with everything from signing people in to ordering food. If you're a member and want to help out, don't be afraid to ask what you can do.
DO encourage new speakers
You don't need big name speakers to have a good user group. Encourage new speakers to present to the group. When I was at Tulsa, we encouraged new speakers at every session. It was usually pretty effective.
DO seek out officers from diverse backgrounds
It's great to have officers from different backgrounds. Employ a healthy mix of consultants and non-consultants. Do this even if the officers work for a competitor. It shows that you aren't trying to dominate the user group with your company.
DO encourage others to meet up outside of the user group
This is something Houston is playing around with. We live in a large city and are encouraging members to get together throughout the month at a local bar or restaurant. We're still judging the success on this as I think it's yet to establish it's rhythm yet.
DO post your topics early
This sounds easy in theory but now that I have been involved with a few groups, I know it's not. Ideally, you want to line up multiple speakers months in advance. It is great if you can have these posted to the web site, include them in your communications, and even announce the next topics during each meeting.
DO engage in social media
Some groups do this better than others. Although probably less than 10% of your user group is on twitter, it's still a good way to remind users about what is going on throughout the month.
DON'T be afraid to ask questions
As a member, don't be afraid to ask questions of the presenter, the officers, or other members. Chances are there are users there that face the same challenges as you.
DON'T use vendor spotlights in place of presentations
Although it's great to support your sponsors, your users are there to learn, not to be sold to. It's ok, to let a vendor spend two minutes before a presentation and say hey thanks for letting us sponsor. However, it should never be used as a replacement for finding a speaker. Your users don't like this and it will drive them away from the group. Most user groups allow the vendor to set up a table and buy beers afterwards. This is more effective for them than giving a full-blown sales presentation.
DON’T sell your member list
We have vendors asking for the contact info of our members all the time. The answer to that request always is unequivocally NO. It is absolutely unacceptable to sell your members out like that. They expect some you to have some degree of ethics and decorum and not to sell their information away. If you run an ISV, under no circumstances should you ever send out a marketing E-mail from your company using the user group’s E-mail list. I’ve seen this happen and it makes me sick.
DON'T use the user group to promote your business
I attended a user group once where the self-promotion was so bad it made me sick. The first words out of your mouth shouldn't be about your company. The best officers I have seen have never even mentioned where they work once in front of the group. Your members will only tolerate this for so long before they stop coming. I've seen this nearly destroy groups before. It's ok to talk about your company afterwards over a beer. However, it's unacceptable to spend 15 minutes before the meeting talking about your company. Plus, this severely cripples sponsorship opportunities. As someone promoting your own business, you would never allow a competitor to sponsor. Plus, your competitors aren’t going to want to sponsor a corrupt user group anyways. Just don’t do it.
I remember one time in Houston, where someone did a 10 minute case study on a solution they built. Some of the members felt it was a bit salesy and were about to run them out the door. In reality, I didn't think it was salesy at all. The presenters were developers not account managers. However, you always have to be careful when doing case studies because they can come off as a sales pitch if you’re not careful.
Make your sales the old-fashioned way by playing golf, buying beers, and going to expensive dinners.
I may have bragged a little bit about my user group, but hey, it’s pretty awesome. Seriously, if you’re ever in town the third Wednesday of the month, you should make a point to attend. :) Hopefully, you find some of this information helpful. What tips do you have for user groups? Leave a comment and let me know.
If you have tried to do any work with SharePoint 2013 Client Web Parts with the Office Developer Tools Preview 2 in Visual Studio 2012, you know it’s an exercise in frustration. It literally locks up every three or four lines of code you try to type. When it does this, it will freeze for ten seconds or more. This occurs when you are editing the page associated with the web part. It’s a total productivity killer.
It finally bugged me enough that I started looking for a solution. I was pretty close to switching to notepad. Luckily, I found a quick work-around by launching a different text editor. To launch another editor, right click on your web part page in the Solution Explorer and choose Open With…
Choose Source Code (Text) Editor.
This editor is similar to the Web Form Editor but it won’t lock up. You will lose IntelliSense and AutoComplete. However, I’d gladly close my own div tag to not have the editor lock up every ten seconds. The HTML is still color coded at least, but the JavaScript is not. Your JavaScript should be in a separate file anyways though. :) If you have been experiencing this issue, I hope this helps.
By now you may have noticed that Search has changed quite a bit in SharePoint 2013. The ResultScriptWebPart powers most of the magic behind the Search Center. It replaces the CoreResultsWebPart that we have been using since SharePoint 2007. I expected many things in it to operate the same, but this web part really has been rewritten from the ground up and that’s a good thing. One new feature (of many) it brings us is the ability to sort search results. You can specify a sort order in the query or by enabling it in the web part properties. This allows the user to pick from a list of properties. I’ll cover it in the future because it deserves a quick post. However, for you developers out there, I know that some of you will want to write some code that passes a sort order. This post explains how to do it.
Before we begin, you have to know what you can sort of. There are a lot of options out-of-the-box such as Author and Write (Modified Date) or you can use your own property. Whatever you choose, you need to check the managed properties page and make sure it has the Sortable option set to Yes - active. You do this from the Search Schema page on your site collection or Search Service Application.
Since SharePoint 2007, we have been able to pass a query using KQL using the “k” parameter to the results.aspx of your search center. We can still do this in SharePoint 2013. To add a sort order though, we do things a little differently. Using a new query string parameter called Default, we can pass in a number of properties including sort order. The format is quite a bit different though as it uses a JSON notation. To start, I am going to issue a query with out k parameter using this new JSON notation. Let’s do a search for our sales documents. To do this, I include my search center URL (/search/pages/results.aspx) and append #Default= to it followed by the JSON object with a name value pair {"k":"Sales"}. When I assemble to entire string, it looks something like this.
http://server/search/Pages/results.aspx#Default={"k":"Sales"}
Now we want to add sorting. To do that, we use the “o” parameter. It takes a collection of JSON objects. The parameter p represents the managed property to sort by and the value d is the sort direction specified as a 0 or 1. A value of 0 represents ascending and a value of 1 represents descending. For example to sort by modified date descending, we use the Write managed property. The string would look like this:
"o":[{"d":1,"p":"Write"}]
When we add it to our query string from above it looks like this.
http://server/search/Pages/results.aspx#Default={"k":"Sales","o":[{"d":1,"p":"Write"}]}
This shows us the newest documents. In SharePoint 2013, they moved the modified date to the InfoCard so you actually have to hover over a search result to see the date. To show the oldest documents, you would just specify d:'”0” instead.
As another example, we could also sort by Author.
http://server/search/Pages/results.aspx#Default={"k":"Sales","o":[{"d":0,"p":"Author"}]}
I’ve included a bigger screenshot where you can see the name of the first author in the list on the InfoCard.
We can also combine them to have multiple sort orders, but I have had mixed results with that so far. Here’s what the query string would an example look like though.
http://server/search/Pages/results.aspx#Default={"k":"Sales","o":[{"d":0,"p":"Write"},{"d":1,"p":"Author"}]}
I’m pretty excited about sorting functionality in the search center. Users have been wanting it for quite a long time and only a select few ever had it (those with FAST Search for SharePoint). If you are writing search solutions, be sure and check my post about sorting with the REST API. This technique works with both SharePoint 2013 on-premises as well as SharePoint Online Preview. All of the screenshots came from Office 365.
Every organization wants better search results. However, few have actually spent the time to create file plans, content types, and managed properties to make it happen. SharePoint 2013 has a feature called entity extraction though that can actually infer values by comparing what it finds in the body of the document to a dictionary. This feature is Entity Extraction. If you are familiar with FAST Search for SharePoint or FAST ESP, you know that entity extraction is nothing new. What it does is gives you the power to infer the values of managed properties in search without having metadata in your site columns. FS4SP and SharePoint 2013 come with an extraction dictionaries for company names out-of-the-box. This means when it crawls it will search the contents of the body of your documents and if it recognizes any companies it knows, you can then search and refine on it. However, the true value comes when you create your own. SharePoint 2013 makes it really easy to create these custom dictionaries.
Let me give you a few more examples to help you really understand the significance. Most companies have a concept of a department or business unit. We can create a dictionary with all of the company’s departments such as Accounting, Human Resources, IT, Operations, etc. With entity extraction, if it finds a document and somewhere the text “Human Resources Organization Chart” is found, it then sets the value of the managed property to Human Resources. Now, when the user does an advanced search and says “show me all Human Resources documents”, that result comes back with more confidence. By using the extraction dictionary, we get better results that just using a regular keyword search. Now you may be wondering that’s nothing special, but we can also add other terms, for example, I could add “HR”, and “Benefits” to that dictionary as well and map them to the Human Resources department. Once I have a property defined like this, I can combine it with others as well. This will let us issue queries like “show me all documents from Human Resources with a document type of Policy”.
As another example, you could pre-load SharePoint with a dictionary of product names. For those of you in the energy industry, you could pre-populate it with a list of wells or names of leases. Are you starting to see the significance? In previous versions of SharePoint, we had to set site columns in our document libraries to capture these values either from the user or programmatically. Now with the right dictionary, we can assume the values with a reasonable degree of confidence.
Alright, enough explanation, let’s see this in action and hopefully it will make sense. MSDN has a great post on this but I found and error in it so that is why I am writing this post (plus I know you all like screenshots). For my example, I have a bunch of PowerPoint documents about SharePoint 2013 on my site. I’ve created a custom entity extraction dictionary that will help me refine these documents by feature in SharePoint (i.e.: Apps, Business Intelligence, Search, Social, etc).
Before you build your dictionary file though, you need to decide on what type of extraction to use. You can choose whether you want it to match a word or just part of it. You can also specify whether it needs to match exactly. The MSDN article summarizes this well, but here’s a quick recap of your choices.
- Word Extraction – case-insentive, word has to match, limited to 5 dictionaries
- Word Part Extraction – case-insensitive, only part of a word has to match, limited to 5 dictionaries
- Word Exact Extraction – case-sensitive, word has to match, limited to 1 dictionary
- Word Part Exact Extraction – case-sensitive, only part of a word has to match, limited to 1 dictionary
For today’s example, I am going to use Word Part Extraction as it gives you some of the most flexibility. You might want to use Word Exact Extraction for extracting IDs from a document. For example, you could use it to extract part numbers or invoice numbers. Those aren’t something you would typically want to refine on but you may want to query on them. FAST Search for SharePoint only provided case-sensitive extraction. This made the feature less useful as you had to accommodate all possible varieties of case combinations in your dictionary file.
To get started, we need to create a dictionary file. I started with notepad and I also edited with Excel some. The format is simple “Key,Display form”. The key is what the crawler matches in the body of the document and the Display form is what gets displayed in the refiner. Although the Display form is optional, you want to include it as it allows you to control the exact way the text looks in the refiner (including case sensitivity). Here’s the dictionary file I created.
Key,Display form
bcs,Business Connectivity Services
Business Connectivity Services,Business Connectivity Services
wcm,Web Content Management
Web Content Management,Web Content Management
bi,Business Intelligence
Business Intelligence,Business Intelligence
ecm,Electronic Content Management
Electronic Content Management,Electronic Content Management
Apps,Apps
Analytics,Analytics
Development,Development
Service Application,Service Application
Excel Services,Excel Services
Office Web Apps,Office Web Apps
owa,Office Web Apps
wac,Office Web Apps
PerformancePoint,Business Intelligence
Search,Search
Social,Social
My Sites,My Sites
Communities,Social
Visio Services,Visio Services
Workflow,Workflow
The next step is to use the PowerShell command, Import-SPEnterpriseSearchCustomExtractionDictionary. The documentation on this page is correct which is how I found the solution to my problem. The first step is to get a reference to the Search Service Application using Get-SPEnterpriseSearchServiceApplication. Then for our import command, we pass the service application, the Filename, and the DictionaryName. Here’s where the complexity comes in. The Filename requires a UNC path. That means something like \\servername\path\file.csv. It won’t take a relative path. The next tricky part is where I found an issue in the documentation. I wanted to use the Word Part extraction, it has the case of the word “WordPart” incorrect in the dictionary name. If you do specify the DictionaryName wrong, it will not work. Here are the list of valid values which correspond to the extraction types we talked about above.
- Microsoft.UserDictionaries.EntityExtraction.Custom.Word.n [where n = 1,2,3,4 or 5]
- Microsoft.UserDictionaries.EntityExtraction.Custom.ExactWord.1
- Microsoft.UserDictionaries.EntityExtraction.Custom.WordPart.n [where n = 1,2,3,4 or 5]
- Microsoft.UserDictionaries.EntityExtraction.Custom.ExactWordPart.1
For the non-exact entries, you need to specify a value at the end (1 – 5) which specifies which extraction dictionary it uses. In the case below, I did number 2 since I already messed up number 1 by specifying the dictionary name incorrectly. :)
$searchApp = Get-SPEnterpriseSearchServiceApplication
Import-SPEnterpriseSearchCustomExtractionDictionary –SearchApplication $searchApp –Filename \\server\c$\folder\WordPartExtraction.csv –DictionaryName Microsoft.UserDictionaries.EntityExtraction.Custom.WordPart.2
When you run the commands, it will look like this. I put the above in a script file for convenience.
The next part I believe is a little different than the way you did it with FAST Search for SharePoint. What you need to do is tell search which managed properties to perform entity extraction. Effectively, what we are doing here is telling search to look at the body of the document and see if anything in there matches items in the dictionary. To do this, go to your Search Service Application –> Search Schema and edit the property named body.
Scroll to the bottom and you will see the Custom entity extraction section. Now, we just need to check the box next to the dictionary we want to use. In my case, it is Word Part Extraction – Custom2. If you have multiple dictionaries you may select them here. Save the managed property after you make your selection.
After you save, you need to start a full crawl on your content source, typically Local SharePoint sites.
When this finishes, we can now begin to use our new extracted entities in our search center. The first thing we want to do is add this new extraction to the Refinement web part so that users can refine results by SharePoint feature. Go to your Search Center and issue a query that is going to get you some results that you know you can refine on. In my case, I typed the word SharePoint since I knew all of my documents were related to that. Once you get search results, edit the page. Then on the left side, edit the Refinement web part. Now, click the Choose Refiners button.
This page will list all managed properties marked for refinement. What’s really nice is that when you click on a managed property, it gives you a preview of the available refiners.
In the list of available refiners, I select WordPartCustomRefiner2. Below, you see a list of Sample values and the number of times it found a match. To configure the property, select it and click the Add button. I also added this new refiner up to the top since I want users to see it first.
Now, I gave it a Display name and you have the option to choose a different Display template as well as how it is sorted. You can actually customize how the refiner is rendered on the page with the display template too but that’s outside of the scope of today’s post. At the bottom of the page there is also a Preview button. Click on it and you will see what your search results look like before you even save the page.
If you’re happy with the way the refiners look, save the page, check it in, and publish it. Now issue a query. I am going to use the same one from before.
As you can see, we now see a list of the SharePoint features that we specified in our dictionary. Let’s try a few refiners. Let’s start by selecting Electronic Content Management. I do know that should it read Enterprise, but it was after midnight when I started working on this. :)
Notice, that in the results, that Word Document 3 comes back and you see “ECM” in it. That’s the only text in the document and it matched it to that feature since we defined it in the dictionary. Now let’s try Business Connectivity Services.
It returns several slide decks this time because it mentions it in the Intro deck, the BCS deck, UPS, etc. I’m not sue how Visio Services made a hit to it, but maybe there is something new I need to learn. :)
You can also select multiple refiners. In this case, I selected Apps and Web Content Management.
By now, I am hoping you are seeing the power of entity extraction. It gives you a new level of classification on documents and the users never had to tag anything manually at all.
You can issue queries with these dictionaries as well too. For example, if I want to search for anything tagged with Apps, I would issue the following query.
WordPartCustomRefiner2:"Apps"
I hope after reading this post, it has got you started thinking. Most organizations have next to no metadata on their documents. This isn’t a replacement for taking the time to classify your documents properly with site columns and content types. Inferring metadata will never be as good as the users taking the time to classify the documents themselves. However, it is a great stop-gap in dealing with all of that untagged content you have.
I think this is one of the most significant feature in SharePoint 2013 that most people will probably never use. :( Take the time to set up a few dictionaries and you are going to get immediate value out of search and your users are going to notice.
Although the new Result Source feature in SharePoint 2013 is really cool, writing queries against them is not. With scopes you could specify them by name (i.e.: All Sites or People). However with result sources, you must specify the id not the name. This cane make it tricky to develop custom coded solutions. Luckily, it turns out that the IDs of the built-in result sources are all the same (at least they appear to be) with the exception of the Conversations result source. In comparing an on-premises installation of SharePoint 2013 with a SharePoint Online Preview tenant, here is the list I came up with. Hopefully, it will save you some time when you need to write a query.
Result Source | ID |
Documents | e7ec8cee-ded8-43c9-beb5-436b54b31e84 |
Items matching a content type | 5dc9f503-801e-4ced-8a2c-5d1237132419 |
Items matching a tag | e1327b9c-2b8c-4b23-99c9-3730cb29c3f7 |
Items related to current user | 48fec42e-4a92-48ce-8363-c2703a40e67d |
Items with same keyword as this item | 5c069288-1d17-454a-8ac6-9c642a065f48 |
Local People Results | b09a7990-05ea-4af9-81ef-edfab16c4e31 |
Local Reports And Data Results | 203fba36-2763-4060-9931-911ac8c0583b |
Local SharePoint Results | 8413cd39-2156-4e00-b54d-11efd9abdb89 |
Local Video Results | 78b793ce-7956-4669-aa3b-451fc5defebf |
Pages | 5e34578e-4d08-4edc-8bf3-002acf3cdbcc |
Pictures | 38403c8c-3975-41a8-826e-717f2d41568a |
Popular | 97c71db1-58ce-4891-8b64-585bc2326c12 |
Recently changed items | ba63bbae-fa9c-42c0-b027-9a878f16557c |
Recommended Items | ec675252-14fa-4fbe-84dd-8d098ed74181 |
Wiki | 9479bf85-e257-4318-b5a8-81a180f5faa1 |
Of course this doesn’t help you much for your own custom result sources, but at least you should be able to work with this list for anything built-in. This is especially useful if you want to write queries to do people search or get popular items.
This is one of those posts that I have promised to write but I totally forgot about. What I want to talk about today is passing parameters to SharePoint 2013 Client Web Parts. These parameters are equivalent to the properties on traditional web parts. The process is relatively straight forward, but I thought I would share some of my experiences with it. This article on MSDN will get you started, but I do things a bit differently.
Unfortunately, the updates to the tools with Preview 2 don’t add anything to make this process easier. There are three steps to adding a property to a client web part.
- In elements.xml, add a property element which contains the description, type, default value, and other information.
- In elements.xml, edit the query string of the src attribute of the Content element to pass the parameter to the client web part page.
- Write JavaScript code to receive the value of the parameter.
When it comes to Client Web Part parameters, there are four types:
You will use the above values in the Type attribute of the property you add. It must match exactly. Let’s take a look at the Property element and see what the values do. In this case, we’ll add a string property. In Preview 1, the elements.xml file used to include a commented example, but now that’s gone. Here’s an example.
<Property Name="MyString" Type="string" WebBrowsable="true" WebDisplayName="Ny String" WebDescription="Text description of my string" WebCategory="Configuration" DefaultValue="Default My String" RequiresDesignerPermission="true" />
Aside from the type, the properties that you will probably care about are the following:
- Name – name of the parameter you will query by JavaScript
- Type – data type
- WebDisplayName – name of the parameter displayed to the user in the web part properties
- WebDescription – description of the parameter displayed to the user
- WebCategory – group name in the web part properties
- DefaultValue – the default value if the user does not change it
The Property element looks similar for both boolean and int types as well. However, if you specify enum, the user will see a dropdown list of values. It will contain an EnumItems child element which looks like this.
<Property Name="MyEnum" Type="enum" RequiresDesignerPermission="true" DefaultValue="1" WebCategory="Configuration" WebDisplayName="Enum property">
<EnumItems>
<EnumItem WebDisplayName="Choice 1" Value="1"/>
<EnumItem WebDisplayName="Choice 2" Value="2"/>
<EnumItem WebDisplayName="Choice 3" Value="3"/>
</EnumItems>
</Property>
Once you finished defining your properties, you need to add them to the query string. Effectively, SharePoint passes the values the user specifies by query string to the client web part page. Each parameter comes in two pieces: the value your JavaScript will query and the the name from each Property element. Each parameter looks something like this.
Parameter1=_Parameter1_
The value of the parameter is always enclosed in underscores (_). For the two parameters we added above, here is what our src attribute will look like. I’m using the same app part I created for the Preview 2 blog post.
<Content Type="html" Src="~appWebUrl/Pages/ClientWebPart1.aspx?MyString=_MyString_&MyEnum=_MyEnum_&{StandardTokens}" />
You’ll also notice that I have included {StandardTokens}, this tells SharePoint to provide common useful values such as the URL to the Host Web (SPHostUrl).
Now, we need to add JavaScript to the client web part itself to read the value and do something with it. I use a helper method to help get query string values. I’m pretty sure I got this method from one of the early SharePoint app examples but I’m not sure entirely.
function getQueryStringParameter(urlParameterKey) {
var params = document.URL.split('?')[1].split('&');
var strParams = '';
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split('=');
if (singleParam[0] == urlParameterKey)
return decodeURIComponent(singleParam[1]);
}
}
Using this method, we just request a value for the name of our parameters. In my example below I simple assign the value to a div.
$("#myString").text(getQueryStringParameter("MyString"));
$("#myEnum").text(getQueryStringParameter("MyEnum"));
Now, when we add the App Part to the host web, we can configure it. In the case I gave, you can find the values in the Configuration header.
The values I selected can then be seen in the web part on the page.
When you’re ready to add properties to your client web parts, I hope this post helps. It’s not too complicated but it is a bit tedious. Give it a try and see how it works for you.
During SPC12, Preview 2 of the Microsoft Office Developer Tools for Visual Studio 2012 came out. You can get it using the Web Platform Installer. This made a few improvements in how we build Client Web Parts (App Parts). Earlier this year, I provided a step-by-step on how to create SharePoint-hosted Client Web Parts as well as use the Client Object Model with it. Although you can still follow the steps outlined there, this post will tell you what’s different between Preview 1 and Preview 2.
When you create the project, the first two steps look pretty much the same.
The next step has a new link to allow you to create a developer account with Office 365 Preview.
The good news is, they’ve added a wizard that helps you get stared with client web parts. This wizard does three things for you. It creates an application page for the client web part, it add it to elements.xml, and it registers the CSS files from SharePoint so that the content inside the IFRAME is styled appropriately. Once you have created the project, add a new item, and choose Client Web Part (Host Web).
When you go to the next step, you get a new page in the wizard that gives you the option to create a new page for the client web part.
This is a nice addition because with Preview 1, we had to create this page manually and we had to add the necessary AllowFraming tag as well as everything else. Now, this tag is created for us as well as JavaScript to load the CSS from the host web. This lets you get started right away without having to worry about the wrong styles being used.
Now, if you want to use the SharePoint Object Model, you will still need to follow the steps I outlined in my previous post. This includes adding a JavaScript file for this page and loading the SP.Runtime.js and SP.js files. The wizard also wires up the elements.xml file for you, so that you don’t have to manually type this in anymore.
You will still need to add the properties yourself which I still owe you all a post for, but I promise it’s coming soon. :) When you run the app, and add the app part, it should look something like this.
One thing, that you need to watch out for is that there is a bug that affects Internet Explorer 9. When you add the App Part to the host web with the Visual Studio debugger attached, you will get multiple JavaScript errors. However, if you keep clicking continue, you will eventually get through them. The work-around is to either add the app part to the page in a browser not attached to the debugger or to upgrade to Internet Explorer 10 (or use another browser).
Looking back at my analytics, I found that my People Search in SharePoint 2010 was quite popular. I thought I would update that post for SharePoint 2013. The process is similar but there are a few things to be aware of. People Search requires you to have the User Profile Service Application up and running as well as My Sites. If you don’t have either of those configured, People Search will not work and you will likely see an error like the following in your crawl log.
Error in the Microsoft SharePoint People Protocol Handler. (Invalid URI: The URI is empty.)
User Profile Synchronization Application
Let’s start with configuring the User Profile Service Application. We need this to retrieve user profiles from Active Directory so that search can use them. If you already have your profiles imported, you can skip this section and proceed to My Sites. The nice thing about SharePoint 2013 is that you can set it up the easy way now with the new (well old from 2007) synchronization option called Use SharePoint Active Directory Import. You can still using the full sync if you want, but for those of you that don’t want to mess with the pain you had with UPS in the past, give this a try. You can always change it later. If you already have profile synchronization working, there is no reason to mess with this setting. To get to this setting, go to your User Profile Service Application –> Synchronization Settings. Choose the option Use SharePoint Active Directory Import and click ok. You’ll get a warning mentioning it’s limitations after that.
Once you are done here, go to Configure Synchronization Connections and Create New Connection. Again, you don’t need to do this if you already have synchronization up and running. Enter the name of your domain, provide an account that has credentials. This account does not need to be a domain admin (nor should it be). Click Populate Containers and select the OUs that you want to import and then save the connection. Back on the UPSA page, go to Start Profile Synchronization and select Start Full Synchronization and click OK.
My Sites
We now need to configure My Sites. Typically this is hosted on a new web application but it doesn’t have to be. Now, you need to create a My Site Host. To do this, create a new site collection on the root path (/). Choose the My Site Host template and create the site collection.
Once you have created this web application and set up it’s site collection, you will need to create some managed paths for it. To do this, select the web application that host My Sites and click Managed Paths in the ribbon.
On this screen, remove the existing Sites managed path. Then add a wildcard inclusion for personal and a explicit inclusion for my.
Now, we need to enable Self Service Site Creation. Return to the Web Applications page and click the Self-Service Site Creation. From here, select On and click Ok.
Search
At this point, you may be good to go. However, there are a few settings that you should verify. First, go to the Service Applications page. Select your User Profile Service Application but do not click on the link. Instead click on the Administrators button. Typically, what you will find here is that when the Search Service Application was created, it adds the Search service account to this list. However, if you change the default content access account (as you should), it will not get updated here automatically. Therefore, you need to add your crawl account to this list and choose the Retrieve People Data for Search Crawlers permission.
Now, we are probably ready to crawl, but it’s worth checking the content source to be sure. Go to your Search Application –> Content Sources. Edit your default content source (typically Local SharePoint sites). Ensure that there is an entry with the sps3 protocol handler (i.e.: sps3://myserver) in the start addresses. You only need one sps3 entry and typically it is just the URL of the first web application you happened to create. It does not have to be the URL of the My Site Host.
Once you have confirmed these URLs are correct, you can start an incremental crawl. When it finishes, you can go to you will be able to go to your search center and search for people in your organization. If it doesn’t work, return to your Search Service Application and view your crawl log. There usually is something there to help indicate what the problem is.
Sorting search results has always been an interesting subject in SharePoint. It was actually harder to do than you would think. In SharePoint 2007, all you had was relevance and modified date (write). The KeywordQuery class did not have anything to allow you to sort. You could get around this though by using SQL Syntax though but it was not ideal. As a reminder, you should stop using SQL Syntax now. In SharePoint 2010, Microsoft added a SortList property to the KeywordQuery class however, if you tried to use it, you would likely get the error: Exception from HRESULT: 0x80040E60. This is because, code was added to prevent SharePoint 2010 to sort on anything but relevance and modified date. The only way to get sortable search results was to add FAST Search for SharePoint (FS4SP). That was unfortunate as many people wanted to sort but did not want to make that kind of investment.
Fast forward to today, and now we can take advantage of the SortList property and it really works. Let’s build off of my previous KeywordQuery post. All we need to do is add items to the read-only SortList collection. For example, to sort by author, we would add the following line before executing the query with SearchExecutor.
keywordQuery.SortList.Add("Author", SortDirection.Ascending);
Now the results are sorted by author.
We could also sort in descending order as well. For example to show files largest to smallest, use this snippet.
keywordQuery.SortList.Add("Size", SortDirection.Descending);
Here are the results:
These can be combined as well.
keywordQuery.SortList.Add("Author", SortDirection.Ascending);
keywordQuery.SortList.Add("Size", SortDirection.Descending);
The results then look like:
We can invoke this functionality when using the Search REST API as well. To do this, we use the querytext parameter to issue our query just like before, but we add the sortlist parameter. The syntax of the parameter is “manageproperty:sortorder”. For sort order you must specify ascending or descending. Be sure to include the values in single quotes. We delimit multiple sort properties using a comma (,). Using the above sort example, here is what the query string would look like.
http://server/_api/search/query?querytext='SharePoint'&sortlist='author:ascending,size:descending'&selectproperties='Title,Author,Size,Path,Write'
From the screenshot, you can see the PowerPoint Presentation is also the top result. I’ve also included a few selectproperties to make the results more legible.
I’m really excited that all users of SharePoint search now have this capability. It really adds a lot of value to the solutions that we can deliver. The last thing I will mention is that you must have the managed property you are searching on marked as sortable. I’ll talk about new features of managed properties in a post pretty soon.
Over the past few versions of SharePoint, I have provided a number of blog posts on how to query search. Of those, my posts on how to use the KeywordQuery class using KQL are some of the most popular (2010 and 2007). In continuing that tradition, I am proud to present the “How to” post on using the KeywordQuery class in SharePoint 2013. The good news: your old KeywordQuery code should still work. The bad news: the way you did it is marked completely obsolete. I actually had to dig through quite a bit of documentation to find the right API that wasn’t obsolete. One thing that always provided confusion was that there were similar classes named KeywordQuery in both Microsoft.Search.Query and Microsoft.Office.Server.Search.Query. To reduce some of that confusion, the KeywordQuery class in Microsoft.Search.Query is now marked as obsolete. The new updated class is in Microsoft.Office.Search.Query. Microsoft may have killed the word Office from the name of the product in 2010, but it’s alive and well in the API.
Now let’s talk about what we need to execute some queries with our code. The code here today will work well from farm solutions in SharePoint as well as other .NET applications hosted on the same SharePoint server. We’ll be building a console application today. If you want to query remotely using the object model, then you will need to use the Search Client Object Model which I covered previously. Create a new console application, and then you will need to add a few references. These can be found in the 15 hive in the ISAPI folder. Add the following:
- Microsoft.Office.Server
- Microsoft.Office.Server.Search
- Microsoft.SharePoint
- Microsoft.SharePoint.Security
The classes we need are in the search assembly but you’ll get lots of errors about dependencies missing if you don’t include the others. We then need the following references. This gives us what we need for search, SharePoint, and for the underlying datatable object that is available after we query.
using System.Data;
using Microsoft.SharePoint;
using Microsoft.Office.Server.Search.Query;
Once we have this, we’re ready to get started. There are many ways to instantiate a KeywordQuery object. For simplicity, I am just going to pass a SPSite object for one of my site collections. You can also pass a SPWeb or you can use the proxy technique like I used in my SharePoint 2010 post. We’ll first, get our SPSite object.
using (SPSite siteCollection = new SPSite("http://server/sitecollection"))
We then create a KeywordQuery object using that site collection.
KeywordQuery keywordQuery = new KeywordQuery(siteCollection);
Now, we specify our query using the QueryText field. This is where you can use your custom KQL queries. The documentation team updated the post on this so there are a lot of good tips here. I am just going to search for the word SharePoint.
keywordQuery.QueryText = "SharePoint";
In the past, we would then set the ResultsProvider and ResultTypes that we want, but now we don’t have to. In fact, this whole process takes considerably less code. Instead we use the new SearchExecutor class that I first mentioned in my Client OM post.
SearchExecutor searchExecutor = new SearchExecutor();
We then use it’s ExecuteQuery method passing it our KeywordQuery class.
ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
Assuming it executes correctly, we can then get our search results. Before, we used to use an indexer using ResultTypes.ReleventResults to get the table of results we need. The indexer has been made obsolete, so now we must use the new Filter method. I figured out what it required from the obsolete descriptor of the indexer. For the first value, it needs a string with a value of TableType and since the previous enum is also obsolete, we now use KnownTableTypes.RelevantResults.
var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
It returns an IEnumerable<ResultType>, so we need to filter it. I just FirstOrDefault() to get the table we need.
var resultTable = resultTables.FirstOrDefault();
Previously, we then had to load the data into a DataTable which took a few more lines of code. However, now that is not required due to the new Table property.
DataTable dataTable = resultTable.Table;
Now that you have a DataTable, you can bind it or query it as you see. You can also look at the results using the visualizer in Visual Studio.
Here is the entire code snippet.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using Microsoft.SharePoint;
using Microsoft.Office.Server.Search.Query;
namespace SearchConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (SPSite siteCollection = new SPSite("http://server/sitecollection"))
{
KeywordQuery keywordQuery = new KeywordQuery(siteCollection);
keywordQuery.QueryText = "SharePoint";
SearchExecutor searchExecutor = new SearchExecutor();
ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
var resultTable = resultTables.FirstOrDefault();
DataTable dataTable = resultTable.Table;
}
}
}
}
So if you have a lot of search based code, you may need to do some updates at some point. However, I think these are good changes and simplify the process a little bit. In an upcoming post, I’ll cover some of the new things you can do with the KeywordQuery class.