Creating Custom Search Applications

[Previous] [Next]

Site Server provides some powerful indexing and search capabilities. However, Site Server does not provide a targeted way for users to search the information contained in its catalogs. Fortunately, Site Server has an object model that you can use to build search applications that allow users to target the information in their searches. We'll take a look at this object model and the different user interfaces from which you can use it to allow users to search Exchange Server information.

Site Server Search Object Model

Site Server includes an object model that makes building your search applications easier. This object model consists of two objects: Query and Util. The ProgID for the Query object is MSSearch.Query; for the Util object, it's MSSearch.Util. While the Util object library is interesting, only a few of its methods are useful for Exchange Server developers. Therefore, we won't look at the Util object library; instead, we'll focus on the Query object library because this is primarily the one you'll use to build search applications. Figure 17-5 shows the object model for the Query object library.

click to view at full size.

Figure 17-5. The Query object library with methods and properties.

Query Object Model

Using the Query object model, you can create powerful search applications. You use the Query object model to create and execute queries against a Site Server catalog. This object model extends the ADO object model, providing a mechanism by which you can count, retrieve, and view the results of your queries.

Let's take a look at the properties of the Query object.

  • Allow Enumeration. By setting this property to True, you allow your query to enumerate parts of the catalog recursively. This means you can perform more complex queries—but at a performance cost. This property defaults to False because most searches can be performed without enumeration. Set this property to True only in rare instances such as when using multiple wildcards or advanced regular expression searches.
  • Catalog. This string property specifies the Site Server catalog you want to search. If you have selected a default catalog, you don't have to set this property. You can specify multiple catalogs to search by separating their names with a comma.
  • Columns. This string property specifies the columns from the search catalog that you want to display in your search results. The names of the columns are case insensitive and should be separated by commas.
  • LocaleID. This number property specifies the Win32 Locale ID. Based on this number, Site Server uses the word breaker and word stemmer of that language for your search. If you're building Web-based search applications, this property will default to the Locale ID of the browser.
  • MaxRecords. This number property allows you to limit the number of search results returned per page. By default, this property is 0, which returns all search results. You should definitely modify this property in your applications if you want to perform your queries faster. For best results, set this property to 25 or less to help your users limit their searches to items that most closely match their criteria.
  • OptimizeFor. This string property tells Site Server how to optimize its searching of a catalog. You have a few different options for this property that will affect the results displayed for your search. The default values for this property are recall and hitcount. Let me explain what recall means. When you specify recall, results are trimmed when the query is executing, which might make performance slower but will result in an accurate hit count. (Result trimming is the removal of items that don't match the criteria or for which the user doesn't have authorization to view.)
  • Instead of recall, you can specify performance, which conducts its security checks after the maximum number of hits is calculated. You might get an inaccurate hit count total because the user might not be able to view some of those hits due to the lack of authorization on the items. You can alternate between performance and recall, but you cannot use both in this property at the same time.

    The hitcount and nohitcount values specify whether you want to calculate the total number of matches found. If you specify hitcount, performance will be a little slower because the entire catalog must be processed before displaying the first result.

  • Query. This string property specifies the query you want to use to search the catalog. For example, this property might appear this way:
  •  "( @FileWrite < 1999/11/26 AND > 1999/11/ 24 ) & ( @DocTitle Outlook ) _ & ( @MessageClass IPM.Note.* Or IPM.Post* )" 

    Usually in your application you won't generate the string for the Query property directly. Instead, you'll use the conventional query string variables, which we'll discuss later in this chapter.

  • StartHit. This number property can be a single number or an array of numbers, depending on whether you're searching multiple catalogs in a single query. The StartHit property specifies the starting result number for each catalog and is commonly used with paged results. StartHit allows you to determine which result to start with if more results are available than displayed in your search application. For example, if you display only 50 results at a time and provide a More Results button in your application, you should set the StartHit property to 51. This property is used in conjunction with the ADO extended property NextStartHit, which you'll see later in this chapter.
  • SortBy. This string property specifies which columns you want to sort by and whether to sort in ascending or descending order. You can sort only by columns that are retrievable by Site Server. The catalog schema specifies whether a column can be retrieved. Site Server supports sorting by up to four columns. You specify each column using its name followed by either [a] or [d] for ascending or descending and use commas to separate multiple columns—for example, Rank[d], Title. Try to sort by rank or another column that applies to the type of items your users are searching; otherwise your sorting might not make sense to users of your application.
  • QueryTimedOut (read-only). This property is available only after you use the CreateRecordset method. This property returns True if the query exceeded the time limit you specified for the catalog and False if the query didn't exceed the time limit.
  • QueryIncomplete (read-only). This property is available only after you use the CreateRecordset method. Also, it should be used only if the enumeration property is set to False. If enumeration is set to True, QueryIncomplete always returns False. This property can tell you whether the query was resolved. A query might not be resolved for a number of reasons, such as being too complex or referring to nonexistent columns. If you can't successfully create a recordset for your query results, check this property to see whether the query itself is at fault.

