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 ashit){  
            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 模块变动很大,请注意

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