August 2009 - Posts

I have posted in the past about how to create a scope display group using the API, but I think new users to Enterprise Search could use some information on the basics of working with scope display groups.  The begin with we need to start off by defining what a scope display group is.  I’m assuming you know what a scope is already (it’s just a subset of your search index defined by rules you define).  A scope display group is simply a grouping of scopes that can be used with the SearchBox web part.  When you first, create your search center, you get a search box that looks something like this.

EnterpriseSearchBox

This works great, but I have found that my users like to be able to quickly pick what they are searching for.  Out of the box, this will search the entire index and that is just not good enough if users are just trying to find out what Bob Smith’s phone number is.  The user doesn’t necessarily want to see every document that Bob wrote.

What I want to do is give the user a preset list of scopes to choose from (i.e.: People, Documents, Sites, etc.).  This is where scope display groups come in.  A scope display group will allow us to pick which scopes we want to show the user and then we can configure that group for use with the SearchBox web part.  To do this, we go to our Site Collection settings and click on the Search Scopes link on the right.  Here, you will see a list of all your scopes and what groups they currently belong in.  To create a new one, use the New Display Group button.  Creating a scope display group is quite easy.  Just give it a name and select which scopes you want to include.  You can set the order you want the scopes to appear in as well as set the default scope.  Here is what my group looks like.

EnterpriseSearchScopeDisplayGroup

That’s great.  We now have a scope display group, but how do we use it?  Go to the default page of your search center and edit the page.  Then edit the SearchBox web part.  The first parameter we need to set is under Scopes called Dropdown Mode.

EnterpriseSearchScopesDropDownMode

As you can see this list has a number of parameters.  The default value is Do not show scopes dropdown.  The other options in the list refer to what the default value of the scopes list is when you see it and whether or not to include contextual scopes (i.e. search this site / list).  I usually go with Show, and default to ‘s’ URL parameter or Show, do not include contextual scopes, and default to ‘s’ URL parameter.  What it means by default to the ‘s’ URL parameter is that it will default to the value of the scope that was passed in via query string.  Remember, search center works by passing everything in via query string (including the scope), so if you do not tell it to default to the ‘s’ parameter, it will use the default value you specified in the scope display group.  The default value here doesn’t matter as much here but you will need to make these same changes to the SearchBox web part on the results page as well.  That way when users search for additional terms on the results page, it has the same scope.

That is how we tell the SearchBox web part to show a list of scopes, but we have to tell it which group to display as well.  Under Miscellaneous, look for the last property called Scope Display Group.  Type in the name of your display group, mine is called Search Dropdown and save the changes to the web part and page. 

EnterpriseSearchBoxScopeDisplayGroup

Your search page, should now look something like this.

EnterpriseSearchBoxDropDownList

As you can see the changes you need to make are pretty simple.  This is a quick way to give your users more value out of your existing search center.  As a reminder, you can create scope display groups using the API as described in my post.  The Office Server SDK has some of this info as well but doesn’t go into a ton of detail.  That is why I expanded on it in this post.  That and I know people like pictures. :)  I have to say I am a big fan of the Windows 7 snipping tool.  I’ve used it a ton in taking screenshots.

Follow me on twitter.

I posted in the past how to do a simple search engine friendly redirect using the 301 HTTP status code.  I thought this was always something that needed to be included in the framework and it looks like it finally will be in .NET 4.0.  I saw this mentioned in Scottgu’s post about multi-targeting and thought it was worth mentioning today since I have posted on it in the past.  To do a 301 redirect, use the new RedirectPermanent() method on the response object.  It takes the same parameters as the Redirect() method.  Here is what it would look like.

Response.RedirectPermanent(“/SomeOtherPage.aspx”);

This type of redirect obviously doesn’t really matter for internal sites, but for public facing sites, if you are relying on a redirect somewhere, it is best to do it with a 301 as the search engines don’t really care for the standard 302 redirect.  At least they didn’t at one time.  I am not SEO expert.

Follow me on twitter.

I see this topic come up quite a bit.  You have a set of search results, and the user just wants to see the documents, not the folder that contains the documents.  Consider the following example, when I query for accounting documents below.

EnterpriseSearchFolderResults

As you can see in the results above, the first item returned is the folder named Accounting which contains the documents listed after it.  Many times, people do not want this showing up in the results, so we need to come up with a way to remove it.  The easiest way of course is to use the IsDocument keyword and pass it a value of 1.  This will give you results like the ones below.

EnterpriseSearchNoFolderResults