Now let's look at the methods of the Query object:

  • CreateRecordset. This method takes one argument, Sequential, because Site Server Search can create only sequential, forward-only cursors. When you call this method, make sure that all the properties of the Query object are set to your preferences. This method will create an ADO recordset containing the search results from your query. You can then use standard ADO methods and properties to scroll through the recordset. You'll see how to do this later in the chapter in the sample applications. Also, Site Server extends the ADO object model with some extra properties that we'll look at later in this chapter.
  • DefineColumn. This method defines a new display name for a column in your query. You need to pass this method the data type, property set ID, property ID, and property name as a string to specify the column you're interested in. You can reassign names for built-in or custom properties that you've already defined. Instead of using this method, if you plan to define a new name for a column or a new column, it's better to modify the definecolumns.txt file that's under the Site Server directory. This will make your column changes permanent so that you don't have to define the column with each query. Furthermore, this text file will give you the list of property set IDs and property IDs. (A property ID can either be a unique number or name.) The Site Server property set IDs and property IDs are different from the Exchange Server ones, so be careful not to confuse them. Here's an example of using the DefineColumn method:
  •  Q.DefineColumn "CustomColumn (DBTYPE_VECTOR | OR DBTYPE_I2) = d1b5d3f0-c0b3-11cf-9a92-00a0c908dbf1 CustomColumn" 

  • Reset. This method clears the settings in the Query object. If you want to start with a fresh Query object, call this method.
  • QueryToURL. Call this method when you want to convert the settings of the Query object to a URL-encoded string. This is useful if you want to pass the query from one Web page to another. You'll see the format of this URL-encoded string when we discuss conventional query string variables later in this chapter. The following is an example of this method:
  •  strQuery = Q.QueryToURL 

  • SetQueryFromURL. Since Site Server can change the settings into a query string, it can also set the Query object from the variables in a URL query string. This is what SetQueryFromURL does. This method expects a URL encoded string. Site Server then parses the string, looks at the values, and overwrites any settings already present on the Query object with the settings from the string, ignoring unrecognized settings. Here's an example of using this method from Microsoft Active Server Pages (ASP):
  •  <% Q.SetQueryFromURL("ct=MyCatalog&c2=@All&q2=Outlook") %> 

ADO Recordset Extensions

In addition to the standard methods of the Recordset object, such as Move, and the properties of the Recordset object, such as BOF and EOF, Site Server adds some extensions that are useful for search applications. However, Site Server does not support the full range of ADO Recordset methods and properties. Table 17-1 lists which methods and properties are supported and unsupported.

Table 17-1. ADO Recordset methods and properties supported and unsupported by Site Server.

Supported Unsupported
Methods Close, GetRows, Move (forward-only), MoveNext, Supports AddNew, CancelBatch, CancelUpdate, Clone, Delete, MoveFirst, MoveLast, MovePrevious, NextRecordset, Open, Requery, Resync, Update, UpdateBatch
Properties BOF, CacheSize, CursorType, EOF, Filter, MaxRecords, Source, Status ActiveConnection, AbsolutePosition, AbsolutePage, Bookmark, EditMode, LockType, PageCount, PageSize, RecordCount

Site Server adds five extended properties to the Recordset object, and these are described in the following list. To use these properties, use the following syntax: Recordsetobj.Properties("ExtendedProperty").

  • CatalogSeqNums. This property should be used only for multiple catalog searches. It holds new entries in the catalogs between searches. It maps to the CrawlLastModified property in the Search Gatherer Property Set. If you're searching only a single catalog, use the CrawlLastModified property and specify it as one of your query variables. Once you retrieve this property, whose value is a set of numbers separated by semicolons, store the values for each user. Then you can offer a search that displays only items that have been modified since the last catalog search. The following shows an example of searching only new items if CatalogSeqNums returned 10;15 for two catalogs. Note that this search uses the standard query syntax rather than the conventional query string variables you'll see later in the chapter.
  •  ( @All Outlook ) & ( @CatalogSeqNums > 10;15 ) 

  • MoreRows. Use this Boolean property after you create your recordset. It returns True if more search results are available than are currently being displayed and False if no more search results are available. You can use this property to determine whether you need enable the More Results button in your search application.
  • NextStartHit. You can use this method to determine where the display of the next batch of results should start, with respect to the results currently being displayed on the page. You will receive a comma-delimited string of numbers for multiple catalog searches. You should then feed those numbers into the StartHit property of the Query object so that you can display successive pages of search results.
  • RowCount. If you have set the OptimizeFor property on the Query object to the HitCount value, the RowCount property will return the total number of records found in all catalogs searched. Because determining total number of hits might be time consuming, Site Server defaults to returning only 200 hits per search. Don't be alarmed if you know your search has more than 200 hits. You can configure Site Server to return all hits, either by setting the calculated number of results for each catalog in Site Server administration to a large number or by setting the MaxRecords property of the Query object to 0. For catalogs that contain Exchange Server information, keep the number of results returned fairly low. You should use this property in conjunction with the RowLimitExceeded property.
  • RowLimitExceeded. This Boolean property specifies whether the RowCount for the catalog was exceeded. Since you can determine whether there are more results than yielded by the RowCount property, you can use the RowLimitExceeded property to display results such as "1 - 50 items of more than 200 found". If you specify nohitcount for the OptimizeFor property on the Query object, RowLimitExceeded will always return False.

