Introduction

The built-in Windows SharePoint Services search results page (SearchResults.aspx) is rather limited in the document metadata that it displays in the result list. Furthermore, the format of the list is not what every client wants. My client wanted lots of document metadata displayed in a "table", and only wanted results from a single document library in the site. So, I created a custom search page to implement this functionality. To display the results, I used a DataGrid, but you could use any formatting you want.

This example shows how to create a custom search results page that will display any metadata available for a document. It is totally flexible in that it gets the metadata fields to display from a client-configured document library view. This means that if the client decides s/he wants more (or fewer) categories to display, or wants to change their order, the results page will automatically adjust when the document library view is changed. This solution uses a simple Content Editor web part with some client-side HTML and JavaScript to invoke the search. This leaves the original search box and search results page for normal site-wide searches (which includes multiple libraries and lists).

Summary of Features:

The search form is implemented as a simple Content Editor web part, containing only client-side HTML and JavaScript.

The search itself is implemented by creating a custom search results page, based on the pre-existing SearchResults.aspx, which uses the built-in SharePoint search functionality and manipulates the results to display more metadata than the standard search results page.

The metadata to display, and the order to display it in, can be changed using a standard SharePoint view on the document library to be searched.

The search results page provides a form field for immediate re-search without having to return to the originating page.

Results can be sorted by clicking on any column header.

Clicking on the document title itself (or the icon) takes the user to the document’s property page.

Built-in search functionality is not affected.

Sample results page:

Background

In order for you to understand and apply the contents of this article, you need to know your way around SharePoint - both in custom development, and in standard configuration. In particular, you need to know how to create a custom view for a document library, and how to add and configure a content editor web part. These are standard configuration processes not requiring any hard-core development.

This article also assumes you know a little bit about the SharePoint object model.

Using the Code

There are three main steps to implementing this solution: creating the custom search aspx page, configuring a "view" on a SharePoint document library, and adding a Content Editor web part to a SharePoint page. We will cover them in decreasing order of complexity.

The ASPX Page

To begin, make a copy of the original search results page. In a default installation, this is found here: C:\Program Files Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\LAYOUTS\1033.

You can give it any name you want, such as CustomSearchResults.aspx. By copying the original, you get all the "chrome" that should be on a standard SharePoint page, to ensure your page looks like part of the site. Be aware that a re-install of SharePoint will likely overwrite this directory, wiping out your file, so be sure to save a backup somewhere!

All of our code has to go in the aspx page itself (no code-behind), since we can't rebuild SharePoint as a web app.

The page will read two values from the query string (passed from our custom search form on another page). These values are:

CRSearchString - the string the user wants to search for

Target - the fully qualified URL of the site to be searched (current site)

Once you've created a copy of the page, do the following:

Modify page directives/includes:

EnableViewState must be set to True

Remove the “Inherits From…”

Add references to the System, System.Collections, System.Data, and System.Web namespaces

Strip out all server side code and controls

Add a server-side DataGrid control (or whatever list format you desire for the results)

In a server-side script block, do the following:

Get querystring values

Create DataGrid columns using the fields in the “Search Results” view

Invoke the search on the document library (using querystring values)

Filter out results not from the Content Repository library

Bind the results to the DataGrid columns

Specify the sort method

Add a client-side text box and button, with the same format as in the calling content editor web part

In the client-side JavaScript, insert the same code as in the calling content editor web part, for re-search

Let's go into more details on step 4, shall we? There's actually a lot more happening in that server-side code block!

There are four server-side methods: Page_Init, Page_Load, BindData, and DataGrid1_Sort. We shall discuss each one in detail.

Page_Init Method

This method is used to dynamically create the DataGrid columns from the fields found in the search results view. Using the Target parameter of the querystring, it creates a SPWeb object pointing to the current site. Then, using the the Lists collection of the SPWeb object, we get a reference to the document library to search. I've hard-coded the library name, but you could just as easily pass in the name of the document library as another querystring parameter. Then, we get the fields from the search results view. It is the sort view that determines the metadata to be displayed and its order - making the results format completely dynamic.