As you can see, this works great, but you don’t exactly want to have to explain to the user how to type in IsDocument:1 into the search box.  However, it is quite easy to include this if you are building your own advanced search control.  One way to handle this is to create a custom scope that just returns documents.  This is quite easy to do but there are a few steps.  The first thing we need to do is allow the IsDocument managed property to be used in scope rules.  To do this, go to Search Administration –> Metadata Properties and then find IsDocument in the list.  Check the Allow this property to be used in scopes checkbox and hit ok.

EnterpriseSearchIsDocumentManagedProperty

After making changes to any property in search, you of course need to perform a full crawl.  This could take minutes or days depending on the size of your search index.  Once you are done, go to the Scopes page and create a new scope.  For the purpose of my example today, I’ll just call mine Documents.  After you create the new scope, you need to add a new rule.  Choose Property Query and then select IsDocument from the list.  Enter a value of 1 and choose Require.  Now whenever you query against this scope only documents will be returned.  Perform a scope update when you are finished or simply wait for the timer job to update it whenever it is scheduled.

EnterpriseSearchIsDocumentScopeRule

At this point you are ready to use your new scope in the Search Center.  There are a number of ways to include this such as putting it in a scope display group and giving the user a choice of scopes to use, but for today’s example, I am just going to use the Scope parameter on the CoreResultsWebPart (CRWP).  Edit your results.aspx page and then edit the CRWP.  Under Miscellaneous, type in the name of your scope in the field and then save your changed to the web part and page.

EnterpriseSearchCRWPScope

At this point you are ready to try our your new search results.  If all works successfully, you should have search results like this.

EnterpriseSearchFolderResultsScope

As you can see, it’s pretty simple to set this up.  If you don’t want all of your queries to be for documents only, you can customize the search box web part to display a list of scopes or something like that.  I’ll probably cover how to do that in a future post (although it’s pretty easy).

Follow me on twitter.

A while back, I wrote a post on how to customize the search results page.  I focused on some of the options with the CoreResultsWebPart (or CRWP as I call it), but today I thought of some more things to discuss in regards to the XSLT document that is used to display the search results.  If you want to know how to add your own managed properties to the search results page, be sure and check out the link above.  What I am going to talk about today is what some of the XSL templates do.  Before you begin, I recommend saving a copy of the XSL you are working on and putting it in source control.  It’s quite easy to mess up what you have and then nothing renders at all.  In the worst case scenario, you can always create a new results page and copy over the XSL from there.

The first template to talk about is at the top of the document and is called dvt_1.noKeyword.  This is the template that is displayed whenever the user submits a blank search query or when fixed keyword querying is turned on and no keywords have been specified.  Here is what this section looks like. 

<xsl:template name="dvt_1.noKeyword">

  <span class="srch-description">

    <xsl:choose>

      <xsl:when test="$IsFixedQuery">

        <xsl:value-of select="$NoFixedQuery" />

      </xsl:when>

      <xsl:otherwise>

        <xsl:value-of select="$NoKeyword" />

      </xsl:otherwise>

    </xsl:choose>

  </span>

</xsl:template>

Since the CoreResultsWebPart supports some form of variation, the text it displays comes through the variables $NoFixedQuery and $NoKeyword.  In English, here is what that text looks like.

Variable Value
$NoFixedQuery Please set the 'Fixed Query' property for the webpart.
$NoKeyword Enter one or more words to search for in the search box.

If you need to customize the text, the quickest way is just to remove the xsl:value-of and put your own text in.  I am sure you can customize the values of those variables, but that falls out of the scope of today’s post.

The next section, I want to mention is dvt_1.empty.  I find that this is one of the most common sections, people want to customize as it is what is displayed when there are no matching search results.  This template also displays alert me and RSS links which you probably don’t need to customize.  The section you will be most interested in is in the span with an id of CSR_NO_RESULTS.

  <span class="srch-description" id="CSR_NO_RESULTS">

    <xsl:value-of select="$NoResults" />

    <ol>

      <li>

        <xsl:value-of select="$NoResults1" />

      </li>

      <li>

        <xsl:value-of select="$NoResults2" />

      </li>

      <li>

        <xsl:value-of select="$NoResults3" />

      </li>

      <li>

        <xsl:value-of select="$NoResults4" />

      </li>

    </ol>

  </span>

</xsl:template>

As you can see the variables $NoResults, $NoResult1, etc… are used here to give more information to user on how to find what he or she is looking for.  This is the section you want to edit if you need to provide different information to the user on how to search.

The dvt_1.body template adds links around the search results, and sets up the basic table layout that the typical search results page uses.  This is where the links are to change the sort order, add an alert, or view RSS. 