Building an ASP Search Application

The most common way that developers build search pages for Site Server is by using ASP applications. This allows you to provide your search application with a Web interface while taking advantage of the Site Server object model's easy-to-use, Webbased methods. We'll take a look at leveraging public folders for a knowledge base application and providing a rich search of this knowledge base using Site Server.

Before we dive into the code, I'd like to point out a few things about the sample application. This application includes a custom Outlook form for submitting information into the knowledge base and for reading information from the knowledge base. Figure 17-6 shows this form.

To make it easy for Web users to work with the knowledge base application, the form is also converted into an ASP-based form using the Outlook HTML Form Converter. This makes it possible for non-Outlook users to view the knowledge base articles. Figure 17-7 shows this version of the form.

The knowledge base form in Figure 17-6 and Figure 17-7 implements some custom Outlook fields such as Category, which specifies the type of information contained in the knowledge base article—for instance, best practice, idea, issue, or resolution. The form also uses a field called Product, which specifies the product called out in the knowledge base article—for example, Exchange Server, Outlook, or Office. Finally, the form uses a field called Industry, which specifies the industry that the knowledge base article refers to. You'll see how Site Server allows you to search these custom properties with minimal effort. Figure 17-8 shows the ASP solution for this search.

click to view at full size.

Figure 17-6. The Outlook form for the knowledge base application.

click to view at full size.

Figure 17-7. The HTML version of the Outlook knowledge base form.

click to view at full size.

Figure 17-8. The ASP solution that allows you to search the knowledge base application.

Conventional Query String Variables

When building custom search applications, you first need to look at Site Server's capabilities for conventional query string variables, which are two-letter tags that you can specify along with a Web address. From these two-letter tags, Site Server can create and populate query variables used in searching catalogs. Even though these variables are targeted toward Web search pages, you can leverage them in other applications, as you'll see momentarily.

The conventional query string variables are designed to identify the property you want to query and the value to use for the query criteria. You can also use these two-letter tags to specify properties on the Query object. For example, the "ct" tag identifies the catalog to use for the search. In the "qn" tag, q specifies the conventional query string variable and n specifies a number. You should use the "qn" tag in conjunction with the "cn" tag because the "cn" tag specifies the column you want to search for the "qn" value. You should use the "on" tag only with numeric and date columns. Here is an example query string from a typical search:

q1=&c2=@DocAuthor&q2=&c3=@filewrite&o3=%3E%3D&q3=1y&c5=
@MessageEmailAddress&q5=&o4=@DocTitle&q4=&c6=@MessageFolder&q6=
&ct=Discussion&c7=@META_Type&q7=&c8=@META_Industry&q8=&c9=
@META_Product&q9=

Table 17-2 specifies the different values you can have for these query string tags; each corresponds to a Query object property. In addition to tags that correspond to Query object properties, this table also includes query string tags that correspond to multicolumn searches. This is where the "qn/cn" tags come into play. If you use the "qu" tag to set the Query property on the Query object, you should not use the multicolumn search tags as well.

Table 17-2. Tags for conventional query string variables.

Tag Description
"ae" Allows enumeration. If you pass a nonzero digit, enumeration will be allowed for the query.
"ct" Identifies the catalogs to search. You should pass a string specifying catalogs separated by commas for multiple catalog searches.
"mh" Specifies the maximum number of hits. By setting this to 0, you specify that all records should be returned.
"op" Indicates what to optimize for. The value x specifies performance, r specifies recall, and an optional h specifies no hitcount.
"ou" Specifies the full query that you want to perform. For example, you could pass "( @FileWrite < 1999/11/22 )" using this tag to search for all items written before 11/22/1999.
"sh" Specifies the starting result number for each catalog.
"sd" Associated with the SortBy property on the Query object. Sorts in descending order—for example, sd=rank.
"so" Sorts in ascending order, for example, so=rank.
"cn" Specifies a column that you want in your query. If you leave this blank but specify a corresponding "qn" tag, Site Server will assume that you want to query the contents of the full-text index for the catalog. You should precede your column names with an at sign (@) when using this tag—for example, @FileWrite.
"on" Operator that specifies which comparison operator you want to use. You should use this tag for numeric and date queries only. The valid operators for this tag are =, !=, >, >=, <, and <=.
"qn" The query term you want to search for. If you specify a "qn" tag without a "cn" or an "on" tag, Site Server will search the full-text contents for this query term.

