October 1999

Create an ASP Search Page for Microsoft Search Server

by Susie Adams

The ability to gather and access information today has become more important than it's ever been. What's more, the demand for timely access to that data has also increased. However, information today is often stored in many different formats in many different locations. Computers store information in file systems, Web servers, mail servers and even relational databases. For years, you could query only one of these data stores at a time. Fortunately, products like Microsoft Index Server and Search Server have evolved to help alleviate this problem. These products allow you to execute a single query against multiple data storage media. The Search Server technology is based on the construction and use of a document catalog. Search finds and gathers documents and then indexes them to create a catalog. Then, you, the developer, can create a Web site that allows users to search through the cataloged information. In this article, we'll show you how to incorporate Microsoft Search Server into an ASP application that can query multiple data sources and display the results in a browser. (To use this technique, you must have Site Server 3.0 installed.)

Note: Don't confuse Microsoft Search Server with the Microsoft Index Server. Although they both allow users to query information, they're not the same product and have several notable differences. Search Server is installed as part of the Microsoft Site Server product and Index Server as a part of Windows NT 4.0.

How Search Server Works

To understand what Search Server contains underneath the covers, we should first go over some basics about catalog creation. To create a catalog, Search Server must complete four basic steps:

To carry out these steps, Search enlists a Windows NT service named Gatherer. This utility helps Search gather content, extract information, create the index, compile the catalog, and then distribute it. It most commonly does so by crawling through HTML links, files, or Microsoft Exchange documents.

Extract information, create an index, compile and propagate

When Search gathers a document, it opens it and extracts the necessary information. Next, it creates an index and compiles the information into a catalog. To accommodate the many document types out there, Search uses a plug-in filter module for each file format to extract the content, links, and document-specific properties--like the author's name or file size. Search includes filters for common formats like HTML, Microsoft Office, and plain text files. Several third-party vendors provide additional filters. Once Search has extracted the information, it creates an Index. An index keeps track of which words a document contains and where they're located within the document. To keep the index as small as possible, Search ignores noise words like a, and, the, and so on. You can modify these noise words by changing Search's internal noise file. When Search finishes indexing the documents, it compiles the information into a catalog and then propagates it to a host, which then allows users to search the catalog.

Create a catalog definition

To create a search application, you must first define a catalog. The catalog definition contains all the instructions and parameters Search will use to build the catalog. It tells Search where to gather documents and where to put the catalog after it's been compiled. To build a catalog, open the Microsoft Management Console for Site Server and right-click on the Catalog Build Server item contained within the Search Server folder. Select New | Catalog Definition from the shortcut menu. Next, enter a name for the catalog in the New Catalog Definition dialog box; then click OK. When you do, the Management Console adds the new catalog to the list. We called our catalog ZDJCatalog, as shown in Figure A.

Figure A: To create search application, you must first create a catalog.

Next, in the Search - Catalog Build Server dialog box, select the Crawl option, and then click Add to indicate a starting point for the crawl. When the Add Start Address Wizard appears, select the File Crawl option and click Next. Enter C:/ as the start location and click Finish. Search quickly builds a new catalog for you in the Build Catalog Definition tree view.

Next, we need to indicate which Search Server, or host, will use the catalog. In this demo, we'll house and search the catalog on the same server. To set the host property, re-open the Search - Catalog Build Server properties dialog box and select the Propagation tab. Click the Add button. In the wizard that appears, enter the host's name in the Name text box and click Finish. At this point, we've completed the catalog's definition. Now we can build the catalog.

Build the catalog

Search Server offers two ways to build a catalog--manually, or on a timed schedule. For now, we'll build it manually. To do so, right-click on the ZDJCatalog item and select Task | Start Build from the shortcut menu. Search generates the catalog in the background. To check the current build's status, right-click on the catalog item and select Properties from the shortcut menu. In the dialog box, select the Status tab. Search Server displays the files in the window as it indexes them, as shown in Figure B.

Figure B: The Status tab in the Search - Catalog Build Server dialog box displays the status of an on-going catalog build.

Test the results

After Search Server completes the build, you can use the MMC to test the catalog. To do so, expand first the Search Server item, and then the ZDJCatalog item in the MMC. The MMC displays a Search HTML page beneath. When you select the Search item, the MMC displays the sample in the right part of the screen, as shown in Figure C.

