A Web 2.0 search using YUI
I have always felt that the default Wordpress search was enemic and inaccurate at best, so I finally got around to changing the default search, and returning the results in a more “Web 2.0″ fashion. It could be easily adapted to any blog template since it only requires some YUI includes and a single JS file.
The inspiration for the change came from Jack Slocum’s new Wordpress commenting system that uses the YUI libraries to not only change the look and feel of how you view the comments, but also changes how they are organized. In addition, I wanted to start making some use of the MySQL full text index function that would allow results sorted by relevance and not my publishing date. I’ll highlight some of the requirements and code, but I did do everything long-hand, and kept the style information in-line for ease of reading.
For Wordpress users, the first thing that is required is to create a full text index for searching. I felt that the only columns that were relevant to the search were the “post_title”, and the “post_content” fields. Using any MySQL admin console, use this command to generate the full text index.
CREATE FULLTEXT INDEX ft_index ON wp_posts(post_title,post_content);
Once you have that index, you can perform full text index queries like this:
select * from wp_posts where match(post_title,post_content) against('ajax');
I then created a PHP page that returns XML using a querystring parameter for the select statement. The page is pretty simple, and here is how a basic one would look:
<?
$server = "localhost"; // Name or IP of database server.
$user = "yourUserName"; // username
$pwd = "yourPassword"; // password
$db = "yourDb"; // Name of database to connect to.
$conn = mysql_connect($server, $user, $pwd);
if (!$conn) {
die("mysql_connect() failed");
}
if (!mysql_select_db($db)) {
die("Could not select database named ".$db);
}
$searchQuery = stripslashes(strip_tags($_GET['s']));
$q = '';
$rec = 0;
if ($count == "") $count=20;
if($searchQuery != ""){
$q = "select a.id as post_id,a.post_title,a.post_name,a.comment_count as post_comment_count,LEFT(a.post_content,500) as post_brief,b.cntaccess as post_views,YEAR(a.post_date) as post_year,MONTH(a.post_date) as post_month, DAY(a.post_date) as post_day from wp_posts as a, wp_most_accessed as b where match(post_title,post_content) against('$searchQuery') and post_status='publish' and a.id=b.postnumber";
$rs = mysql_query($q, $conn );
if(!$rs) echo $errResult;
header('Content-type: text/xml');
$recordcount = mysql_numrows($rs);
print ("<?xml version='1.0' encoding='UTF-8'?>");
print ("<searchResults entries='$recordcount'>");
while ($line = mysql_fetch_array($rs, MYSQL_ASSOC)) {
$i=0;
$rec++;
print ("
<post position='$rec'>");
foreach ($line as $col_value) {
$fieldname=mysql_field_name($rs,$i);
print ("<$fieldname>".htmlspecialchars(strip_tags($col_value))."</$fieldname>");
$i++;
}
print ("</post>");
}
print(" </searchResults>");
}
?>
So once you have your site returning XML from your search, in a format like this , you only need to include a few YUI includes like these (you can download the 0.11.4 YUI libraries here):
<script type="text/javascript" src="/yui/yui_0.11.4/build/yahoo/yahoo.js"></script>
<script type="text/javascript" src="/yui/yui_0.11.4/build/connection/connection.js"></script>
<script type="text/javascript" src="/yui/yui_0.11.4/build/event/event.js"></script>
<script type="text/javascript" src="/yui/yui_0.11.4/build/dom/dom.js"></script>
<script type="text/javascript" src="/yui/yui_0.11.4/build/dragdrop/dragdrop-min.js"></script>
<script type="text/javascript" src="/yui/yui_0.11.4/build/animation/animation.js"></script>
<link rel="stylesheet" type="text/css" href="/yui/yui_0.11.4/build/container/assets/container.css" />
<script type="text/javascript" src="/yui/yui_0.11.4/build/container/container.js"></script>
For Wordpress users, I dropped these includes into the “header.php” file of my current theme folder.
It is then in the “footer.php” file that I dropped this include into:
<script type='text/javascript' src='/js/searchJs.js'></script>
The source of the “searchJs.js” can be found here.
Inside the searchJs.js file there are a few things to note. At the very bottom are two YAHOO event handlers that are very critical to the whole project.
YAHOO.util.Event.on('searchform', 'submit', wpSearch.explodeShadow, wpSearch, true);
YAHOO.util.Event.onAvailable('searchform', wpSearch.init, wpSearch);
The first line essentially intercepts the searchForm’s submit event and passes it to the wpSearch.explodeShadow function. The second waits until it can find the searchForm element, and then initializes the wpSearch object. The initialization positions and resizes some of the divs so they are ready to jump out from the text field that you type your search query into.
When the form submittal happens, the wpSearch.explodeShadow function makes a 40% opaque square grow from the upper-left corner of the text search box. When it completes that animation, it then calls the wpSearch.explodeOverlay function which does the same thing with a solid blue background div. When that animation finishes, it call another function to make the search results visible.
The search results are populated when the wpSearch.searchSite function is called from within the explodeShadow function. Since I am creating an asynchronous AJAX request, while we are waiting our .4 seconds for our animations to complete, the search is happening in the background.
Finally is the wpSearch.closeEverything function that reverses it all. Mix in some standard async request handlers, and that is the guts of the search project, warts and all.
I know it works with Opera 9, Firefox 1.5, and IE 6, but let me know if it fails otherwise. You can do the search from the Home page, but I am thinking about adding the search to every page.