Using Operators with Date Values

Site Server provides the ability to perform searches on dates using both relative and absolute values. Relative dates express the current date and time. You express these dates using the minus sign (-) and an integer unit followed by a time unit. The time units can be years (y), months (m), weeks (w), days (d), hours (h), minutes (n), and seconds (s). For relative dates, you can use the operators less than (<), greater than (>), less than or equal to (<=), or greater than or equal to (>=). An example of a relative date query is @FileWrite >= -2y. This query would return any documents that were last saved on a date greater than or equal to the current date/time two years ago. Relative date queries are probably the most common queries that you'll perform.

Site Server also supports absolute date queries. When using this type of query, you must specify a date in one of these formats:

  • yyyy/mm/dd hh:nn:ss
  • yyyy-mm-dd hh:nn:ss

You might not want to use absolute date queries because of this formatting and because Site Server assumes the date you pass in is relative to the Universal Time Coordinate (UTC), formerly Greenwich Mean Time (GMT). Your users might get confused if they enter a specific date and values they didn't expect to meet that date are returned because of time zone differences.

Note that the equal sign (=) should not be used with absolute dates. Site Server will try to perform an exact match on the date and time, including milliseconds, for your documents. Instead, if you need to target a specific time period, use less than (<) and greater than (>). For example, to show all documents saved on November 25, 1999, you would use the following query syntax:

 @FileWrite < 1999/11/26 AND > 1999/11/24 

Exchange Server Property Set

Before you start building a search application, you need to understand which Exchange Server properties Site Server can index. By default, Site Server indexes only five Exchange Server-specific properties and maps a number of its built-in properties to Exchange Server values. Let's take a look at the Exchange Server property set in Site Server and see how to search custom Exchange Server properties.

The five Site Server properties that directly support Exchange Server are MessageClass, MessageDisplayName, MessageDisplayCC, MessageFolderName, and MessageFolderPath. All these properties are indexed, but the only one that is retrievable as a column, by default, is MessageFolderName. Although you can query on the other properties, you can't retrieve them for display in your resultset. Table 17-3 describes each of these properties in more detail.

Table 17-3. Built-in Exchange Server properties.

NameDescription
MessageClass Message class of the item. This can be IPM.Note, IPM.Post, or any valid Exchange Server message class. This property is useful to query on if you want to limit your search to only messages, posts, documents, or other types of items stored in an Exchange Server public folder.
MessageDisplayName Display name of the recipient. For posts, you might want to use MessageFolderName instead to allow users to search by the folder the item was posted into.
MessageDisplayCC Display name of any carbon copy recipients.
MessageFolderName Name of the folder the message was posted into.
MessageFolderPath Path to the folder that the message was posted into. You might want to specify a wildcard to search for items posted into a particular section of your public folder hierarchy.

The built-in Site Server properties shown in Table 17-4 are mapped to Exchange Server values for Exchange Server data sources. These properties are indexed and retrievable by Site Server.

Table 17-4. Built-in Site Server properties.

NameDescription
DocTitle Subject of the message.
DocAddress URL of the message. This property leverages the Outlook Web Access server name you put into the Site Server administration program.
Description First 300 characters of the message. You can use this property to provide an autopreview feature for your search applications.

Free Documents in Public Folders

Besides supporting built-in Exchange Server properties, you can have Site Server index Office document properties of the free documents in public folders. These properties include the author and category properties. Note that in order to provide Office document support for documents stored in Exchange Server, you need to have Site Server Service Pack 3 installed on a computer running Site Server.

Exchange Server and Outlook Custom Properties

Site Server also supports automatic searching on Exchange Server or Outlook custom properties. You don't have to specify custom properties to search on them; Site Server will automatically provide this capability to your search application. Site Server uses the HTML META property set for searching on these properties. To use the META property set, you must preface the name of your custom property with a @META_propertyname. For example, if your custom property in Exchange Server is named Username, to search on that property for a value of Tom, you would specify in your query @META_Username Tom. If your Exchange Server custom property contains spaces, wrap your META tag in quotes, as in "@META_User name".

By default, Site Server indexes string and date properties but not numbers. Furthermore, these properties are not retrievable. This means you can search using custom properties, but you cannot display the values contained in those properties as part of the results returned in the ADO recordset. Your best bet when using Site Server and Exchange Server together is to try to use only string properties wherever possible because you'll have to modify the definecolumns.txt file to search date properties.

When using custom properties, you can also tag HTML documents with the same display name for your custom properties using the HTML META property format. This allows you to use the META property search capabilities across HTML and Exchange Server documents in your Site Server searches. You'll see how to use the capabilities of Site Server and Exchange Server custom properties later in this chapter.

NOTE
You can find a list of all Site Server columns in the Site Server help file included on the companion CD.

Security Requirements for the ASP Search Application