Figure C: To test the catalog, expand the Search Server item in the MMC and select the Search HTML item.

Now, enter a search string in the field. (We entered Microsoft.) When you do, the catalog lists the search results in the page.

Create the ASP Search Application

To search the catalog, you need to create an ASP application that lets you enter a query, then retrieve the results. For our example, we'll create two ASP pages, a search page, and a results page. To begin, create a new Visual InterDev project on your local Web server. Next, create a new page named SearchASP.asp and enter the code from Listing A.

Listing A:

The Search.asp Page

<html>
<head>
<title>Site Server Demo - Search</title>
</head>
<body text="000000" link="000000" alink="000000" 
vlink="000000" topmargin="0" leftmargin="0" >
      
<table width="100%" height="100%" cellpadding="5" 
border="0">
<tr valign="top">
<td>  
	<%
	set objSearchAdmin = Server _
		.CreateObject("Search.SearchAdmin.1")
  set objSearchServer = objSearchAdmin.SearchServer
  set objSearchCatalog = objSearchServer.SearchCatalogs
    
	for each objSearchCatalog in objSearchCatalog
		if LCase(objSearchCatalog.Status) = "enabled" then 
			strName = objSearchCatalog.Name
			if CatalogList = " then
				CatalogList = strName
			else
				CatalogList = CatalogList 
& "," & strName
			end if
		end if
	next
	if CatalogList = " then NoCatalogs=true
	if NoCatalogs = true then
		L_NoCatalogs_text = "No catalogs were available. " & _
			"Please contact the administrator for this site."
		Response.write "<p><b>" 
& L_NoCatalogs_text & "</b>"
	else
		CatalogArray = split(CatalogList,",",-1,1)
		%>

		<form method="get" action="results.asp" 
		target="Right">
		<font face="Verdana,Arial,sans-serif" style="font-
			size: 10pt">
			<% L_Search_text = "Search" 
				L_All_Catalogs = "All Catalogs"%>
			<% = L_Search_text %>:
		</font>
		<select size="1" name="ct">
		<option value="<% = CatalogList %>" selected>
		<% = L_All_Catalogs %>
		<% For each catalog in CatalogArray %>
			<option value="<% = catalog %>">
<% = catalog %>
		<% Next %>
	  </select>
  <% end if %>
  <br><br>
    
  <font face="Verdana,Arial,sans-serif" 
		style="font-size: 10pt">
  <% L_Find_text = "For documents containing" %>
  <% = L_Find_text %>:
  </font>
  <input type="Text" name="q1" value size="23">
  <input type="Submit" name="submit">
  </td>
</tr>
</table>
</form>
</body>
</html>

This page allows you to select one or all Search catalogs and then enter a search string. As you can see in Listing A, to retrieve a list of available catalogs, the first section of VBScript loops through the SearchCatalogs collection. It then builds a comma-delimited string of enabled catalogs. If the procedure doesn't find any catalogs it displays a message; otherwise, it fills a dropdown box with the catalog names. (Notice the use of VB 6.0's new Split function.) Next, it displays a text box in which you enter search criteria. Finally, the page's form action submits the page to Results.asp, which retrieves the input field's value and executes the query.

Display the results

Now that we can submit a catalog search, our application needs a place to display the results. The Results.asp page is a little more complicated, because it must gather the results, and then build an HTML table to display them. In such a page, you can display several variables that describe search query's results, some of them you've no doubt seen before. For our example, we'll list each document's title, description, size, and rankings. To begin, create a new page named Results.asp in your VID application, and then add the code in Listing B. To query the catalog and retrieve the documents, the page creates a reference to the Query and Util objects. Next, it uses the Query object's SetQueryFromURL method to set the query string. (To learn more about the available search objects, properties, and methods, refer to the appropriate Site Server technical documentation.)

In addition, the page sets a few extra properties. It sets MaxRecords to 25, SortBy to Rank and DocTitle, and lists the columns we want to display. Once it sets these properties, the code uses the CreateRecordset method to execute the query. Once it does so, it's just a matter of looping through the recordset and displaying the results.

