如你有这样的需求,就已经知道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 模块变动很大,请注意
写的匆忙,待完善完善。重构下代码把插件发布出来。