When setting up your virtual directory to allow searches against Exchange Server information, you must authenticate the user before Site Server can return information to the browser. This authentication is required so that the search doesn't return information that a particular user shouldn't see. Because of this authentication requirement, you should disable anonymous access to the virtual directory where you place your search pages and require Basic or Windows NT Challenge/Response authentication, or you should add code to your search page to authenticate the user directly in Microsoft Visual Basic Scripting Edition (VBScript) or JavaScript code.

If you don't authenticate the user in your code or through the directory security in Microsoft Internet Information Services (IIS), such a user will be able to search and view any Exchange Server information contained in the Site Server catalog that anonymous users are allowed to view. The only problem you might run into would be when search results are returned to the user and that user tries to browse those search results using Outlook Web Access. If you haven't added the particular public folder where that search result resides into the anonymous folders in the Exchange Server administration program, the user won't be able to see the results.

Getting Input from the User

The first section of search.asp processes existing input from the user. Since the application divides the search results into groups of 10 hits, it uses session variables in ASP to remember the criteria the user entered for his search across multiple search result pages. If you customize this application to search on additional properties, be sure to modify this section so that when the user clicks to see more search results, the search criteria are not lost at the top of the HTML page. The code that follows gathers the input from the user and sets the ASP session variables appropriately if the page being displayed is a More Results page of a query:

 <% 'Process Input Section %> <% if Request.QueryString <> "" then %> <% 'Initialize or set nonpredefined variables 'from information posted to page 'Purpose of DisplayText variable is to have only the words from 'the query for display in the introductory sentence and in the More 'Results link if Request("DisplayText")= "" then DisplayText= Request("q1") else DisplayText = Request("DisplayText") end if 'Purpose of RecordNum variable is to display which records 'are displayed on each results page if Request("RecordNum") = "" then RecordNum=1 else RecordNum=Request("RecordNum") end if 'Purpose of session variables is to store information for use in 'editing a search. Only information from the initial query is stored: 'these variables are not updated on More Results pages. 'If you add search criteria in your search page, make sure 'to add their initial values to the Session object. 'Server.HTMLEncode is required to store queries with double quotes. if Request("sh") = "" then Session("q1")=Server.HTMLEncode(Request("q1")) Session("q2")=Request("q2") Session("ct")=Request("ct") Session("c3")=Request("c3") Session("o3")=Request("o3") Session("q3")=Request("q3") Session("o4")=Request("o4") Session("q4")=Request("q4") Session("c5")=Request("c5") Session("q5")=Request("q5") Session("c6")=Request("c6") Session("q6")=Request("q6") Session("c7")=Request("c7") Session("q7")=Request("q7") Session("c8")=Request("c8") Session("q8")=Request("q8") Session("c9")=Request("c9") Session("q9")=Request("q9") end if 'Read session object into variables q1=Session("q1") q2=Session("q2") ct=Session("ct") c3=Session("c3") o3=Session("o3") q3=Session("q3") o4=Session("o4") q4=Session("q4") c5=Session("c5") q5=Session("q5") c6=Session("c6") q6=Session("q6") c7=Session("c7") q7=Session("q7") c8=Session("c8") q8=Session("q8") c9=Session("c9") q9=Session("q9") %> <% end if %> 

The second section of the search.asp file is the search initiation section. This section displays the form in which the user can specify the criteria for the search. Notice that the inputs on the form are assigned specific names. This allows the ASP page to leverage the conventional query string variables. Be aware that Site Server supports only nine columns for the conventional query string variables. So don't try adding a c10/q10 combination to the ASP search page. It won't work.

