SharePoint 2007 Author search refiner

10 Dec

SharePoint 2010 has a lot of nice features. One of them are search refiners. The search results can be narrowed down by Result Type, Site, Author, Modified Date, etc.

When knowing and seen that feature I’m really disappointed SharePoint 2007 doesn’t has this. Ok, the Advanced Search page is available, but it’s by far not that easy to use as the search refiners in SharePoint 2010. 

Another thing popped up into my mind when using the SharePoint 2010 search refiners. Why can’t I select for example multiple authors? Not all, not one, but two or three or whatever I like at that moment with that search action. 

Purpose 

These two things: Search refiner in SharePoint 2007 and the possibility to select more than one item
are the starting point of this post.

One other thing: I like SPServices and jQuery, so I’m going to try to use these techniques instead of a farm solution.

So let’s create a search refiner on authors in this post. When the user performs a search and the results are displayed the option to select one or more authors to narrow down the search results will be available. All the different authors are displayed with a checkbox to select one or more and narrow down the search results accordingly.

While writing this post a lot of explaining has to be done so I decided to show it first. After that I’ll explain how to get there.

See it in action 

User types a search text

The results are displayed

The unique authors are listed, the user selects one

Search results based on the author selection and the initial search text:

Now you know what it does, let me explain it. 

Analyzing the search 

When a user types his search text in the standard search box, SharePoint 2007 performs a search by encoding the query and posting it to the results page. That’s basically just it, a basic query.

The Advanced Search page uses SQL syntax and can contain almost every query and result one can possibly imagine. For the purpose in this post we have to use the SQL syntax.

The next step is to analyze the Advanced Search box webpart and how a search is performed.

By typing a search string in the ‘All of these words’ box and performing the search, the results are displayed. When analyzing the source of this page the SQL query can be found.
Searching on the word ‘item’ and the property ‘Author Contains anita’ results in the following SQL query (removed the encoding):

SELECT WorkId, Rank, Title, Author, Size, Path, Description, Write, SiteName, CollapsingStatus, HitHighlightedSummary, HitHighlightedProperties, ContentClass, IsDocument, PictureThumbnailURL  from scope() where freetext(defaultproperties, ‘+item’) And (Author like ‘%anita%’) Order By Rank desc

How does SharePoint know how to format the query like this? How does it know to put ‘item’ and the author search in the right place of the query?

With the help of the IE Developer toolbar:
‘All of these words’ has the name ASB_TQS_AndQ_tb.
The first ‘(Pick property)’ has the name ASB_PS_plb_0, the operator ASB_PS_olb_0 and the value ASB_PS_pvtb_0. The number at the end of the name will increase when multiple properties are used.  For the second property the names ending with _1 are used. The And Or operator when using multiple properties is called ASB_PS_lolb_0 and increases when more than two properties are used. These names are used to make up the SQL query.

There are more controls on this page but only the ones mentioned above will be used in this example.

The code 

To perform the right search, the SQL syntax will be used the way the Advanced Search webpart is using it by using html controls with the same names mentioned above. 

Getting the authors 

When the user performs a search, the results are displayed. The get all the authors of these search results to display the names in a checkbox label later on, the operation QueryEx of the SPServices library is used. 

First the query is set up: 

var queryTextSQL = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>"
queryTextSQL += "<Query>"
queryTextSQL += "<Context>"
queryTextSQL += "<QueryText language='en-US' type='MSSQLFT'>"
//use the same scope (Advanced Search)
queryTextSQL += "SELECT Title, Rank, Size, Description, Write, Path, Author FROM scope() WHERE FREETEXT (DEFAULTPROPERTIES, '+" + sessvars.searchValue + "') ORDER BY \"Rank\" DESC"
queryTextSQL += "</QueryText>"
queryTextSQL += "</Context>"
queryTextSQL += "</Query>"
queryTextSQL += "</QueryPacket>";

To use the SQL syntax the type attribute is set to MSSQLFT.
The sessvars.searchValue is filled with the initial search text which the user typed in the search box.  Sessions variables without cookies are used here. More information can be found here: http://www.thomasfrank.se/sessionvars.html 

After performing a search this search text is displayed in the querystring of the url.

In code it is stored in a variable using $().SPServices.SPGetQueryString() with the following code: 

var queryStringVals = $().SPServices.SPGetQueryString();
var searchItem = queryStringVals["k"];

This value is needed to get it into the SQL query and to put it back in the search box after performing the search. As you know when performing a ‘regular’ advanced search query, the search text is gone and not displayed in the search box anymore. To prevent ‘losing’ the user who performed the search the value will be put back in the search box. 

After setting up the query let’s get the unique authors for the results:

    $().SPServices({
        operation: "QueryEx",
        queryXml: queryTextSQL,
        completefunc: function(xData, Status) {
        k=0;
            $(xData.responseXML).find("RelevantResults").each(function() {
                $(this).find("AUTHOR").each(function() {
                    authors[k]=$(this).text();
                    k++;
                });
            });

            if(authors.length > 0)
            {
                authors = $.unique(authors);
                CreateCheckboxes(authors, "Author", "Contains");
            }
        }
    });

