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