为什么在Magento后台列里搜索某些值时不能使用索引?(例如order#) 当你的网站有几个管理员的时候,这篇简短的文章将非常有用。我在一个项目中有超过2000个有不同角色的管理员(这个数字还在增长)。你可以想像下当大量管理员在使用后台时是有多慢,特别是他们在搜索或者为数据排序时。 2000名管理员对Magento来说是个大数字吗?取决于他们如何使用后台部分,但是他们中的大部分要排列东西,搜索些有时他们并不需要的东西。 在实现新功能前,我们做了一次测试。试图在Magento后台寻找你的销售订单网格中的#: 10。记住有多少搜索结果,花了多长时间。 为什么通过Magento网格搜索花费如此大? 因为当你在搜索语句两侧都使用Like语句时MySQL服务不能使用索引。记住MySQL(innodb)有B-Tree索引。 一个例子,当你想要找出所有使用gmail作为邮箱的客户。你可以搜索Magento后台客户网格的邮箱列,像“@gmail”这样。结果是你找出了所有的gmail邮箱地址。在这个过程中,MySQL浏览所有的客户行和所有的customer_email区域字符在你得到结果前。这种特殊的情况没什么问题,但是当你搜索订单#: 10001时呢? 同样的事情,因为Magento针对特定的列时,经常在你的字符串前后添加"%"。所以如果你想要搜索#: 10001订单,Magento会转换成“%10001%”并在你的字符串前加Like语句。所以在最后Magento会发送这样的语句到MySQL“SELECT customer_email FROM…. WHERE customer_email LIKE ‘%10001%';这可能不是你所期待的,但它就是这么实现的。 如果我们想要改变这种行为并给MySQL(Magento)索引提速。我们需要在后台网格搜索时做些改变。最通用和用户友好的方式就是使用"*"字符替换MySQL的特殊字符"%"。 现在如果你想要使用“like”操作符,你将需要在字符串前、后或两侧添加。所以在我们改变后,你讲能找到所有的gmail通过在你的字符串前后添加"*"。所以,“@gmail”将变成“*@gmail*”。我希望你能看到这背后的思想。 让我们用下面的内容替换app/code/core/Mage/Adminhtml/Block/Widget/Grid.php中的私有方法_addColumnFilterToCollection($column)。我们只影响sales_order网格。 /** * */ protected function _addColumnFilterToCollection($column) { if ($this->getCollection()) { $field = ( $column->getFilterIndex() ) ? $column->getFilterIndex() : $column->getIndex(); if ($column->getFilterConditionCallback()) { call_user_func($column->getFilterConditionCallback(), $this->getCollection(), $column); } else { $cond = $column->getFilter()->getCondition(); /** START WITH CODE FOR A NEW WAY OF SEARCHING * - if you look in app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Filter/Abstract.php * - you'll see that ->getCondition(); returns an array: * public function getCondition() * { * return array('like'=>'%'.$this->_escapeValue($this->getValue()).'%'); * } * - and _escapeValue returns: * protected function _escapeValue($value) * { * return str_replace('_', '\_', $value); * } * - so we'll "unescape" it in case where we have [$cond['eq'] and unset($cond['like']);] */ if ($field && isset($cond)) { if($this->getAction() instanceof Mage_Adminhtml_Sales_OrderController) { if (isset($cond['like'])) { //cover case where like should go to equal; else means that we have at least one * so like should stay... if (!(substr($cond['like'], -2) === '*%') && !(substr($cond['like'], 0,2) === '%*')) { $cond['eq'] = str_replace('%', '', $cond['like']); $cond['eq'] = str_replace('\_', '_', $cond['eq']); //This line was added with new revision unset($cond['like']); } else { if (substr($cond['like'], 0,2) !== '%*') { $cond['like'] = substr($cond['like'],1); } else { $cond['like'] = '%' . substr($cond['like'],2); } if (substr($cond['like'], -2) !== '*%') { $cond['like'] = substr($cond['like'], 0, -1); } else { $cond['like'] = substr($cond['like'], 0, -2) . '%'; } } } } /*END WITH CODE FOR A NEW WAY OF SEARCHING*/ $this->getCollection()->addFieldToFilter($field , $cond); } } } return $this; } 在我们改变Magento核心文件后,再次尝试搜索订单#: 10。现在你能看到多少结果?花了多长时间呢?Magento现在使用order#索引。现在你所能搜索的数字10的类型是:“10”, “*10″, “*10*”和“10*”。注意,只有第一个是同等的,其它的使用了Like语句。 注意,你可以去掉“if($this->getAction() instanceof Mage_Adminhtml_Sales_OrderController) { … }”这个判断来将改变应用到整个后台。 你也可以选择重写Mage_Adminhtml_Block_Widget_Grid类而不是修改Magento核心文件。这个小技巧可以有效地提高我们客户的MySQL服务, (责任编辑:好模板) |