Home.NET Coding an AjaxPro.NET Based Search Engine ...
Coding an AjaxPro.NET Based Search Engine for Your Website
In yesterday's article, we began building a search engine with AJAX functionality to be embedded into a .NET-based web site. We set up the frameworks we planned to use and designed the database. In this part, the conclusion to the tutorial, we will get into the client- and server-side design, add a couple of optimizations to the code, and then test out our new search engine.
Contributed by Xianzhong Zhu Rating: / 13 October 23, 2007
Before putting the AjaxPro.NET framework into our application, we should get everything ready. Above all, we should add a reference to AjaxPro.2.dll to the project, which will result in generating a Bin folder. With this done, we will also have to modify the relevant part in the configure file-Web.config, as show in the following code snippet:
SqlConnection conn = new SqlConnection(strConnection);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = string.Format(
"SELECT id, title, author FROM Article WHERE title LIKE '%{0}%'",strWord );
DataSet ds = new DataSet("ArticleDataSet");
SqlDataAdapter da = new SqlDataAdapter(cmd);
try{
da.Fill(ds);
}
catch (SqlException) {}
finally{
conn.Close();
}
return ds.GetXml();
}
First, according to the downloaded materials, we have to put the attribute [AjaxPro.AjaxMethod] before every method that will be called via the AjaxPro.NET framework from the client side. The several steps that follow are the typical ASP.NET 2.0 database operation. The first point to notice is that we name the DataSet object "ArticleDataSet," so that will be conveniently used later. In the last step, we convert the record data in table form into a XML string by calling the GetXml method of our created DataSet instance.
Now let's turn our attention to the next Ajax method, namely GetXsl:
This method is relatively simple; its working relies on the XmlDocument class. We first get and load the required .xslt file, "trans.xslt," into the doc object. Then we directly return the XML-formatted string by returning the value of the InnerXml property of the doc object.
There is one final Ajax method: GetResultCount, which is responsible for returning the total number of the articles that satisfy the searching condition. Since it is also pretty simple, we will not dwell on it; please refer to the downloadable source code included at the beginning of this article.
On the client side, we first have to add references to the necessary JavaScript files that are required by Google's AJAXSLT framework at the <head> section of the "Search.aspx" (our search engine sample page) file:
Please note that we must follow the order specified above. This is because the programming logic of the latter .js files depends on the related parts that are coded inside the former. Here, however, we don't plan to dig into the inner workings.
Next, we follow the steps listed below to program the client side.
First, when the application starts up, we invoke the "MySearchEngine.GetXsl()" method to load the cascade style we are going to use to apply to the .xml files. This is the way specified by the AjaxPro.NET framework to call the server-side methods. Here the Ajax method GetXsl is prefixed by a particular namespace, MySearchEngine, that is defined above.
Then, inside the "onkeyup" event handler of the HTML <input> element (with the id being searchword), we invoke the GetResultXml client-side method, whose complete source code is listed below:
Here, with the help of a simple helper function named el we get the value of the HTML <input> control "searchword." Then we call the MySearchEngine.GetResultXml" server-side method, which is exposed via the AjaxPro.NET framework. Readers with more or less AJAX experience may be familiar with the followed code snippet; it is very similar to that used in invoking the general and original XMLHttpRequest with a typical callback function to deal with the returned result. Here, inside the GetResultXml_callback callback function we first invoke the xmlParse JavaScript helper function supplied by the AJAXSLT framework to convert the string formatted XML data into the formal XML format. Finally, by calling another AJAXSLT helper function, namely xsltProcess, we convert the above XML data into HTML format using the readily converted style data that is held in the xslt variable.
By now, we can come to the conclusion that with the XSLT light-weight language, the XML data can be converted into client-side HTML format without any complexity.
Let's take a quick look at the .xslt file we have just used, namely trans.xslt:
<xsl:attribute name="style"><xsl:if test="position() mod 2 = 0">background- color:#F7F7DE;
</xsl:if><xsl:if test="position() mod 2 = 1">background-color:white;
</xsl:if></xsl:attribute>
<td>
<xsl:value-of select="id"/>
</td>
<td>
<xsl:value-of select="title"/>
</td>
<td>
<xsl:value-of select="author"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Here, we have utilized some skills. First, we select a HTML <table> element to render the final XML data. Second, with the lines of code that lie below the for-each clause that judges whether the current line number is odd, we have achieved the effect of rendering the background color of the table rows alternately.
At this point, we have accomplished our original target-building an AjaxPro.NET based search engine inside our own website. However, there is at least one aspect that deserved to be optimized. In the above solution, we, through the "onkeyup" event of an HTML <input> element, trigger the background searching operation. Is this reasonable?
Let's take the example of a user who wants to search for "ajax" in a real scenario. It may take him one to two seconds to enter four letters, and the final results before him are all related to "ajax." Nevertheless, according to the solution introduced above, the background will execute an inquiry four times. This is because the "onkeyup" related event handler will be triggered four times when the user enters "ajax." Obviously, this is unreasonable; it puts a leaden burden on the server side which quite contradicts the original intention of AJAX.
In fact, during the course of the user quickly entering the search words it's unnecessary to send out the search request when the user does not care about the results. It's only when the user finishes entering the search words that it makes sense to send out a request to the server side. Thus, we can do some optimization: instead of using the "onkeyup" event handler to trigger the background search action, we can adopt the more active solution of making a comparison every x seconds (say the time interval is set to 100 ms). If the system sees that within 500 milliseconds the value within the HTML <input> element does not change and that the value differs from the search key word associated with the current retrieved results, then the system draws the conclusion that the user has altered the search key words and the new ones have been completely entered. At this point, it starts to send out a request to the background server side.
Now, let's delve into the process of optimization.
First, we add three global variables: curWord, lastValue, and diffCount, whose functions are detailed and explained in the following code:
// global variable, used to remember the retrieved words related to the current searched result
var curWord = "";
// global variable, used to remember the last time checked result
// we set this variable to prevent it from sending out much too frequent requests during the course of the user entering a string of words
var lastValue = "";
// global variable, used to remember the comparison result between the current entered value and current retrieved value
// add 1 if different, or else set to 0
var diffCount = 0;
Next, we define a checkInput function. This function is responsible for checking out the value of the <input> element. If the current value of this <input> element differs from that retrieved currently and is identical with the one checked last time, then the value of the diffCount variable is added to 1. If the value of the diffCount variable is added to 1 for 5 continuous times then its value must be greater than 5, when a request will be sent out to the server side-perform the inquiry and finally show the results. The following gives the complete source code associated with the checkInput function:
function checkInput(){
var val = el("searchword").value;
if (val != curWord && lastValue == val){
diffCount++;
}
else
{
diffCount = 0;
}
if (diffCount > 5) {
diffCount = 0;
curWord = val;
GetResultXml();
}
lastValue = val;
setTimeout("checkInput()", 100);
}
Moreover, there is also one point that needs to be optimized. In the above solution, up to now, we have not added a hint message to show the number of the articles that meet the search condition. So, we work out another helper function, named GetResultCount, and also its server-side counterpart, the Ajax method "MySearchEngine.GetResultCount" which was mentioned before. Thus, here we only list the client-side function:
function GetResultCount(){
var strWord = el("searchword").value;
var count = MySearchEngine.GetResultCount(strWord).value;
el("resultcount").innerHTML ="There are totally " + count + "articles that meet the searching condition.";
}
Here, we first get the value of the <input> element. Then we invoke the server-side function "MySearchEngine.GetResultCount" synchronously to get the number of articles that meet the search condition. At last, we display the matching number-related message to the users.
Finally, you can press F5 to appreciate your work. The following Figure 2 shows one of the run-time snapshots.
Figure 2-one of the run-time snapshots of our search engine sample application.
In Figure 2, when the user enters some key words and waits for a very short amount of time (measured in milliseconds) a table will appear before him, displaying the data that meet the specified search condition. Additionally, a friendly "hint" message appears above the table to tell him how many items of data meet the search condition.
What's more, from the above Figure 2, clever readers may want to add a "Search" like button. That's a pretty good idea! But forgive me for leaving that as your homework.
Summary
In this article, with the help of the AjaxPro.NET server-side AJAX framework, we built a simple search engine which is based on the server-side database. Since XML is a standard format for transferring data and XSLT is a standard language for formatting and decorating XML data, we converted the original data persisted in the database (the XSLT data simply stored under a sub folder of the web site) into XML-formatted strings and passed them back via AjaxPro.NET.
Then, on the client side, with the help of AJAXSLT, the Google client-side AJAX framework, we reverted the XML-formatted strings to original XML/XSLT data. And further, by using AJAXSLT we turned the XML data into an HTML format that could finally be rendered on the browser.
Since this is just a demo for exploring local search engine techniques, the example in this article can be further enhanced and optimized in many aspects. We've only worked out two means of optimization for it. However, the optimization of this search engine is only limited by your imagination.