Note: The Query recordset object is slightly different then the ADO recordset object and contains additional methods. To learn more, refer to the appropriate Site Server documentation. To run the application, right-click on the SearchASP.asp page in the VID project and select Browse from the shortcut menu. When VID displays the page, enter a query string and click the Submit button. When you do, the Results page lists the matches, as shown in Figure D.

Figure D: Our search generated a list of documents regarding Microsoft.

Conclusion

In this article, we've only given you a taste of this powerful, robust product. As you explore Search Server further, you'll no doubt begin to see its potential for mining document stores. In this article, we've shown how to use this technology in an ASP page.

Listing B:

The Results.ASP code

<html>
<head><title>Search Results</title></head>
<body text="#000000" link="#000000" alink="#000000" 
	vlink="#000000" topmargin=17 leftmargin=15 
	bgcolor="ffffff">
<font face="Verdana,Arial,sans-serif" 
	style="font-size: 10pt;">
<font color="#333399" style="font-size: 14pt">
Search Results
</font>
<hr>
<%
DisplayText=Request("q1")
' RecordNum displays number of results per page.
if Request("RecordNum") = " then
  RecordNum=1
else
  RecordNum=Request("RecordNum")
end if 
%><font color="#000000" face="Verdana,Arial,sans-serif" 
	style="font-size: 10pt;">
<% 
Response.write "Searching For" & " <b>" & DisplayText _
	& "</b>"
%>
</font>
<%
' Set query and utility objects, define object 
' properties.
set util = Server.CreateObject("MSSearch.util") 
set Q = Server.CreateObject("MSSearch.Query") 
Q.SetQueryFromURL(Request.QueryString)    
Q.MaxRecords = 25
Q.SortBy = "Rank[d],DocTitle"
Q.Columns = "DocTitle, DocAddress, FileWrite, Size, " & _
	"Description, FileName, DocSignature, Rank, " & _
	"DetectedLanguage, MimeType, SiteName, NNTP_MessageID" 

set RS = Q.CreateRecordSet("sequential") 
if RS.BOF and RS.EOF then
	if Q.QueryIncomplete=true then
		Response.write "The query is too complex." & _
			"Try using a simpler query."
	else
		Response.write "No documents matched your query."
	end if
else 
	Response.write "<table><tr><td><font size = 2>"
	' Set up number found.
	if RS.Properties("RowLimitExceeded") = true then
		NumberFound = "More than" & " " & RS.Properties("RowCount")
	else
		NumberFound= RS.Properties("RowCount")
	end if

	' If more than one result, display which results. 
	if RS.Properties("RowCount") <> 1 then
	' If only one on any but first page of results.
		if RS.Properties("RowCount") = cInt(RecordNum) then
			Displayed = RecordNum 

		' For all pages except last page.
		elseif RS.Properties("RowCount") > (RecordNum + _
			Q.MaxRecords -1) then
			Displayed = RecordNum & " - " & (RecordNum + _
				Q.MaxRecords - 1) 
		' For last page with more than one result.
		else
			Displayed = RecordNum & " - " & _
				RS.Properties("RowCount") 
		end if
	else
		Displayed="1"
	end if 
%>
<table width=75% align=center border=1>
	<tr>
	<td align=center nowrap bgcolor=80BBDD><font size=2>
		<% Response.write "Found : " & NumberFound  %></font>
	</td>
	<td align=center bgcolor=80BBDD nowrap><font size=2>
		<% Response.write "Showing: " & Displayed %></font>
	</td>
	</tr>