One thing to note is that the fields collection returns the internal field name - not the display name. The internal name is perfect for manipulating the DataGrid rows, but not what we want the user to see as column headers. So there's a bit of extra work to get the display name of each field in the DataGrid columns.

Another important item of note regards the sorting capability of the DataGrid. Because the Title field is formatted with the HTML anchor tag for the document (using the document ID in the URL), if you sort on this column, the grid ends up being sorted by document ID, not by title. So a hidden column allows sorting by the actual document title, not the URL.

We loop through this collection, saving the document URL (which contains the document ID, to be used later) and the URL to the document icon (to be displayed in the grid). Because the search will have returned items from throughout the site, and we only wanted to search documents in a particular document library, we only keep items that have the specified document library address in their URL:

There are a couple of things to note in the above code. First, multi-valued fields have their values in a string, separated by "x;". Even if there is only one value, it will begin with those characters. So we strip those out, but leave a semicolon in between values. Secondly, there are three "special" columns that require some extra tweaking:

Document icon - We have to format it so that it will display the actual image, and display it as a link.

Document title -It must be hyperlinked to the document property page (which is why it can't be the value that we do the sort on).

Sort by title - This is the hidden column, which contains simply the document title, and it is used when the user clicks on the "Title" row, to sort the grid by title.

The last bit of code in this method places the DataTable into a DataView, applies the sort (if any) to the DataView, and then binds the DataView to the DataGrid.

Page_Load Method

This method is very simple. All it does is call the BindData method with an empty sort parameter, when the page first loads.

DataGrid1_Sort Method

The last method in the server-side code block is the method to apply the sort. It simply calls BindData and passes the name of the column to sort on (whichever column the user clicked on):

Client-side Custom Code

The only other custom code on the page is the client-side JavaScript to allow the user to re-search. This is the same code you place in the content editor web part on the page to start the search process. It's very straightforward:

Note that if you place this code on a standard SharePoint page, don't name the textbox "SearchString". This is the name of the textbox used by the built-in SharePoint search box, and the function will grab it, instead!

To use this code, insert it in a client-side script block. Then, place an HTML textbox and button on your page, and wire up the button to call the above method. Do not place these in a client-side <form> tag as you normally would. (You can't use client-side <form> tags on SharePoint pages.) This is not well-formed HTML, but it does work.

Because this custom page has all of the HTML for the "chrome" of your site, your custom code (the HTML, the DataGrid, and whatever else you want to place on your page) goes in a table cell near the bottom of the page. The sample aspx page included with this article has this section clearly marked.

Document Library View

Creating a view on a SharePoint document library is very simple. From the document library page, click the "Modify Settings and Columns" link in the left-hand action menu. On this page, you can specify the metadata for the documents, and at the bottom of the page is the link to allow you to create a new view or modify an existing view. You can select the columns to be displayed, and also specify their order. Create a view called "Search Results" (or whatever you want - just make sure the Page_Init and Bind_Data methods refer to this view). Any time you want to change the metadata displayed on the results page, simply change this view.

Content Editor Web Part

To invoke the search from another page on the site, simply add a content editor web part, placing the JavaScript function and the HTML textbox and button in it. You can add any other content/formatting as you choose. To add a content editor web part to a page, click the "Modify this page" link, then choose "Add Web Parts | Browse...", and drag the content editor web part onto your page. Use the source editor to add your HTML and JavaScript.

Points of Interest

When I first started this project, I thought it would be fairly simple - just take the object returned by the search tool and access the other metadata fields. Then I discovered that the object returned by the SharePoint search engine does not include all the metadata for the documents. Hence the need for using the ID obtained from the search result object to obtain the actual document object, which does contain the metadata.

Adding somewhat to the complexity, was using the sometimes awkward SharePoint object model. For example, dealing with both the internal name and the display name of the metadata fields led to code like this: ResultsRow[InternalViewFldNames[j]] = ThisItem[ListFields.GetFieldByInternalName(InternalViewFldNames[j]).Title];.

Another "gotcha" was the problem of sorting the grid by title. During testing, sorting on all the other columns was fine, but the Title column was not sorting properly. Since the sort is a method of the DataGrid object, I was initially stumped. But when I realized that with the anchor tag containing the URL to do the document, if the column was sorted "alphabetically", it was really sorting by ID. But by adding a hidden column (as described above) that contains simply the document title, and making the search action of the Title column invoke the search on the hidden column, the grid is sorted properly by title.

History

06.22.06 - Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Yes it is "possible". How to implement it depends on many, many factors. Think about having the search results page determine which department the user is a member of at the time of the search request (maybe via audiences, AD groups, URL), and display the results appropriately.

Thanks very much for your comment!Each department in my company already have their own sharepoint page with an advanced search web part on it.But all the web parts are poiting to the same search results page _layouts/OSSSearchResults.aspxMy question is: can I copy that file and just rename it for each department? Must be in the same folder?After link the advanced search web part with those files, Will I be able to edit the properties (XSLT)?

I think you should be able to just copy the page for each department, customizing it as needed, and then just change the search link from each department page. You should be able to have these pages anywhere.

You don’t necessarily have to place a custom search result page in the _layouts folder.I wanted to have a search result page that didn’t use the application.master because I wanted to keep the navigation (quicklaunch) etc.So I created a standard webpart page, stored it in a document library and opened it with SharePoint designer.

To use the new custom search result page I used the javascript provided in the above solution and (obviously) changed the location of the search result page.The search results are being displayed nicely.

This is a great code example. However, the SPSearchResultCollection class is now deprecated and it appears the FullTextSqlQuery class does not fit well with this code example. I am trying to build a custom search results webpart to display results from a FullTextSqlQuery search of my own. Would you happen to have an updated example or can maybe point me in the right direction?

I know it's been a while since this article was written, however I am getting this same erro as the earlier message. I did create a view called "Search Results", however I am getting the error on this line:

SRView = ContRep.Views["Search Results"];

Can you make any suggestions as to how I cna start to track this down?

Hmmm. A couple of thoughts:1. Are you using Sharepoint 2003, or WSS 3.0/MOSS? No guarantees that this will work in 3.0/MOSS.2. Make sure that "Search Results" is the actual (internal) name of the view.3. Are you trying to pull info from a different site/web/area? That is, do you have the web part in one location, and the list view in another?

1. Is it WSS 2.0 on SBS 20032. I am not sure how to check that, when I go into the view in "Edit" mode, the "View Name" box says "Search Results".3. WSS and the App are on the same server, the application is at http://server:99/searchtest.

I can pull in results using this code, the search runs successfully. So I do not think it is a permissions problem...:

Anotehr question. When I change the view in the code to "General Documents" I get by the error and get a lsit of the field names, however for some reason I am only getting 5 when there are 7 columns:Type, Name, Modified, Modifed By, Checked Out To, Created By, Title

Yes the view displays all 7 columns. I am also having an issue with adding columns to the datagrid and something I just realized is that I was trying to do this from a code-behind file...do you think that's the problem?

First of all, this article refers to WSS 2.0, not 3.0 - no guarantees it will work with 3.0. Don't know which one you are using, but that could be the problem.

Given that error message, it could be a number of places - anywhere you are using an array or collection reference. Did you create the Search Results view? Did call it "Search Resutlts"? In all the code examples, are you replacing my dummy 'doclibraryname' and such with your actual values?

I have also created a "Search Results" view in the Document Library called Library.

When I try a search, either nothing happens (i.e. the page with the web part is just reloaded) or I get Error - Value does not fall within the expected range. The URL appears to get generated from the search is as follows:http://vmwin2k3sr2/_layouts/1033/searchresultsCR.aspx?CRSearchString=Test&Target=http%3A//vmwin2k3sr2/Library