The Result template displays each search result.  The post I wrote on customizing search results has more information on editing this section to display additional managed properties.  If you need more of a grid layout for search results, you can change the table formatting here and in the body template to put the results in table cells instead of in a single row.  If you are looking to match the style of other text that is rendered, using a div with a class of srch-description works well.

There are also a few other templates used to display strings, the file size, as well as do the hit highlighting.  I haven’t found a need to customize that yet.  Most of this information is pretty obvious, but I have seen some posts out there on the topic, so I thought this might be a good reference.

A colleague asked me today how he could formulate a search query by content type today.  I figured it was as simple as doing  a keyword query by content source, but wasn’t sure so I decided to go check.  I took a look and there is a ContentType managed property so I decided to use it as in the following example.

ContentType:”Company Document”

In my example, I wanted to return all documents that were of type Company Document.  This in fact worked exactly as expected as you can see in my screenshot below.

EnterpriseSearchContentType

This means you can of course display the content type in your search results if you want by modifying the selected columns XML document of the CoreResultsWebPart.  For more information about customizing search results, see this post.  This is just a quick post today, but hopefully it will help you if you ever need to query in this manner.

So you become a pro SharePoint developer, you’re great at making solution packages, but now you’re feeling lazy and want to cut out a few steps when you’re just making some changes during development.  This post is for you.  In reality, the best way to deploy your web parts and other custom code is using a solution package with a feature.  Deploying solution packages can take quite a bit of time.  If you’re just changing one line of code, do you really need to go through the hassle of deploying a solution package every time?  The answer is in fact no as long as you are careful and are willing to deal with the consequences should anything go wrong.  Most of the scenarios we are talking about today assume you have a solution package built and you have deployed it at least once.  We’re just talking about how you can make development faster on subsequent code changes.

One thing I will point out before we start is that if you are in a farm environment, your changes must be deployed to all front end servers.

Web Parts

Updating a web part is pretty simple.  All you need to do is copy the assembly to wherever you deployed it to (GAC or bin folder).  Your .webpart file does not need to be updated unless you have made changes to it.

Web Parts Deployed to the GAC

Although, I try to put as little in the GAC as possible, some things just have to go there.  If you have made a change to your web part’s code, you can easily just copy the assembly out of your project and put it straight in the GAC via the C:\Windows\Assembly folder.  Don’t forget that SharePoint caches assemblies in the Global Assembly Cache, so you need to recycle your application pool of any web application that is using your web part on each front end server.  You can just do an iisreset, but an application pool reset is a bit faster.

Web Parts Deployed to the bin folder

If you are deploying to the bin folder, ghetto deployment is even easier.  Just copy out the DLL to the bin folder of your web application and you are good to go.  No application pool reset is necessary.  If you do make significant changes and your code requires different permissions, then you will need to deploy with your solution package so that you can specify an updated CAS policy.

Pages and User Controls

If your code behind changes, then the rules above for web parts apply.  Just copy out the binary to the deployment location.  If you made changes to a .aspx or .ascx file, these files can simply be copied out to the 12 hive (LAYOUTS folder for pages and CONTROLTEMPLATES folder for controls).

Event Receivers

The binaries for your event receivers can also be copied out directly to the GAC.  Just like with web parts, remember to reset your application pool to avoid caching issues.

Workflow

Workflow can also be deployed directly to the GAC.  If you have any changes to what forms are registered or anything else you will need to deploy it regularly though.

Some of this may have seemed obvious, but I have had a few developers ask me questions like this when starting out.  Of course if anyone goes wrong, just start open, retract your solution and redeploy it the correct way.  Hopefully, though you can use some of these tips to speed up development when you are making lots of changes.  No one likes to sit there and wait for a solution to deploy.  Have any other tips?  Leave a comment.

with 6 comment(s)
Filed under: ,

I needed to do a quick proof of concept of displaying search results in Silverlight recently and it actually proved easier than I thought it was going to be.  I tried this once before in a beta version of Silverlight 2 and it was much more difficult at the time.  I was able to easily add the reference to the search web service but the proxy was filled with classes that were not supported in the Silverlight .NET Framework.  This time I was relieved to find that it was much simpler.  I’ve written many applications to query using the web service, so the first thing I thought of was how is this thing going to authenticate to SharePoint.  I started doing tons of unnecessary research when I should have just written some code.  When you create a reference to the web service, you do not have a credentials property you can set like you could in a regular .NET application.  It turns out that it just works provided you do all of the other necessary things to call a web service first in Silverlight.

