Updated Relevancy Searching with MySQL FULLTEXT!

Edits for fulltext searching in MaianCart 2.1 (Relevancy Filtering)

Are your searches on your store not producing accurate enough results?  Got a lot of similar products?  Then you probably need FULLTEXT SEARCHING.

The following edits are for advanced users only.  You will be editing several php files and MySQL tables, as such, you are advised to make a full back-up of the following files & tables:

Files:
/control/system/search.php
/control/system/advanced-search.php
/control/classes/products.php
/content/language/english/search.php
/control/defined.inc.php
/control/connect.inc.php

MySQL Tables:
mc_products
mc_search_index (Optional - For Testing and Error Checking)

Any references to line numbers are based on the unedited files and will change slightly as you edit along with this tutorial, please be observant!

Notice:

Neither myself (Dan Austin) or David Ian Bennett can guarantee the following changes will not damage your system.  A misplaced character can have extremely adverse affects so you are hereby informed that neither myself, David Ian Bennett or MaianScriptWorld can be held responsible for loss of data, monies, or personal information, or be held responsible for any events occurring as a result of implementing these changes.  It is highly recommended that you test these edits on a developmental version of your site!

Set-Up:
The very first thing we should do when editing our system is to turn on error reporting.  If you hit an error in your code when testing the error reporting feature of MaianCart will help you isolate the problem (Don't forget to change this back when you are done!):

In /control/connect.inc.php:

Line 111:

define('ENABLE_MYSQL_ERRORS', 0);

change to:

define('ENABLE_MYSQL_ERRORS', 1);

In order for us to be able to use the fulltext search capabilities of MySQL we need to alter our mc_products table to create a fulltext index.  This is the index that MySQL will use to perform searches on.  What you choose to be indexed is down to your personal preferences and, due to its complex nature, it is advised you think carefully about which fields you use.  In our set-up we have decided on only matching Product Names and their full descriptions.  Choosing too many fields for your index can have negative effects on the search, as there are more fields to match against and can in fact produce less relevant results.

Using your MySQL editor of choice run the following query on the `mc_products` table (It is advised to run this after you have loaded in all your products for speed):

ALTER TABLE `mc_products` ADD FULLTEXT (pName, pDescription);

Throughout this tutorial I am using pName and pDescription, but any columns from the `mc_products` table can be used.

Now MySQL knows what to match search terms against, we need to set-up MaianCart to pass the search term to MySQL.

First we need to change the way MaianCart search creates keywords in order to be passed through to MySQL.  Open up the /control/system/search.php file and change the following (lines 52-88):

Line 60:

$splitWords  = explode(' ',$_GET['keys']);

Change to;

$splitWords  = explode(',',$_GET['keys']);

This function normally splits individual words in a search string into separate ones, which are then searched individually.  It does this by finding any white spaces and using those to split the string.  We no longer require this functionality so change the separator to one of your choice.  We are using a comma (,) instead.

Now we need to create our new MySQl query to perform the searching.  In order to get a score for each result's accuracy for ordering our results we need to strip the majority of the query out of /control/system/search.php and move it to /control/classes/products.php.

In /control/system/search.php edit the following (on Line 84):

$searchSQL .= mc_defineNewline().($i ? 'OR ' : 'AND (').' LOWER(`pName`) LIKE \'%'.$sw.'%\' OR LOWER(`pCode`) LIKE \'%'.$sw.'%\' OR LOWER(`pDescription`) LIKE \'%'.$sw.'%\' OR LOWER(`pTags`) LIKE \'%'.$sw.'%\' ';
      }
    }
    $searchSQL = $searchSQL.')';
  }

   
Change to;

$searchSQL .= "'.$sw.'";
      }
}
    $searchSQL = $searchSQL.'';
  }

This removes all the MySQL queries from the function, and is necessary for implementing the scoring system.  '$searchSQL' is now only parsing the search words.  We'll reintroduce our MySQL queries in /control/classes/products.php.  Leave /control/system/search.php open for now, as we'll have some more edits to do later.

