Magento搜索插件 – 用自带的Zend_Search_Lucene搜索引擎优化magento搜索

如你有这样的需求,就已经知道magento社区版的搜索是多么的不靠谱。这是一个人为地产品缺陷【反正官方不打算改善】

需求

我的需求很简单:
搜索结果准确一点 – 关键词匹配的越多就靠前
热卖的产品排前点 – 相同关键词匹配的情况下 销量大的靠前
需求可以无限…… 这里就先实现下这2个。
本文用 Zend Framework 自带的Zend_Search_Lucene 全文索引来构建magento搜索。

网上改善magento搜索插件大部分用 sphinx来搭建,性能也更好。用Zend_Search_Lucene是为了方便。Zend Framework 自带,无需安装其他的东西就能运行。

开发

用Zend_Search_Lucene,首先得建立索引。索引的内容可以是 标题 关键词 描述 ,任何的产品属性都可以索引。

这里只索引下面几个

  • 标题, 用于搜索
  • SKU, 用于搜索
  • 产品ID, 用于更新 删除索引
  • 销量, 用于排序

建立索引

public function runIndex(){

    $stores = Mage::getModel('core/store')->getCollection();
    //每个store单独索引
    foreach($stores as $store){
        $store_id = $store->getId();

        $index = new Zend_Search_Lucene($this->getIndexPath().$store_id, true);  //每个store单独保存索引
        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());  

        $orderQtyArr = $this->getOrderQty();

        $collection = $this->getProductCollection($store_id);
        foreach($collection as $product){

            $contents = strtolower($product->getName());
            $contents .= '|'. strtolower($product->getSku());

            $orderQty = isset($orderQtyArr[$product_id])? $orderQtyArr[$product_id] : 0;

            $doc = new Zend_Search_Lucene_Document();  
            $doc->addField(Zend_Search_Lucene_Field::UnIndexed('orderqty', $orderQty));  //销售数量,用于排序
            $doc->addField(Zend_Search_Lucene_Field::Text('pid', $product->getId()));  
            $doc->addField(Zend_Search_Lucene_Field::Text('contents', $contents ,'UTF-8'));  

            $index->addDocument($doc);  
            echo '.';

        }
        $index->optimize(); 
        $index->commit();  

    }


}

索引后的文件
索引后的文件
索引的程序 可以单独建立一个shell脚本,方便 管理。

这里用事件来处理索引问题
after_reindex_process_catalogsearch_fulltext事件 用来在 更新默认的catalogsearch_fulltex索引后 也同时更新 本插件的索引
catalog_product_save_after事件 用来在 更新产品后更新对应产品的索引
catalog_product_delete_after_done 事件 用来清除已删除产品的索引

上面3个事件 对应的方法,不懂实现magento事件的自行补脑。

class Rongliang_SearchX_Model_Observer
{

    public function luceneFulltext(){
        Mage::helper("searchx")->runIndex();
    }

    public function updateLuceneFulltext(Varien_Event_Observer $observer){

        $product = $observer->getEvent()->getDataObject();
        Mage::helper("searchx")->updateIndex($product);
    }

    public function deleteLuceneFulltext(Varien_Event_Observer $observer){

        $product = $observer->getEvent()->getProduct();
        Mage::helper("searchx")->deleteIndex($product);

    }

}

更新索引

Zend_Search_Lucene没有更新的方法,其实就是先删除再添加,删除的时候搜索产品ID ,然后删掉。

    //更新索引
    public function updateIndex($product){

        $index = new Zend_Search_Lucene($this->getIndexPath().$product->getStoreId());  
        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());  
        $query = Zend_Search_Lucene_Search_QueryParser::parse($product->getId(), "UTF-8");  
        $index->setDefaultSearchField('pid');
        $hits = $index->find($query);  
        foreach ($hits as $hit){  
            $index->delete($hit->id);  
            Mage::log($hit->id,null,'hit.log');
        }  

        $contents = strtolower($product->getName());
        $contents .= '|'. strtolower($product->getSku());

        $orderQty = $this->getOrderQtyById($product->getId());

        $doc = new Zend_Search_Lucene_Document();  
        $doc->addField(Zend_Search_Lucene_Field::UnIndexed('orderqty', $orderQty));  //销售数量,用于排序
        $doc->addField(Zend_Search_Lucene_Field::Text('pid', $product->getId()));  
        $doc->addField(Zend_Search_Lucene_Field::Text('contents', $contents ,'UTF-8'));  

        $index->addDocument($doc);  

        $index->optimize();  
        $index->commit(); 

    }

接管Magento搜索

重写Mage_CatalogSearch_Model_Resource_Fulltext的方法prepareResult($object, $queryText, $query)
把 从Zend_Search_Lucene 的搜索结果 合成数组 $this->_foundData[产品ID] = 排序 返回

public function prepareResult($object, $queryText, $query)
{
    try{

        $index = new Zend_Search_Lucene(Mage::helper("searchx")->getIndexPath().Mage::app()->getStore()->getId());
        $keyword =strtolower($queryText);
        $this->_foundData = array();

        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
        $userQuery = Zend_Search_Lucene_Search_QueryParser::parse($keyword, "UTF-8");
        $index->setDefaultSearchField('contents');
        //$index->setResultSetLimit(Mage::helper("searchx")->getLimit()); //设置后结果不准确

        $hits = $index->find($userQuery,'score',SORT_DESC,'orderqty',SORT_DESC);

        $i = count($hits);
        $j = 0;
        foreach ($hits as $hit) {
            $this->_foundData[$hit->pid] = $i--;

            $j++;
            if($j>Mage::helper("searchx")->getLimit()){
                break;
            }
        }

    }
    catch(Exception $e){
        //出错,返回默认的搜索
        return parent::prepareResult($object, $queryText, $query);
    }

    return $this;
}

结语

本插件搜索排序可以根据 添加时间 价格 等等,开发应用还是蛮方便的。
Zend_Search_Lucene 的搜索方式请查看官方文档,实现更多需求。这里只有了最简单的一个。
性能没测,也没对比过, 我这边的都是小站,性能没压力。想练手可以 把sphinx换进去,
这里只测试了 magento 1.9.3.1 以上版本,最近几个版本的 CatalogSearch 模块变动很大,请注意

写的匆忙,待完善完善。重构下代码把插件发布出来。

评论

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>