You can see the use of the HTML META property set in this next section of code. The search.asp file allows users to search using three custom properties from the knowledge base application. You can also see the use of the Exchange Server property set.

 <% 'Search Initiation Section 'Show the search form. If the search is being edited, read 'the information that has been previously stored in the 'Session object. %> <p> <table cellpadding=5 border=0> <form action="Search.asp" method="get"> <tr> <td nowrap><font size=2> <% L_QueryLabel_text = "Search for:" %> <% = L_QueryLabel_Text %>&nbsp; </font> </td> <td> <input type="hidden" name="c1" value="@All"> <input type="text" name="q1" size="25" maxlength="100" value="<% = q1 %>"> </td> <td width=5></td> <td> <% 'Author L_AuthorLabel_text = "Sent by" %> <font size=2><% = L_AuthorLabel_text %>:</font> </td> <td> <input type="hidden" name="c2" value="@DocAuthor"> <input type="text" maxlength=100 size=25 name="q2" value="<% = q2 %>"> </td></tr> <tr> <td> <font size=2> Sent in the last: </font> </td> <td> <input type=hidden name="c3" value="@filewrite"> <input type=hidden name="o3" value=">="> <select name="q3"> <option value="" <%if Request("q3")="" then %>selected<%end if%> > <option value="-1d" <%if q3="-1d" then %>selected<%end if%> >day <option value="-1w" <%if q3="-1w" then %>selected<%end if%> >week <option value="-1m" <%if q3="-1m" then %>selected<%end if%> >month <option value="-1y" <%if q3="-1y" then %>selected<%end if%> >year </select> </td> <td></td> <td><font size=2> Sent to: (alias) </font></td> <td> <input type=hidden name="c5" value="@MessageFolderName"> <input type=text name="q5" size=25 maxlength=50 value="<% = q5 %>"> </td> </tr> <tr><td> <font size=2>Subject:</font> </td> <td> <input type=hidden name="o4" value="@DocTitle"> <input type=text name="q4" size=25 maxlength=50 value="<% = q4 %>"> </td> <td></td> <td><font size=2> Folder: </font></td> <td> <input type=hidden name="c6" value="@MessageFolderName"> <input type=hidden name="q6" size=25 maxlength=50 value="<% = q6 %>"> <select name="ct" value="ct"> <% 'Enter new catalog names here %> <!-- Use the following syntax for to search all catalogs <option value="Discussion,ProductKnowledge,Business,list_Servers"> All Indexed Folders --> <!-- You need to put your catalog name here --> <option value="Discussion"> \Discussion </Select> </td> </tr> <tr> <!-- Add search by category --> <td><font size=2> Category: </font></td> <td><input type=hidden name="c7" value="@META_Type"> <select name="q7"> <option value="" <%if q7="" then %>selected<%end if%> > <option value="Best Practice" <%if q7="Best Practice" then %> selected<%end if%> >Best Practice <option value="Idea" <%if q7="Idea" then %>selected<%end if%> >Idea <option value="Issue" <%if q7="Issue" then %> selected<%end if%> >Issue <option value="Resolution" <%if q7="Resolution" then %> selected<%end if%> >Resolution </select> </td> <td></td> <!-- Add search by industry --> <td><font size=2> Industry: </font></td> <td><input type=hidden name="c8" value="@META_Industry"> <select name="q8"> <option value="" <%if q8="" then %>selected<%end if%> > <option value="Communication and Entertainment" <%if q8="Communication and Entertainment" then %> selected<%end if%> >Communication and Entertainment <option value="Distributed Services" <%if q8="Distributed Services" then %> selected<%end if%> >Distributed Services <option value="Financial Services" <%if q8="Financial Services" then %>selected<%end if%> >Financial Services <option value="Government" <%if q8="Government" then %> selected<%end if%> >Government <option value="Healthcare" <%if q8="Healthcare" then %> selected<%end if%> >Healthcare <option value="Information Systems( Hardware/Software)" <%if q8="Information Systems( Hardware/Software)" then %>selected<%end if%> > Information Systems(Hardware/Software) <option value="Manufacturing" <%if q8="Manufacturing" then %> selected<%end if%> >Manufacturing <option value="Professional Services" <%if q8=" Professional Services" then %>selected<%end if%> > Professional Services <option value="Transportation" <%if q8="Transportation" then %> selected<%end if%> >Transportation </select> </td> </tr> <tr> <!-- Add search by product --> <td><font size=2> Product: </font></td> <td><input type=hidden name="c9" value="@META_Product"> <select name="q9"> <option value="" <%if q9="" then %>selected<%end if%> > <option value="(Other)" <%if q9="(Other)" then %> selected<%end if%> >(Other) <option value="Access" <%if q9="Access" then %> selected<%end if%> >Access <option value="Excel" <%if q9="Excel" then %> selected<%end if%> >Excel <option value="Exchange Server" <%if q9="Exchange Server" then %> selected<%end if%> >Exchange Server <option value="Mail" <%if q9="Mail" then %>selected<%end if%> >Mail <option value="Office" <%if q9="Office" then %> selected<%end if%> >Office <option value="Outlook" <%if q9="Outlook" then %> selected<%end if%> >Outlook <option value="PowerPoint" <%if q9="PowerPoint" then %> selected<%end if%> >PowerPoint <option value="Visual Basic" <%if q9="Visual Basic" then %> selected<%end if%> >Visual Basic <option value="Visual C++" <%if q9="Visual C++" then %> selected<%end if%> >Visual C++ <option value="Word" <%if q9="Word" then %>selected<%end if%> >Word </select> </td> <td colspan=5 align=right> <% 'Links to tips and search button L_SearchButton_label = "Search" L_SearchTips_link = "Tips" %> &nbsp;&nbsp;&nbsp; <input type="submit" value="<% = L_SearchButton_label %>"> &nbsp;&nbsp;&nbsp; <font size=2><a href="tips.htm"><% = L_SearchTips_link %></a> </font> </td> </tr> </table> </form> <% 'End of Search Initiation Section %> 

The next section of the code checks whether the user has an ActiveX control installed on his computer to launch Outlook to view the item. This ActiveX control is provided with Site Server. The filename for the control is exciol.ocx, and the CodeBase property for the control points to C:\siteserver\knowledge\search\controls\exciol.ocx by default.