The code fires the query and gets the authors from the results. The CreateCheckboxes method will create all the necessary controls and will be explained in the next part. 

The controls

In this example an author search refiner will be built where a name of an author is the value in the query. To accomplish this, the control (using a checkbox in this example) which displays a single author has to have the name ASB_PS_pvtb_0, the next author ASB_PS_pvtb_1, etc. 

The other names of controls which have to be used are ASB_PS_plb_x (x starting at 0) to set the property name and ASB_PS_olb_x as the operator. Authors are used here, so ASB_PS_plb_x has always the value ‘AUTHOR’ and the operator has always the value ‘Or’ since an item can have one author at the same time. These two controls are hidden controls, since these controls are of no use to the user. Only the checkboxes are displayed. 

The CreateCheckboxes method will set this up for each individual author: 

    function CreateCheckboxes(authors, propertyName, operator)
    {
        var authorContainer= $('#someElementId');
        var j=1;
        $.each(authors, function( iteration, item )
        {
            //add or statement
            if(j==1)
            {
            //<input value="Or" />
            authorContainer.append(

                $(document.createElement("div"))
                .append(

                    createHiddenNamedElement("input","nameprefix$ASB_PS_lolb_" + (j-1), "Or")
                )
              )
            }


            authorContainer.append(
                $(document.createElement("div"))
                .append(
                        $(document.createElement("input")).attr({
                                type:  'checkbox'
                                ,name: 'nameprefix$ASB_PS_pvtb_' + j
                                ,id:    'author_' + item
                                ,value: item

                        })
                        .click( function( event )
                        {
                            var cbox = $(this)[0];
                            if(cbox.checked)
                            {
                                //remember selected checkboxes to put back after postback
                                sessvars['numberOfAuthorsSelected']++;
                                sessvars['numberOfAuthorsSelected' + sessvars['numberOfAuthorsSelected']] = escape(cbox.value);
                             }
                             else{
                                //remember only the selected items
                                var total = sessvars['numberOfAuthorsSelected'];
                                sessvars['numberOfAuthorsSelected']=0;
                                for(var i=1;i<=total;i++)
                                {
                                    if(sessvars['numberOfAuthorsSelected' + i] != escape(cbox.value))
                                    {
                                        sessvars['numberOfAuthorsSelected']++;
                                        sessvars['numberOfAuthorsSelected' + sessvars['numberOfAuthorsSelected']] = sessvars['numberOfAuthorsSelected' + i];
                                    }
                                }
                             }
                        } )
                )
                .append(
                    $(document.createElement('label')).attr({
                            'for':  'author_' + item
                    })
                    .text( item )
                )
                //add hidden input values
                .append(
                    createHiddenNamedElement("input","nameprefix$ASB_PS_plb_" + j, propertyName)
                )
                .append(
                    createHiddenNamedElement("input","nameprefix$ASB_PS_olb_" + j, operator)
                )
            )
            j++;
        } );
        SetSelection();
    }

First the operator ‘Or’ is created. After that, the author checkboxes are created. In the click event the tracking of selecting and unselecting the checkboxes is performed. Then the label is set for the checkbox and last the hidden controls are created to store the property name and the operator.
At last the SetSelection() method will be called. This method just selects the appropriate checkboxes based on the stored values.�
To perform the actual search a search button is needed:

<input onclick='WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("nameprefix$ASB_BS_SRCH_1", "", false, "", "results.aspx", false, false))' type="submit" value="Search" />

The url has to be set to the current page so the results, according to the selected authors and the search text provided, are displayed in the search results webpart on the same page. 

One last important issue

All properties are treated as numbers by default. When performing a search with the code created right now, an error will occur when selecting an author and perform the search:
Invalide parameter: Author. Expect a number. ITIDEA\anita.boerboom is given instead.’ 

To tell SharePoint to treat the Author property like text the following control has to be added to the page with the name ASB_TextDT_Props :

<input type="hidden" name="ASB_TextDT_Props" id="Hidden4" value="Author" />

When multiple properties have to be treated like text the values can be separated by #;#, e.g. Title#;#Author 

When properties have to be treated like dates, use ASB_DateTimeDT_Props the same way as the text property described. 

Summary 

Before creating a search refiner a lot of investigating was necessary, but it can be built for SharePoint 2007 fully in script. 

Also this example can be extended to show the number of items per author and of course other refiners can be built on other properties.

3 Replies to “SharePoint 2007 Author search refiner

  1. Pingback: Tweets die vermelden SharePoint 2007 Author search refiner -- Topsy.com

  2. Terrific work! This is the type of information that should be shared around the web. Shame on the search engines for not positioning this post higher!

Comments are closed.