For the purpose of today’s post, we are going to assume that whatever page is hosting the Silverlight application is running under an account that can access SharePoint (i.e.: Cassini or from SharePoint itself).  I started by creating a reference to the web service at the usual location (/_vti_bin/search.asmx).

SilverlightSearchWebService

I thought I would need to mess with the security element in the ServiceReferences.ClientConfig file, but in fact you can just leave it alone.  The next thing you have to do is create a cross-domain policy file so that Silverlight knows it has permission to call the web service.  Here is what the file, ClientAccessPolicy.xml, looks like.  You can customize it for your needs of course.

<?xml version="1.0" encoding="utf-8"?>

<access-policy>

  <cross-domain-access>

    <policy>

      <allow-from http-request-headers="*">

        <domain uri="*"/>

      </allow-from>

      <grant-to>

        <resource path="/" include-subpaths="true"/>

      </grant-to>

    </policy>

  </cross-domain-access>

</access-policy>

Creating the file is quite simple, but you need to get the file into SharePoint.  This file has to be located at the root of the web application (not site or site collection).  Of course since it is SharePoint, you can’t just go and drop the file into the 80 folder in wwwroot.  So I found, one of the easiest ways to add the file is using SharePoint Designer.  Open it up, connect to your site, and then drag the file onto the root of your site (or create a new one from the menu).  When you are done, it should look something like this.

SilverlightSearchClientAccessPolicy

Now, we get to write code.  Add whatever controls you want to in your XAML file.  You can use Expression Blend to do this or just edit the file manually.  You’ll need a textbox or something to contain the results of the search query.  WCF calls in Silverlight have to be executed asynchronously, so you need to write an event handling method to the QueryEx call like this.

QueryService.QueryServiceSoapClient queryService = new QueryServiceSoapClient();

queryService.QueryExCompleted += new EventHandler<QueryExCompletedEventArgs>(queryService_QueryExCompleted);

To call the web service it works very much like any other .NET application.  Build an input XML document and make a call to QueryExAsync.  In my case I actually get the input from a textbox so that the user can provide the value to query on.

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("<Range>");

queryXml.Append("<Count>50</Count>");

queryXml.Append("</Range>");

queryXml.Append("<Context>");

queryXml.Append("<QueryText language=\"en-US\" type=\"STRING\">");

queryXml.Append(QueryTextBox.Text);

queryXml.Append("</QueryText>");

queryXml.Append("</Context>");

queryXml.Append("</Query>");

queryXml.Append("</QueryPacket>");

 

queryService.QueryExAsync(queryXml.ToString());

We still need to implement the queryService_QueryExCompleted event handling method to get the results and display them in the textbox.

void queryService_QueryExCompleted(object sender, QueryExCompletedEventArgs e)

{

    ResultsTextBox.Text = e.Result.Nodes[1].ToString();

}

Now one thing I have noticed here is that the results XML string does not look exactly like the one that would normally come back as if it was called from a regular .NET application.  I am know Silverlight or WCF expert but I am sure there is a reason for this.  What ends up happening is that e.Results has a Nodes collection with 2 XElement in it.  The first one contains mostly schema information and the second one contains the actual results.  However, some initial data at the top of the tree (such as result count and page size) really is nowhere to be found (from what I can tell).  At this point, you are good to parse and bind the XML data as you please.  Here is what my ugly Silverlight application looks like.

SilverlightSearchScreenshot

I have attached the code to this post, so that you can use it as a starting point.  If you run into issues, I warn you Silverlight won’t give you a lot of help.  I recommend checking out this post on Debugging Web Services in Silverlight from the Silverlight team.  This is a bit of work to do now.  I can definitely say I am looking forward to the Client Object Model (publically announced a few weeks ago) in SharePoint 2010 which should make this easier..

I’ve seen quite a few posts on jQuery and SharePoint lately. I haven’t seen too many people point this out (maybe I didn’t search well enough :) ), so I thought I would take a quick minute to remind you to use jQuery’s noConflict() method in SharePoint.  It appears somewhere in all of the magic JavaScript that powers SharePoint it too also makes use of the $ shortcut.  If you don’t simply having a reference to the jQuery script on a page can cause all number of things to break.  I have seen it break my own JavaScript as well as cause certain things not to display such as the Edit Web Part pane.  The next time you want to use jQuery with SharePoint, just add the following code and you should be good to go.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>

<script type="text/javascript">

  jQuery.noConflict();

</script>

Instead of using the $ to use jQuery methods you will have to use jQuery instead.  For example: $.get() would become jQuery.get().

with 2 comment(s)
Filed under: ,