Magento Quick Search Fix

When I upgraded my website from magento 1.4.2.0 to magento 1.5.x the quick search started causing issue, when trying to search for particular SKUs or product names to be matched exactly it would return all the products or unrelated results, after spending hours and beating head against the wall, I found the fix and thought to share that right here.

To start with I had to override the Mage_CatalogSearch_Model_Mysql4_Fulltext::prepareResult() resource model, Magento uses FullText indexing to query the results, there are two advantages of this, one it is faster then traditional LIKE and RegExp and on the other hand it is more comprehensive by using MATCH, AGAINST combined with IN BOOLEAN MODE.

To override the module you would first need to create the module initialization file under app/etc/modules folder in my case the code is.

  <?xml version="1.0"?>
<!--
Document   : Namespace_MyCatalogSearch.xml
Created on : September 5, 2011, 4:57 PM
Author     : aftab
Description:
Initialize Namespace_MyCatalogSearch
-->
<config>
<modules>
<Namespace_MyCatalogSearch>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_CatalogSearch />
</depends>
</Namespace_MyCatalogSearch>
</modules>
</config>

Now Create Namespace/MyCatalogSearch folder under app/code/local
and then create config.xml under app/code/local/Namespace/MyCatalogSearch

 <config>
<global>
    <!-- Override Core Model -->
    <models>
        <mycatalogsearch>
            <class>Namespace_MyCatalogSearch_Model</class>
        </mycatalogsearch>
        
        <!-- @override resource models -->
        <catalogsearch_mysql4>
            <rewrite><fulltext>Namespace_MyCatalogSearch_Model_Mysql4_Fulltext</fulltext></rewrite>
        </catalogsearch_mysql4>
    </models>
</global>
</config>

Now finally create the model class Fulltext.php under app/code/local/Namespace/MyCatalogSearch/Model/Mysql4

class Namespace_MyCatalogSearch_Model_Mysql4_Fulltext extends Mage_CatalogSearch_Model_Mysql4_Fulltext
{
    /**
     * Prepare results for query
     *
     * @param Mage_CatalogSearch_Model_Fulltext $object
     * @param string $queryText
     * @param Mage_CatalogSearch_Model_Query $query
     * @return Mage_CatalogSearch_Model_Mysql4_Fulltext
     */
    public function prepareResult($object, $queryText, $query)
    { 
        if (!$query->getIsProcessed()) {
            $searchType = $object->getSearchType($query->getStoreId());

            $stringHelper = Mage::helper('core/string');
            /* @var $stringHelper Mage_Core_Helper_String */

            $bind = array(
                ':query' => "\"$queryText\"" //modified, to escape (+ -) and other operators of IN BOOLEAN MODE for exact match
            );
            $like = array();

            $fulltextCond   = '';
            $likeCond       = '';
            $separateCond   = '';

            if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE
                || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) {
                $words = $stringHelper->splitWords($queryText, true, $query->getMaxQueryWords());
                $likeI = 0;
                foreach ($words as $word) {
                    $like[] = '`s`.`data_index` LIKE :likew' . $likeI;
                    $bind[':likew' . $likeI] = '%' . $word . '%';
                    $likeI ++;
                }
                if ($like) {
                    $likeCond = '(' . join(' OR ', $like) . ')';
                }
            }
            if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_FULLTEXT
                || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) {
                $fulltextCond = 'MATCH (`s`.`data_index`) AGAINST (:query IN BOOLEAN MODE)';
            }
            if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE && $likeCond) {
                $separateCond = ' OR ';
            }

            $sql = sprintf("INSERT INTO `{$this->getTable('catalogsearch/result')}` "
                . "(SELECT STRAIGHT_JOIN '%d', `s`.`product_id`, MATCH (`s`.`data_index`) "
                . "AGAINST (:query IN BOOLEAN MODE) FROM `{$this->getMainTable()}` AS `s` "
                . "INNER JOIN `{$this->getTable('catalog/product')}` AS `e` "
                . "ON `e`.`entity_id`=`s`.`product_id` WHERE (%s%s%s) AND `s`.`store_id`='%d')"
                . " ON DUPLICATE KEY UPDATE `relevance`=VALUES(`relevance`)",
                $query->getId(),
                $fulltextCond,
                $separateCond,
                $likeCond,
                $query->getStoreId()
            );
            //$db = $this->_getWriteAdapter();
            //$db->getProfiler()->setEnabled(true);
            $this->_getWriteAdapter()->query($sql, $bind);
            //print $db->getProfiler()->getLastQueryProfile()->getQuery();
            //print_r($db->getProfiler()->getLastQueryProfile()->getQueryParams());
            //$db->getProfiler()->setEnabled(false);
            $query->setIsProcessed(1);
        }

        return $this;
    }
}

You would need to delete already existing search term in order to see the results, by logging to admin Catalog/Search Terms

2 thoughts on “Magento Quick Search Fix

  1. Hi, i have magento 1.6.x.x running. Now i want to modify prepareResult() to make it work for EXACT search. For example, my product prices are in product names (product 1 $10, product2 $100). So if i quick search “$10”, the product(product2 $100) also comes in result. How can i modify this such way that only “$10” products will come?

    • Hi Asif, this seems to be a bit old, but any way, you would want to use the LIKE for that, so an thing in in product name which appears after $ sign will be the price. hope this helps

Leave a reply to Asif Cancel reply