This control has one method that you can call: DisplayMsg. The DisplayMsg method takes the URL to the item, which you can obtain from the DocAddress property in the ADO recordset of results. Then the ActiveX control will automate Outlook to display the item. If the user doesn't have Outlook, or if you don't want to let the user view the results using Outlook, she can use Outlook Web Access to view the item.

 <% if Request.QueryString <> "" then %> <% 'If site visitors use Outlook to view Exchange Server messages, 'the Exciol control is installed on their computers if ExchangeViewer="both" or ExchangeViewer="outlook" then %> <object id="Exciol" height=0 width=0 CLASSID="CLSID:DAFD7A40-73FF-11D1-A811-00AA006EAC9D" CODEBASE="/siteserver/knowledge/search/controls/ exciol.ocx#version=5,5,2148,0" TYPE="application/x-oleobject"> </object> <script language="vbscript"> Sub DisplayMsg(EntryID) Exciol.DisplayMsg(EntryID) End Sub </script> <% end if %> <% if ExchangeViewer="both" or ExchangeViewer="owa" then %> <% 'When linking to Outlook Web Access, open the link in a new window %> <script language="javascript"> function openNewWindow(fileName,theWidth,theHeight) { window.open(fileName,"NewWindow","toolbar=0,location=0,directories=0, status=1,menubar=1,scrollbars=1,resizable=1,width="+theWidth+", height="+theHeight) } </script> <% end if %> 

The next section of code creates the user's query by using the Site Server Query object. As you saw earlier, the Query object has a number of properties that you can use to create complex queries against Site Server catalogs. In the search.asp code, the first step is to create the Query object by calling Server.CreateObject with the ProgID of the Query object, MSSearch.Query. Since the application passes all the information about the query along the URL query string, you can use the method SetQueryFromURL to have the Query object automatically parse the URL query string and set the Query object properties according to the specified conventional query string variables.

 <% 'Set query object properties set Q = Server.CreateObject("MSSearch.Query") 'Define all required query properties: the query itself, and all columns 'that will be used in the results Q.SetQueryFromURL(Request.QueryString) Q.Catalog = request("ct") Q.OptimizeFor="nohitcount,performance" Q.MaxRecords = 10 Q.Columns = "DocAuthor,DocTitle,DocAddress,Description,Size,FileWrite" %> 