Open up your /control/classes/products.php file.  It is very important you do not change anything but the following lines as this file controls your store's products and is used in far more functions than just the search.  Find the function buildSearchProducts on line 1802 and make the following changes:

Line 1803;

global $limit,$public_category11,$public_category10,$public_category12,$public_search3,$public_category9,$page,
         $public_category13,$public_category14,$public_category17,$public_category18,$public_category15,$public_category18,
         $public_product9,$public_category28,$public_product42;

         
Change to;

global $limit,$public_category11,$public_category10,$public_category12,$public_search3,$public_category9,$page,
         $public_category13,$public_category14,$public_category17,$public_category18,$public_category15,$public_category18,
         $public_product9,$public_category28,$public_product42,$advsearch;

         
This is adding a new global variable for the advanced search form, which we'll come back to later.

Next we're going to create our search query, this is by far the most important part to get right as all searches depend on this function working properly.

On Line 1811:

    $query = mysql_query("SELECT SQL_CALC_FOUND_ROWS *,DATE_FORMAT(`pDateAdded`,'".$this->settings->mysqlDateFormat."') AS `a_date`,
           `".DB_PREFIX."products`.`id` AS `pid`
           FROM `".DB_PREFIX."products`
           LEFT JOIN `".DB_PREFIX."prod_category`
           ON `".DB_PREFIX."products`.`id` = `".DB_PREFIX."prod_category`.`product`
           WHERE `pEnable`                 = 'yes'
           ".($this->settings->showOutofStock=='no' ? 'AND `pStock` > 0' : '')."
           $search
           GROUP BY `".DB_PREFIX."products`.`id`
           ORDER BY `".DB_PREFIX."products`.`id`
           ") or die(mc_MySQLError(mysql_errno(),mysql_error(),__LINE__,__FILE__));

           
Change to:

$query = mysql_query("SELECT SQL_CALC_FOUND_ROWS *, DATE_FORMAT(`pDateAdded`,'".$this->settings->mysqlDateFormat."') AS `a_date`,
           `".DB_PREFIX."products`.`id` AS `pid`,
           MATCH(`pName`, `pDescription`) AGAINST (".$search.") AS `score`
           FROM `".DB_PREFIX."products`
           LEFT JOIN `".DB_PREFIX."prod_category`
           ON `".DB_PREFIX."products`.`id` = `".DB_PREFIX."prod_category`.`product`
           WHERE `pEnable`                 = 'yes'
           ".($this->settings->showOutofStock=='no' ? 'AND `pStock` > 0' : '')."
           AND MATCH(`pName`, `pDescription`) AGAINST (".$search.")
           $advsearch

           GROUP BY `".DB_PREFIX."products`.`id`
           HAVING `score` > '0'
           ORDER BY `score` DESC

           ") or die(mc_MySQLError(mysql_errno(),mysql_error(),__LINE__,__FILE__));

What did we just do?  We added a variant of the following query to MaianCart's search query (REFERENCE ONLY):

SELECT *,
    MATCH(`pName`, `pDescription`) AGAINST (".$search.") AS `score`
    FROM mc_products
    WHERE
    MATCH(`pName`, `pDescription`) AGAINST (".$search.")

The above query takes our search term ('$search') and matches it against our Product Names and Descriptions.  Each product will generate a score depending on how close a match it is to the search term.  Products where all or part of the search term is matched in BOTH the Product Name AND Description will get higher scores than those that only have matches in the Name OR Description, which in turn get higher scores than those with no matches at all.  As such, more fields to match against is actually less likely to produce accurate scoring, so I advise a maximum of 2.

'$advsearch' is there to place any advanced search parameters at the right place in the query.  Under the current setup, MaianCart will add these additional queries to the search string and break our query.  Go back to /control/system/search.php and make the following changes (lines 90-137):

// Advanced search options..
  $filterArr = array();
  if (!isset($_GET['sKey'])) {
    if (isset($_GET['adv'])) {
      if (isset($_GET['from'],$_GET['to']) && mc_checkValidDate($_GET['from'])!='0000-00-00' && mc_checkValidDate($_GET['to'])!='0000-00-00') {
        $searchSQL .= mc_defineNewline().'AND `pDateAdded` BETWEEN \''.mc_convertCalToSQLFormat($_GET['from']).'\' AND \''.mc_convertCalToSQLFormat($_GET['to']).'\'';
      }
      if (isset($_GET['cat']) && (int)$_GET['cat']>0) {
        $searchSQL           .= mc_defineNewline().'AND `category` = \''.(int)$_GET['cat'].'\'';
        $_SESSION['thisCat']  = (int)$_GET['cat'];
        $filterArr['cat']     = (int)$_GET['cat'];
      }
      if (isset($_GET['brand']) && (int)$_GET['brand']>0) {
        $searchSQL          .= mc_defineNewline().'AND FIND_IN_SET(\''.(int)$_GET['brand'].'\',`pBrands`)';
        $filterArr['brand']  = (int)$_GET['brand'];
      } else {
        if ($SETTINGS->showBrands=='yes' && isset($_GET['brand']) && strpos($_GET['brand'],',')!==false) {
          $searchSQL          .= mc_defineNewline().'AND FIND_IN_SET(\''.$_GET['brand'].'\',`pBrands`)';
          $filterArr['brand']  = $_GET['brand'];
        }
      }
      if ($_GET['price1']>0 || $_GET['price2']>0) {
        // Tidy up prices..
        $_GET['price1']  = number_format(str_replace(array(','),array(''),$_GET['price1']),2,'.','');
        $_GET['price2']  = number_format(str_replace(array(','),array(''),$_GET['price2']),2,'.','');
        // Multiply fields by 100 to remove decimal places..
        $searchSQL      .= mc_defineNewline().'AND IF(`pOffer`>0,`pOffer`,`pPrice`)*100 >= \''.($_GET['price1']*100).'\' AND IF(`pOffer`>0,`pOffer`,`pPrice`)*100 <= \''.($_GET['price2']*100).'\'';
        // If keys are empty, have price points as the search term..
        if (trim($_GET['keys'])=='') {
          $_SESSION['store_SearchResultsPricePoints'] = $_GET['price1'].' - '.$_GET['price2'];
          // We can also skip the download filter..
          define('SKIP_DOWNLOADS_IN_SEARCH', 1);
        }
      }
      if (isset($_GET['download']) && !defined('SKIP_DOWNLOADS_IN_SEARCH')) {
        $searchSQL .= mc_defineNewline().'AND `pDownload` = \''.(in_array($_GET['download'],array('yes','no')) ? $_GET['download'] : 'no').'\'';
      }
      if (isset($_GET['stock'])) {
        $searchSQL .= mc_defineNewline().'AND IF (`pDownload`=\'yes\',`pStock` >= \'0\',`pStock` >= \''.($SETTINGS->searchLowStockLimit>0 ? $SETTINGS->searchLowStockLimit : '1').'\')';
      }
      if (isset($_GET['specials'])) {
        $searchSQL .= mc_defineNewline().'AND `pOffer` > \'0\'';
      }
      if (isset($_GET['sortby']) && in_array($_GET['sortby'],array_keys($orderByItems))) {
        $filterArr['sortby'] = $_GET['sortby'];
      }
    }
  }

Change to;

// Advanced search options..
  $advsearch = '';
  $filterArr = array();
  if (!isset($_GET['sKey'])) {
    if (isset($_GET['adv'])) {
      if (isset($_GET['from'],$_GET['to']) && mc_checkValidDate($_GET['from'])!='0000-00-00' && mc_checkValidDate($_GET['to'])!='0000-00-00') {
        $advsearch .= mc_defineNewline().'AND `pDateAdded` BETWEEN \''.mc_convertCalToSQLFormat($_GET['from']).'\' AND \''.mc_convertCalToSQLFormat($_GET['to']).'\'';
      }
      if (isset($_GET['cat']) && (int)$_GET['cat']>0) {
        $advsearch           .= mc_defineNewline().'AND `category` = \''.(int)$_GET['cat'].'\'';
        $_SESSION['thisCat']  = (int)$_GET['cat'];
        $filterArr['cat']     = (int)$_GET['cat'];
      }
      if (isset($_GET['brand']) && (int)$_GET['brand']>0) {
        $advsearch          .= mc_defineNewline().'AND FIND_IN_SET(\''.(int)$_GET['brand'].'\',`pBrands`)';
        $filterArr['brand']  = (int)$_GET['brand'];
      } else {
        if ($SETTINGS->showBrands=='yes' && isset($_GET['brand']) && strpos($_GET['brand'],',')!==false) {
          $advsearch          .= mc_defineNewline().'AND FIND_IN_SET(\''.$_GET['brand'].'\',`pBrands`)';
          $filterArr['brand']  = $_GET['brand'];
        }
      }
      if ($_GET['price1']>0 || $_GET['price2']>0) {
        // Tidy up prices..
        $_GET['price1']  = number_format(str_replace(array(','),array(''),$_GET['price1']),2,'.','');
        $_GET['price2']  = number_format(str_replace(array(','),array(''),$_GET['price2']),2,'.','');
        // Multiply fields by 100 to remove decimal places..
        $advsearch      .= mc_defineNewline().'AND IF(`pOffer`>0,`pOffer`,`pPrice`)*100 >= \''.($_GET['price1']*100).'\' AND IF(`pOffer`>0,`pOffer`,`pPrice`)*100 <= \''.($_GET['price2']*100).'\'';
        // If keys are empty, have price points as the search term..
        if (trim($_GET['keys'])=='') {
          $_SESSION['store_SearchResultsPricePoints'] = $_GET['price1'].' - '.$_GET['price2'];
          // We can also skip the download filter..
          define('SKIP_DOWNLOADS_IN_SEARCH', 1);
        }
      }
      if (isset($_GET['download']) && !defined('SKIP_DOWNLOADS_IN_SEARCH')) {
        $advsearch .= mc_defineNewline().'AND `pDownload` = \''.(in_array($_GET['download'],array('yes','no')) ? $_GET['download'] : 'no').'\'';
      }
      if (isset($_GET['stock'])) {
        $advsearch .= mc_defineNewline().'AND IF (`pDownload`=\'yes\',`pStock` >= \'0\',`pStock` >= \''.($SETTINGS->searchLowStockLimit>0 ? $SETTINGS->searchLowStockLimit : '1').'\')';
      }
      if (isset($_GET['specials'])) {
        $advsearch .= mc_defineNewline().'AND `pOffer` > \'0\'';
      }
      if (isset($_GET['sortby']) && in_array($_GET['sortby'],array_keys($orderByItems))) {
        $filterArr['sortby'] = $_GET['sortby'];
      }
    }
  }

This places the advanced search parameters in the right place without breaking the query.

Great!  But we're still not there yet.  Because MaianCart logs searches, we've got some more work to do.

What MaianCart does when it logs searches is take the results from our search and save them to a table (mc_search_index).  This allows MaianCart to cache searches and reduce load on the server (Identical searches don't get searched twice, and saves processing power), as well as logging the search term.  What does this mean?  Well MaianCart actually passes this cached search to another function to be displayed on the site.  As such, our scores, which are vital to the whole search functionality, get lost!  I was stuck on this for a very long time before the solution became apparent.

Open up /control/classes/products.php again and navigate find lines 1651-1682:

// Ordering and filtering..
  $cat      = '';
  $orderBy  = 'ORDER BY '.SEARCH_ORDER_BY;
  switch ($_GET['filter']) {
    case 'price-low':
    $orderBy  = 'ORDER BY REPLACE(IF(`'.DB_PREFIX.'products`.`pOffer`>0,`'.DB_PREFIX.'products`.`pOffer`,`'.DB_PREFIX.'products`.`pPrice`),\',\',\'\')*100';
    break;
    case 'price-high':
    $orderBy  = 'ORDER BY REPLACE(IF(`'.DB_PREFIX.'products`.`pOffer`>0,`'.DB_PREFIX.'products`.`pOffer`,`'.DB_PREFIX.'products`.`pPrice`),\',\',\'\')*100 DESC';
    break;
    case 'title-az':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName`';
    break;
    case 'title-za':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName` DESC';
    break;
    case 'date-new':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pDateAdded` DESC';
    break;
    case 'date-old':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pDateAdded`';
    break;
    case 'all-items':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName`';
    $limit    = 0;
    break;
    case 'stock':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pStock`';
    break;
    case 'multi-buy':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pMultiBuy` DESC';
    break;
  }

Change to;

// Ordering and filtering..
  $cat      = '';
  $orderBy  = 'ORDER BY '.SEARCH_ORDER_BY;
  switch ($_GET['filter']) {
    case 'Relevancy':
    $orderBy  = 'ORDER BY FIND_IN_SET(`'.DB_PREFIX.'products`.`id`, "'.unserialize($searchProds->results).'")';
    break;

    case 'price-low':
    $orderBy  = 'ORDER BY REPLACE(IF(`'.DB_PREFIX.'products`.`pOffer`>0,`'.DB_PREFIX.'products`.`pOffer`,`'.DB_PREFIX.'products`.`pPrice`),\',\',\'\')*100';
    break;
    case 'price-high':
    $orderBy  = 'ORDER BY REPLACE(IF(`'.DB_PREFIX.'products`.`pOffer`>0,`'.DB_PREFIX.'products`.`pOffer`,`'.DB_PREFIX.'products`.`pPrice`),\',\',\'\')*100 DESC';
    break;
    case 'title-az':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName`';
    break;
    case 'title-za':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName` DESC';
    break;
    case 'date-new':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pDateAdded` DESC';
    break;
    case 'date-old':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pDateAdded`';
    break;
    case 'all-items':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pName`';
    $limit    = 0;
    break;
    case 'stock':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pStock`';
    break;
    case 'multi-buy':
    $orderBy  = 'ORDER BY `'.DB_PREFIX.'products`.`pMultiBuy` DESC';
    break;
  }

Now I'll explain what you just did.  You added this (REFERENCE ONLY):

case 'Relevancy':
$orderBy  = 'ORDER BY FIND_IN_SET(`'.DB_PREFIX.'products`.`id`, "'.unserialize($searchProds->results).'")';
break;

This line of code is unbelievably important.  Our cached results are sitting in our mc_search_index table in the correct order (I.E The most relevant product first) and we need to display them retaining this order.  When a user selects Relevancy as their filter for search results, MaianCart is going to order them as it reads them from the cache.  The default setup of MySQL ignores the order so we have to force it by using the FIND_IN_SET function.  Using the above lines of code we add this 'Order By' query on the end of our Search Query function.  Don't forget the break at the end or it won't work!

Testing:

If you want to make sure the search is ordering the results correctly make the following changes to /control/classes/products.php.  You should remove these edits when you are done as they will decrease performance.

Lines 1822-1849:

      // Log search..only log on page 1 of search page.
  if ($search && $this->settings->enSearchLog=='yes' && trim($_GET['keys'])!='' && $page==1) {
    $c = mysql_fetch_object(mysql_query("SELECT FOUND_ROWS() AS rows"));
  }
  while ($PR = mysql_fetch_object($query)) {
    $products[] = $PR->pid;
  }
  mc_logSearchResults($_GET['keys'],(isset($c->rows) ? $c->rows : '0'));
  if (!empty($products)) {
    $ids = serialize(implode(',',array_unique($products)));
    // Check to see if search already exists..
    // If it does, just return key..
    $oldSearch = mc_getTableData('search_index','`results`',$ids);
    if (isset($oldSearch->id)) {
      $key = $oldSearch->searchCode;
    } else {
      // Log key info..
      mysql_query("INSERT INTO `".DB_PREFIX."search_index` (
      `searchCode`,`results`,`searchDate`,`filters`
      ) VALUES (
      '{$key}','{$ids}','".date("Y-m-d")."','".(!empty($filters) ? serialize($filters) : '')."'
      )") or die(mc_MySQLError(mysql_errno(),mysql_error(),__LINE__,__FILE__));
    }
  }
  $url = ($this->settings->en_modr=='yes' ? SLUG_SEARCH.'/'.$key.'/0/'.(isset($filters['sortby']) ? $filters['sortby'] : SEARCH_FILTER).'/1/'.($this->settings->appendindex=='yes' ? 'index.html' : '') : 'index.php?sKey='.$key.'&cat=0&filter='.(isset($filters['sortby']) ? $filters['sortby'] : SEARCH_FILTER).'&next=1');
  header("Location: ".$url);
  exit;
}

Change to;

// Log search..only log on page 1 of search page.
  if ($search && $this->settings->enSearchLog=='yes' && trim($_GET['keys'])!='' && $page==1) {
    $c = mysql_fetch_object(mysql_query("SELECT FOUND_ROWS() AS rows"));
  }
  while ($PR = mysql_fetch_object($query)) {
    $products[] = $PR->pid;
    $scores[] = $PR->score;
  }
  mc_logSearchResults($_GET['keys'],(isset($c->rows) ? $c->rows : '0'));
  if (!empty($products)) {
    $ids = serialize(implode(',',array_unique($products)));
    $scores= serialize(implode(',',array_unique($scores)));
    // Check to see if search already exists..
    // If it does, just return key..
    $oldSearch = mc_getTableData('search_index','`results`',$ids);
    if (isset($oldSearch->id)) {
      $key = $oldSearch->searchCode;
    } else {
      // Log key info..
      mysql_query("INSERT INTO `".DB_PREFIX."search_index` (
      `searchCode`,`results`,`searchDate`,`filters`, `scores`
      ) VALUES (
      '{$key}','{$ids}','".date("Y-m-d")."','".(!empty($filters) ? serialize($filters) : '')."','{$scores}'
      )") or die(mc_MySQLError(mysql_errno(),mysql_error(),__LINE__,__FILE__));
    }
  }

Now with your MySQL editor of choice add a new TEXT column at the end of the mc_search_index table called scores.

Now when you run a search the scores field will be populated with the scores in the same order as the products listed in the results field.  They should run from highest to lowest.  If they do not then there is an error in your code!  Remember to remove these changes after you have confirmed the order of results is correct.

Final Changes:

While all of our searches are being given relevancy scores, it is useless if we cannot filter them by relevancy.  The following edits will set up the Relevancy filter for the end users.

Open /content/language/english/search.php:

<?php

/*+++++++++++++++++++++++++++++++++++++++++++++

  Script: Maian Cart
  Programmed & Designed by: David Ian Bennett
  E-Mail: support@maianscriptworld.co.uk
  Software Website: http://www.maiancart.com
  Script Portal: http://www.maianscriptworld.co.uk

  +++++++++++++++++++++++++++++++++++++++++++++
 
  This File: search.php
  Description: English Language File

  +++++++++++++++++++++++++++++++++++++++++++++*/


$public_search            = '{count} Search Result(s) - &quot;{keys}&quot;';
$public_search2           = 'Filter by Category';
$public_search3           = 'Your search found 0 results, please try another search..';
$public_search4           = 'Results Found: {count}';
$public_search5           = 'Enter keywords and/or filter options. At least 1 option must be completed';
$public_search6           = 'Keywords';
$public_search7           = 'Products Added Between ({format})';
$public_search8           = 'Search by Category';
$public_search9           = 'Downloads';
$public_search10          = 'Price Range ({currency})';
$public_search11          = 'Low Stock';
$public_search12          = 'Search Products';
$public_search13          = 'Specify Search Parameters';
$public_search14          = 'Order/Filter by';
$public_search15          = 'Save This Search';
$public_search16          = '{website} Site Search';
$public_search17          = 'Title: A - Z';
$public_search18          = 'Price: Low - High';
$public_search19          = 'Title: Z - A';
$public_search20          = 'Price: High - Low';
$public_search21          = 'Date: Newest';
$public_search22          = 'Date: Oldest';
$public_search23          = '{count} Search Result(s)';
$public_search24          = 'View All Results';

?>

Change to;

<?php

/*+++++++++++++++++++++++++++++++++++++++++++++

  Script: Maian Cart v2.0
  Programmed & Designed by: David Ian Bennett
  E-Mail: support@maianscriptworld.co.uk
  Software Website: http://www.maiancart.com
  Script Portal: http://www.maianscriptworld.co.uk

  +++++++++++++++++++++++++++++++++++++++++++++
 
  This File: search.php
  Description: English Language File

  +++++++++++++++++++++++++++++++++++++++++++++*/


$public_search            = '{count} Search Result(s) - &quot;{keys}&quot;';
$public_search2           = 'Filter by Category';
$public_search3           = 'Your search found 0 results, please try another search..';
$public_search4           = 'Results Found: {count}';
$public_search5           = 'Enter keywords and/or filter options. At least 1 option must be completed';
$public_search6           = 'Keywords';
$public_search7           = 'Products Added Between ({format})';
$public_search8           = 'Search by Category';
$public_search9           = 'Downloadable Products ONLY';
$public_search10          = 'Price Range';
$public_search11          = 'Low Stock Products ONLY ({count} or less)';
$public_search12          = 'Search Products';
$public_search13          = 'Specify Search Parameters';
$public_search14          = 'Order/Filter by';
$public_search15          = 'Save This Search';
$public_search16          = '{website} Site Search';
$public_search17          = 'Title: A - Z';
$public_search18          = 'Price: Low - High';
$public_search19          = 'Title: Z - A';
$public_search20          = 'Price: High - Low';
$public_search21          = 'Date: Newest';
$public_search22          = 'Date: Oldest';
$public_search23          = '{count} Search Result(s)';
$public_search24          = 'View All Results';
$public_search25          = 'Multi-Buy';
$public_search26          = 'Relevancy';

?>

Note: $public_search25 was missing in our search.php language file.  I have added it here under the belief it is a bug.

On Line 28 of /control/system/search.php:

$orderByItems = array(
  'price-low'  => $public_search18,
  'price-high' => $public_search20,
  'title-az'   => $public_search17,
  'title-za'   => $public_search19,
  'date-new'   => $public_search21,
  'date-old'   => $public_search22,
  'multi-buy'  => $public_search25
);

Change to;

$orderByItems = array(
  'Relevancy'  => $public_search26,
  'price-low'  => $public_search18,
  'price-high' => $public_search20,
  'title-az'   => $public_search17,
  'title-za'   => $public_search19,
  'date-new'   => $public_search21,
  'date-old'   => $public_search22,
  'multi-buy'  => $public_search25
);

And in /control/system/advanced-search.php on line 36:

$orderByItems = array(
  'price-low'  => $public_search18,
  'price-high' => $public_search20,
  'title-az'   => $public_search17,
  'title-za'   => $public_search19,
  'date-new'   => $public_search21,
  'date-old'   => $public_search22,
  'multi-buy'  => $public_search25
);

Change to:

$orderByItems = array(
  'Relevancy'  => $public_search26,
  'price-low'  => $public_search18,
  'price-high' => $public_search20,
  'title-az'   => $public_search17,
  'title-za'   => $public_search19,
  'date-new'   => $public_search21,
  'date-old'   => $public_search22,
  'multi-buy'  => $public_search25
);

These changes make the Relevancy filter an option in the drop down lists on the front-end of your site.

Finally, although Relevancy is an option for users to select, it currently requires them to actually select it.  If you want your results to be filtered by Relevancy by default, open up your /control/defined.inc.php and change:

Line 143:

define('SEARCH_FILTER', 'title-az');

Change to;

define('SEARCH_FILTER', 'Relevancy');

And that's it!  Go take your new search system for a test drive and see how it handles!  Don't forget to go back and take out the error reporting and mc_search_index changes!

- Dan Austin