</table>
<hr>
<%
Do while not RS.EOF 
' Determine format type, create link title, set up link URLs
	Fmt="
	Fmt = RS("MimeType")
	if Fmt = "text/html" then
		DocType="doc"
		Image = "images/html.gif"
	elseif InStr(Fmt, "text/plain") > 0 or InStr(Fmt, _
		"richtext") > 0 then
		DocType="doc"
		Image = "images/text.gif"
	elseif Fmt = "application/x-msexcel" then 
		DocType="doc"
		Image= "images/excel.gif"
	elseif Fmt = "application/msword" then
		DocType="doc"
		Image= "images/word.gif"
	elseif Fmt = "application/x-mspowerpoint" then
		DocType="doc"
		Image= "images/ppt.gif"
	else
		DocType="undefined"
	end if

	if RS("DocTitle") <> " then
		Title = RS("DocTitle")
	elseif RS("FileName") <> " then
		Title = "No Title: " & RS("FileName") 
	else
		Title = "No Title: " & RS("DocAddress")
	end if

	Link = RS("DocAddress")
	LinkTarget = ""
	Response.write "<table cellpadding=0 cellspacing=0>
	Response.write "<tr><td width=21><font size=2>"
	Response.write "<p>"
	Response.write "<table cellpadding=1 cellspacing=1" & _
		"border=0><tr><td align=top>"
	iRank = RS("Rank")
	If (Not IsNull(Rank)) then
		If (iRank > 750) then
			Response.Write( "<img src= _
				"Images/RankHighest.gif"" & _
				"hspace=0 width=21 height=6 border=0>" )
		elseif (iRank > 500) then
			Response.Write( "<img src= _
				"Images/RankHigh.gif"" & _
				"hspace=0 width=21 height=6 border=0>" )
		elseif (iRank > 250) then
			Response.Write( "<img src= _
				"Images/RankLow.gif"" & _
				"hspace=0 width=21 height=6 border=0>" )
		else
			Response.Write( "<img src= _
				"Images/RankLowest.gif"" & _
				"hspace=0 width=21 height=6 border=0>" )
		end if
	else
		Response.Write( "<img src="Images/RankNone.gif"" & _
			"width=21 hspace=0 height=6 border=0>" )
	end if
	Response.Write "</td>"
	%>
	<td align=top>
	<a <% = LinkTarget %> href='<% = Link %>'>
		<img src="<% = Image %>" hspace=2 height=16 width=16 
		border=0></a>
	</td>
	</tr>
	</table>
	<%	Response.Write "</font></td>"
     Response.Write "<td bgcolor=80BBDD><font size=2>" %>
	<a <% = LinkTarget %> href='<% = Link %>'>
		<% = Title %></a>
	</font></td></tr>
	<tr><td></td><td><font size=2>
  <% Response.write util _
		.TruncateToWhiteSpace(RS("Description"),250) %>
	</font></td></tr>
	<tr><td></td><td height=5></td></tr>
	<tr><td></td><td>
	<font color=808080 size=1>[<% = util _
		.TruncateToWhiteSpace(RS("FileWrite"), 12 ) %>]
	<% iSize = CInt(CLng(RS("Size"))/1024) %>
	&nbsp; (<% = iSize %>k) &nbsp;
	<% Response.write Link %>
	</font>
		<% Response.write "<br>&nbsp;&nbsp;" %>
	<font color="808080" size=1>
	<a <% = LinkTarget %> href='<% = Link %>'>
		<% = Link %></a>
	</font>
  	<%
	' 	Increment the results.
		RS.MoveNext
		RecordNum = RecordNum + 1
		%></table><%
Loop

Response.write "</font></td></tr></table>"
Response.write "<hr>"

' If more than one results page, create "More Results" link.
if RS.Properties("MoreRows") = true then
	Q.StartHit = RS.Properties("NextStartHit")
	' Repeat query with new start hit. Query must include
	' custom variables: ie, DisplayText and RecordNum

	L_MoreResults_link = "More Results" 
	MoreLink = "<a href=results.asp?" _
		& Q.QueryToURL & "&" _
		& "DisplayText=" & Server.URLEncode(DisplayText) &  _
		"&" & "RecordNum=" & RecordNum _
		& ">" & L_MoreResults_link & "</a>"
end if 
%>
<table width=75% align=center border=1>
	<tr>
	<td align=center bgcolor=80BBDD nowrap><font size=2>
		<% Response.write L_Found_text & ": " _
			& NumberFound  %>
	</font></td>
	<td align=center bgcolor=80BBDD nowrap><font size=2>
		<% Response.write L_Showing_text & ": " & Displayed %>
	</font></td>

	<% if RS.Properties("MoreRows") = true then %>
		<td align=center bgcolor=80BBDD nowrap><font size=2>
			<% = MoreLink %>
     </font></td>
	<% end if %>
	</tr>
</table>
<br>
</font></td>
</tr>
</table>
<% end if  %>
</font>
</body>
</html>

Copyright © 1999, ZD Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD Inc. Reproduction in whole or in part in any form or medium without express written permission of ZD Inc. is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.