The next step in creating the search results is to actually create the recordset of items that match your criteria. To do this, you need to call the CreateRecordset method on the Query object. This method will create an ADO sequential, forward-only recordset that you can use to display your results. As you can see in the code, different properties are pulled from the Site Server catalog to display the resultset. The code uses standard ADO methods and properties to scroll through the resultset.

 <% 'Create the recordset holding the search results on error resume next set RS = Q.CreateRecordSet("sequential") if err <> 0 then createerror = err.description end if 'If the query can't be executed, print out the error description if err then Response.write "&nbsp;&nbsp;" & createerror 'If no matches are found, display a message elseif RS.BOF and RS.EOF then Response.write "<p>" if Q.QueryIncomplete=true then L_TooComplex_Error = "The query is too complex. " & _ "Try using a simpler query.For information on search " & _ "syntax, see <a href=tips.htm>Search Tips</a>." Response.write L_TooComplex_Error & "<p>" else L_NoMatch_Error = "No messages matching your query were " & _ "found. For suggestions on how to broaden your search, " & _ "see <a href=tips.htm>Search Tips</a>." Response.write L_NoMatch_Error & "<p>" 'Display link for a new search L_NewSearch_link = "New Search" %> &nbsp;&nbsp;<a href="Search.asp"><% = L_NewSearch_Link %></a> <% end if else 'If query could be executed, display results 'Set up the table for displaying results %> <table cellpadding=0> <tr> <td colspan=2><font size=2> <% 'Make introductory sentences match the information 'displayed if Request("sh") = "" then L_Match_text = _ "Here are the messages found matching the query" else L_Match_text = _ "Here are more messages found matching the query" end if Response.write L_Match_text & "<b> " & _ DisplayText & "<b>." %> <br>&nbsp; <% if ExchangeViewer="both" then L_OWA_Info = "For Microsoft&reg; Exchange messages, " & _ "click the author's name to view the message " & _ "using Microsoft Outlook. Click the " & _ "<img srC=html.gif width=16 height=16 " & _ "border=0 align=middle align=middle> " & _ "icon to view the message using Microsoft " & _ "Outlook Web Access." Response.write L_OWA_Info end if %> <br>&nbsp; </font></td> </tr> </table> <table cellpadding=0 cellspacing=0> <tr bgcolor=cccccc> <% L_KBytes_text = "KB" L_Received_text = "Received" L_Subject_text = "Subject" L_Size_text = "Size" L_From_text = "From" %> <td></td> <td><font size=2><b><% = L_From_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Subject_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Received_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Size_text %></b></font></td> <td></td> </tr> <% 'Set up loop to iterate through results Do while not RS.EOF 'Determine format type; set up title for and URL for links if InStr(RS("MimeType"), "text/exch") then DocType="exchange" else DocType="doc" end if 'If message title is blank, use "No subject" instead if RS("DocTitle") <> "" then Title = RS("DocTitle") else L_Untitled_text = "No subject" Title = L_Untitled_text end if 'Provide alternate text if no author exists. HTMLEncode is 'required to handle author fields with < or >. if RS("DocAuthor") <> "" then Author = Server.HTMLEncode(RS("DocAuthor")) else L_NoAuthor_text = "Author unknown" Author = L_NoAuthor_text end if 'Set up link itself. Link depends on whether item is document or 'Microsoft Exchange Server message. if DocType="doc" then Link = RS("DocAddress") Image = "html.gif" elseif DocType="exchange" then Image = "owa.gif" if ExchangeViewer="owa" then Link = "JavaScript:self.openNewWindow(" & chr(34) & _ RS("DocAddress") & "&usemainwnd=1" & chr(34) & ", 640,500)" elseif ExchangeViewer="outlook" or ExchangeViewer="both" then Link = "VBScript:self.DisplayMsg(" & chr(34) & _ RS("DocAddress") & "=1" & chr(34) & ")" end if if ExchangeViewer="both" then OWALink = "JavaScript:self.openNewWindow(" & chr(34) & _ RS("DocAddress") & "&usemainwnd=1" & chr(34) & ", 640,500)" end if end if %> <% 'Create table row for each result %> <tr><td>&nbsp;</td></tr> <tr> <% if DEBUGINFO=true or Request("debug") <> "" then %> <% 'Display column with record number %> <td valign=top><font size=2><% = RecordNum %>.</font></td> <% end if %> <td valign=top> <a href='<% = Link %>'><img src="<% = Image %>" hspace=6 border=0> </a> </td> <td width=20% valign=top><font size=2> <a href='<% = Link %>'><b><% = Author %></b></a> <% if ExchangeViewer = "both" and DocType="exchange" then %> <a href='<% = OWALink %>'> <img src="html.gif" height=16 width=16 border=0 align=top></a> <% end if %> </font></td> <td width=5>&nbsp;</td> <td width=55% valign=top><font size=2> <b><% = Title %></b> </font></td> <td width=5>&nbsp;</td> <td valign=top nowrap><font size=1> <% = RS("FileWrite") %> </font></td> <td width=5>&nbsp;</td> <td valign=top><font size=1> <% iSize = CInt(CLng(RS("Size"))/1024) %> <% Response.write iSize & " " & L_KBytes_text%> </font></td> </tr> <tr> <td></td> <td colspan=6><font size=2> <table cellpadding=0 cellspacing=0> <td width=30>&nbsp;</td> <td><font size=1> <% = RS("Description") %> </font></td> </tr> </table> </td> </tr> <% 'Increment the results RS.MoveNext RecordNum = RecordNum + 1 Loop %> <% 'If query times out, display message stating that more results are 'available %> <% if Q.QueryTimedOut = TRUE then Response.write "<tr><td></td><td colspan=8><font size=2><b>" L_QueryTimedOut_error = "Not all matching messages were returned." & _ "To see all the messages, at the top of the page, " & _ "click Search again." Response.write "&nbsp;<p>" & L_QueryTimedOut_error Response.write "<b></td></tr><font size=2>" end if %> </table> 

The final section of code implements the More Results functionality at the bottom of the Web page. Since 10 results appear on a page, you need to implement this functionality so that the user can look at all the results returned in the query. This section of code uses the MoreRows property to determine whether there are more results than currently displayed. If there are more rows, the StartHit property is set on the Query object to the start hit on the next page. Then the code generates the hyperlink for the More Results text to pass the same query string to search.asp so that the application performs the same query on the catalog but returns the next resultset.

 <hr> <table cellpadding=4> <tr><td><font size=2> <% 'Display a More Results link if there are more results pages if RS.Properties("MoreRows") = true then Q.StartHit = RS.Properties("NextStartHit") 'Repeat query with new start hit. The query must include any 'custom variables you've used: in this case, Text and RecordNum. Response.write "<a href=search.asp?" _ & Q.QueryToURL & "&" _ & "Category=" & Request("Category") & "&" _ & "DisplayText=" & Server.URLEncode(DisplayText) & "&" _ & "RecordNum=" & RecordNum _ & ">" L_MoreResults_link = "More Results" Response.write L_MoreResults_link & "</a>&nbsp;&nbsp;" end if %> <% 'Display link for a new search %> <% L_NewSearch_link = "New Search" %> &nbsp;&nbsp;<a href="Search.asp"><% = L_NewSearch_Link %></a> </font></td></tr> </table> 



Programming Microsoft Outlook and Microsoft Exchange
Programming Microsoft Outlook and Microsoft Exchange, Second Edition (DV-MPS Programming)
ISBN: 0735610193
EAN: 2147483647
Year: 2000
Pages: 184

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net