Skip to content

Instantly share code, notes, and snippets.

@jacquesbh
Last active October 25, 2016 04:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jacquesbh/c6283faab001d724806c1639a821ff7c to your computer and use it in GitHub Desktop.
Save jacquesbh/c6283faab001d724806c1639a821ff7c to your computer and use it in GitHub Desktop.
Patch for fulltext search issue after upgrading to Magento 1.9.3.0
From: Jacques Bodin-Hullin <j.bodinhullin@monsieurbiz.com>
Date: Thu, 13 Oct 2016 16:31:32 +0200
Subject: [PATCH] Fix fulltext search issue after upgrade to Magento 1.9.3.0
---
.../Mage/CatalogSearch/Model/Resource/Fulltext.php | 866 +++++++++++++++++++++
.../Model/Resource/Fulltext/Collection.php | 258 ++++++
2 files changed, 1124 insertions(+)
create mode 100644 app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php
create mode 100644 app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php
diff --git a/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php b/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php
new file mode 100644
index 0000000..30a1cbd
--- /dev/null
+++ app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext.php
@@ -0,0 +1,866 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magento.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magento.com for more information.
+ *
+ * @category Mage
+ * @package Mage_CatalogSearch
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+ */
+
+
+/**
+ * CatalogSearch Fulltext Index resource model
+ *
+ * @category Mage
+ * @package Mage_CatalogSearch
+ * @author Magento Core Team <core@magentocommerce.com>
+ */
+class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resource_Db_Abstract
+{
+ /**
+ * Searchable attributes cache
+ *
+ * @var array
+ */
+ protected $_searchableAttributes = null;
+
+ /**
+ * Index values separator
+ *
+ * @var string
+ */
+ protected $_separator = '|';
+
+ /**
+ * Array of Zend_Date objects per store
+ *
+ * @var array
+ */
+ protected $_dates = array();
+
+ /**
+ * Product Type Instances cache
+ *
+ * @var array
+ */
+ protected $_productTypes = array();
+
+ /**
+ * Store search engine instance
+ *
+ * @var object
+ */
+ protected $_engine = null;
+
+ /**
+ * Whether table changes are allowed
+ *
+ * @deprecated after 1.6.1.0
+ * @var bool
+ */
+ protected $_allowTableChanges = true;
+
+ /**
+ * @var array
+ */
+ protected $_foundData = array();
+
+ /**
+ * Init resource model
+ *
+ */
+ protected function _construct()
+ {
+ $this->_init('catalogsearch/fulltext', 'product_id');
+ $this->_engine = Mage::helper('catalogsearch')->getEngine();
+ }
+
+ /**
+ * Return options separator
+ *
+ * @return string
+ */
+ public function getSeparator()
+ {
+ return $this->_separator;
+ }
+
+ /**
+ * Regenerate search index for store(s)
+ *
+ * @param int|null $storeId
+ * @param int|array|null $productIds
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function rebuildIndex($storeId = null, $productIds = null)
+ {
+ if (is_null($storeId)) {
+ $storeIds = array_keys(Mage::app()->getStores());
+ foreach ($storeIds as $storeId) {
+ $this->_rebuildStoreIndex($storeId, $productIds);
+ }
+ } else {
+ $this->_rebuildStoreIndex($storeId, $productIds);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Regenerate search index for specific store
+ *
+ * @param int $storeId Store View Id
+ * @param int|array $productIds Product Entity Id
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ protected function _rebuildStoreIndex($storeId, $productIds = null)
+ {
+ $this->cleanIndex($storeId, $productIds);
+
+ // prepare searchable attributes
+ $staticFields = array();
+ foreach ($this->_getSearchableAttributes('static') as $attribute) {
+ $staticFields[] = $attribute->getAttributeCode();
+ }
+ $dynamicFields = array(
+ 'int' => array_keys($this->_getSearchableAttributes('int')),
+ 'varchar' => array_keys($this->_getSearchableAttributes('varchar')),
+ 'text' => array_keys($this->_getSearchableAttributes('text')),
+ 'decimal' => array_keys($this->_getSearchableAttributes('decimal')),
+ 'datetime' => array_keys($this->_getSearchableAttributes('datetime')),
+ );
+
+ // status and visibility filter
+ $visibility = $this->_getSearchableAttribute('visibility');
+ $status = $this->_getSearchableAttribute('status');
+ $statusVals = Mage::getSingleton('catalog/product_status')->getVisibleStatusIds();
+ $allowedVisibilityValues = $this->_engine->getAllowedVisibility();
+
+ $websiteId = Mage::app()->getStore($storeId)->getWebsite()->getId();
+ $lastProductId = 0;
+ while (true) {
+ $products = $this->_getSearchableProducts($storeId, $staticFields, $productIds, $lastProductId);
+ if (!$products) {
+ break;
+ }
+
+ $productAttributes = array();
+ $productRelations = array();
+ foreach ($products as $productData) {
+ $lastProductId = $productData['entity_id'];
+ $productAttributes[$productData['entity_id']] = $productData['entity_id'];
+ $productChildren = $this->_getProductChildrenIds($productData['entity_id'],
+ $productData['type_id'], $websiteId);
+ $productRelations[$productData['entity_id']] = $productChildren;
+ if ($productChildren) {
+ foreach ($productChildren as $productChildId) {
+ $productAttributes[$productChildId] = $productChildId;
+ }
+ }
+ }
+
+ $productIndexes = array();
+ $productAttributes = $this->_getProductAttributes($storeId, $productAttributes, $dynamicFields);
+ foreach ($products as $productData) {
+ if (!isset($productAttributes[$productData['entity_id']])) {
+ continue;
+ }
+
+ $productAttr = $productAttributes[$productData['entity_id']];
+ if (!isset($productAttr[$visibility->getId()])
+ || !in_array($productAttr[$visibility->getId()], $allowedVisibilityValues)
+ ) {
+ continue;
+ }
+ if (!isset($productAttr[$status->getId()]) || !in_array($productAttr[$status->getId()], $statusVals)) {
+ continue;
+ }
+
+ $productIndex = array(
+ $productData['entity_id'] => $productAttr
+ );
+
+ $hasChildren = false;
+ if ($productChildren = $productRelations[$productData['entity_id']]) {
+ foreach ($productChildren as $productChildId) {
+ if (isset($productAttributes[$productChildId])) {
+ $productChildAttr = $productAttributes[$productChildId];
+ if (!isset($productChildAttr[$status->getId()])
+ || !in_array($productChildAttr[$status->getId()], $statusVals)
+ ) {
+ continue;
+ }
+
+ $hasChildren = true;
+ $productIndex[$productChildId] = $productChildAttr;
+ }
+ }
+ }
+ if (!is_null($productChildren) && !$hasChildren) {
+ continue;
+ }
+
+ $index = $this->_prepareProductIndex($productIndex, $productData, $storeId);
+
+ $productIndexes[$productData['entity_id']] = $index;
+ }
+
+ $this->_saveProductIndexes($storeId, $productIndexes);
+ }
+
+ $this->resetSearchResults();
+
+ return $this;
+ }
+
+ /**
+ * Retrieve searchable products per store
+ *
+ * @param int $storeId
+ * @param array $staticFields
+ * @param array|int $productIds
+ * @param int $lastProductId
+ * @param int $limit
+ * @return array
+ */
+ protected function _getSearchableProducts($storeId, array $staticFields, $productIds = null, $lastProductId = 0,
+ $limit = 100)
+ {
+ $websiteId = Mage::app()->getStore($storeId)->getWebsiteId();
+ $writeAdapter = $this->_getWriteAdapter();
+
+ $select = $writeAdapter->select()
+ ->useStraightJoin(true)
+ ->from(
+ array('e' => $this->getTable('catalog/product')),
+ array_merge(array('entity_id', 'type_id'), $staticFields)
+ )
+ ->join(
+ array('website' => $this->getTable('catalog/product_website')),
+ $writeAdapter->quoteInto(
+ 'website.product_id=e.entity_id AND website.website_id=?',
+ $websiteId
+ ),
+ array()
+ )
+ ->join(
+ array('stock_status' => $this->getTable('cataloginventory/stock_status')),
+ $writeAdapter->quoteInto(
+ 'stock_status.product_id=e.entity_id AND stock_status.website_id=?',
+ $websiteId
+ ),
+ array('in_stock' => 'stock_status')
+ );
+
+ if (!is_null($productIds)) {
+ $select->where('e.entity_id IN(?)', $productIds);
+ }
+
+ $select->where('e.entity_id>?', $lastProductId)
+ ->limit($limit)
+ ->order('e.entity_id');
+
+ /**
+ * Add additional external limitation
+ */
+ Mage::dispatchEvent('prepare_catalog_product_index_select', array(
+ 'select' => $select,
+ 'entity_field' => new Zend_Db_Expr('e.entity_id'),
+ 'website_field' => new Zend_Db_Expr('website.website_id'),
+ 'store_field' => $storeId
+ ));
+
+ $result = $writeAdapter->fetchAll($select);
+
+ return $result;
+ }
+
+ /**
+ * Reset search results
+ *
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function resetSearchResults()
+ {
+ Mage::dispatchEvent('catalogsearch_reset_search_result');
+ return $this;
+ }
+
+ /**
+ * Delete search index data for store
+ *
+ * @param int $storeId Store View Id
+ * @param int $productId Product Entity Id
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function cleanIndex($storeId = null, $productId = null)
+ {
+ if ($this->_engine) {
+ $this->_engine->cleanIndex($storeId, $productId);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Prepare results for query
+ *
+ * @param Mage_CatalogSearch_Model_Fulltext $object
+ * @param string $queryText
+ * @param Mage_CatalogSearch_Model_Query $query
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function prepareResult($object, $queryText, $query)
+ {
+ $adapter = $this->_getWriteAdapter();
+ $searchType = $object->getSearchType($query->getStoreId());
+
+ $preparedTerms = Mage::getResourceHelper('catalogsearch')
+ ->prepareTerms($queryText, $query->getMaxQueryWords());
+
+ $bind = array();
+ $like = array();
+ $likeCond = '';
+
+ /**
+ * @see http://magento.stackexchange.com/questions/140707/magento-1-9-3-every-product-is-displayed-in-the-search-in-full-text-mode
+ */
+
+ if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE
+ || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
+ ) {
+ $helper = Mage::getResourceHelper('core');
+ $words = Mage::helper('core/string')->splitWords($queryText, true, $query->getMaxQueryWords());
+ foreach ($words as $word) {
+ $like[] = $helper->getCILike('s.data_index', $word, ['position' => 'any']);
+ }
+
+ if ($like) {
+ $likeCond = '(' . join(' OR ', $like) . ')';
+ }
+ }
+
+ $mainTableAlias = 's';
+ $fields = array('product_id');
+
+ $select = $adapter->select()
+ ->from(array($mainTableAlias => $this->getMainTable()), $fields)
+ ->joinInner(array('e' => $this->getTable('catalog/product')),
+ 'e.entity_id = s.product_id',
+ array())
+ ->where($mainTableAlias . '.store_id = ?', (int)$query->getStoreId());
+
+ $where = "";
+ if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_FULLTEXT
+ || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
+ ) {
+ $bind[':query'] = implode(' ', $preparedTerms[0]);
+ $where = Mage::getResourceHelper('catalogsearch')
+ ->chooseFulltext($this->getMainTable(), $mainTableAlias, $select);
+ }
+ if ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) {
+ $where .= ($where ? ' OR ' : '') . $likeCond;
+ } elseif ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE) {
+ $select->columns(array('relevance' => new Zend_Db_Expr(0)));
+ $where = $likeCond;
+ }
+
+ if ($where != '') {
+ $select->where($where);
+ }
+
+ $this->_foundData = $adapter->fetchPairs($select, $bind);
+
+ return $this;
+ }
+
+ /**
+ * Retrieve found data
+ *
+ * @return array
+ */
+ public function getFoundData()
+ {
+ return $this->_foundData;
+ }
+
+ /**
+ * Retrieve EAV Config Singleton
+ *
+ * @return Mage_Eav_Model_Config
+ */
+ public function getEavConfig()
+ {
+ return Mage::getSingleton('eav/config');
+ }
+
+ /**
+ * Retrieve searchable attributes
+ *
+ * @param string $backendType
+ * @return array
+ */
+ protected function _getSearchableAttributes($backendType = null)
+ {
+ if (is_null($this->_searchableAttributes)) {
+ $this->_searchableAttributes = array();
+
+ $productAttributeCollection = Mage::getResourceModel('catalog/product_attribute_collection');
+
+ if ($this->_engine && $this->_engine->allowAdvancedIndex()) {
+ $productAttributeCollection->addToIndexFilter(true);
+ } else {
+ $productAttributeCollection->addSearchableAttributeFilter();
+ }
+ $attributes = $productAttributeCollection->getItems();
+
+ Mage::dispatchEvent('catalogsearch_searchable_attributes_load_after', array(
+ 'engine' => $this->_engine,
+ 'attributes' => $attributes
+ ));
+
+ $entity = $this->getEavConfig()
+ ->getEntityType(Mage_Catalog_Model_Product::ENTITY)
+ ->getEntity();
+
+ foreach ($attributes as $attribute) {
+ $attribute->setEntity($entity);
+ }
+
+ $this->_searchableAttributes = $attributes;
+ }
+
+ if (!is_null($backendType)) {
+ $attributes = array();
+ foreach ($this->_searchableAttributes as $attributeId => $attribute) {
+ if ($attribute->getBackendType() == $backendType) {
+ $attributes[$attributeId] = $attribute;
+ }
+ }
+
+ return $attributes;
+ }
+
+ return $this->_searchableAttributes;
+ }
+
+ /**
+ * Retrieve searchable attribute by Id or code
+ *
+ * @param int|string $attribute
+ * @return Mage_Eav_Model_Entity_Attribute
+ */
+ protected function _getSearchableAttribute($attribute)
+ {
+ $attributes = $this->_getSearchableAttributes();
+ if (is_numeric($attribute)) {
+ if (isset($attributes[$attribute])) {
+ return $attributes[$attribute];
+ }
+ } elseif (is_string($attribute)) {
+ foreach ($attributes as $attributeModel) {
+ if ($attributeModel->getAttributeCode() == $attribute) {
+ return $attributeModel;
+ }
+ }
+ }
+
+ return $this->getEavConfig()->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attribute);
+ }
+
+ /**
+ * Returns expresion for field unification
+ *
+ * @param string $field
+ * @param string $backendType
+ * @return Zend_Db_Expr
+ */
+ protected function _unifyField($field, $backendType = 'varchar')
+ {
+ if ($backendType == 'datetime') {
+ $expr = Mage::getResourceHelper('catalogsearch')->castField(
+ $this->_getReadAdapter()->getDateFormatSql($field, '%Y-%m-%d %H:%i:%s'));
+ } else {
+ $expr = Mage::getResourceHelper('catalogsearch')->castField($field);
+ }
+ return $expr;
+ }
+
+ /**
+ * Load product(s) attributes
+ *
+ * @param int $storeId
+ * @param array $productIds
+ * @param array $attributeTypes
+ * @return array
+ */
+ protected function _getProductAttributes($storeId, array $productIds, array $attributeTypes)
+ {
+ $result = array();
+ $selects = array();
+ $websiteId = Mage::app()->getStore($storeId)->getWebsiteId();
+ $adapter = $this->_getWriteAdapter();
+ $ifStoreValue = $adapter->getCheckSql('t_store.value_id > 0', 't_store.value', 't_default.value');
+ foreach ($attributeTypes as $backendType => $attributeIds) {
+ if ($attributeIds) {
+ $tableName = $this->getTable(array('catalog/product', $backendType));
+ $select = $adapter->select()
+ ->from(
+ array('t_default' => $tableName),
+ array('entity_id', 'attribute_id'))
+ ->joinLeft(
+ array('t_store' => $tableName),
+ $adapter->quoteInto(
+ 't_default.entity_id=t_store.entity_id' .
+ ' AND t_default.attribute_id=t_store.attribute_id' .
+ ' AND t_store.store_id=?',
+ $storeId),
+ array('value' => $this->_unifyField($ifStoreValue, $backendType)))
+ ->where('t_default.store_id=?', 0)
+ ->where('t_default.attribute_id IN (?)', $attributeIds)
+ ->where('t_default.entity_id IN (?)', $productIds);
+
+ /**
+ * Add additional external limitation
+ */
+ Mage::dispatchEvent('prepare_catalog_product_index_select', array(
+ 'select' => $select,
+ 'entity_field' => new Zend_Db_Expr('t_default.entity_id'),
+ 'website_field' => $websiteId,
+ 'store_field' => new Zend_Db_Expr('t_store.store_id')
+ ));
+
+ $selects[] = $select;
+ }
+ }
+
+ if ($selects) {
+ $select = $adapter->select()->union($selects, Zend_Db_Select::SQL_UNION_ALL);
+ $query = $adapter->query($select);
+ while ($row = $query->fetch()) {
+ $result[$row['entity_id']][$row['attribute_id']] = $row['value'];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieve Product Type Instance
+ *
+ * @param string $typeId
+ * @return Mage_Catalog_Model_Product_Type_Abstract
+ */
+ protected function _getProductTypeInstance($typeId)
+ {
+ if (!isset($this->_productTypes[$typeId])) {
+ $productEmulator = $this->_getProductEmulator();
+ $productEmulator->setTypeId($typeId);
+
+ $this->_productTypes[$typeId] = Mage::getSingleton('catalog/product_type')
+ ->factory($productEmulator);
+ }
+ return $this->_productTypes[$typeId];
+ }
+
+ /**
+ * Return all product children ids
+ *
+ * @param $productId
+ * @param $typeId
+ * @param null|int $websiteId
+ * @return array|null
+ */
+ protected function _getProductChildrenIds($productId, $typeId, $websiteId = null)
+ {
+ $typeInstance = $this->_getProductTypeInstance($typeId);
+ $relation = $typeInstance->isComposite()
+ ? $typeInstance->getRelationInfo()
+ : false;
+
+ if ($relation && $relation->getTable() && $relation->getParentFieldName() && $relation->getChildFieldName()) {
+ $select = $this->_getReadAdapter()->select()
+ ->from(
+ array('main' => $this->getTable($relation->getTable())),
+ array($relation->getChildFieldName()))
+ ->where("main.{$relation->getParentFieldName()} = ?", $productId);
+ if (!is_null($relation->getWhere())) {
+ $select->where($relation->getWhere());
+ }
+
+ Mage::dispatchEvent('prepare_product_children_id_list_select', array(
+ 'select' => $select,
+ 'entity_field' => 'main.product_id',
+ 'website_field' => $websiteId
+ ));
+
+ return $this->_getReadAdapter()->fetchCol($select);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return all product children ids
+ *
+ * @param int $productId Product Entity Id
+ * @param string $typeId Super Product Link Type
+ * @return array|null
+ */
+ protected function _getProductChildIds($productId, $typeId)
+ {
+ return $this->_getProductChildrenIds($productId, $typeId);
+ }
+
+ /**
+ * Retrieve Product Emulator (Varien Object)
+ *
+ * @return Varien_Object
+ */
+ protected function _getProductEmulator()
+ {
+ $productEmulator = new Varien_Object();
+ $productEmulator->setIdFieldName('entity_id');
+
+ return $productEmulator;
+ }
+
+ /**
+ * Prepare Fulltext index value for product
+ *
+ * @param array $indexData
+ * @param array $productData
+ * @param int $storeId
+ * @return string
+ */
+ protected function _prepareProductIndex($indexData, $productData, $storeId)
+ {
+ $index = array();
+
+ foreach ($this->_getSearchableAttributes('static') as $attribute) {
+ $attributeCode = $attribute->getAttributeCode();
+
+ if (isset($productData[$attributeCode])) {
+ $value = $this->_getAttributeValue($attribute->getId(), $productData[$attributeCode], $storeId);
+ if ($value) {
+ //For grouped products
+ if (isset($index[$attributeCode])) {
+ if (!is_array($index[$attributeCode])) {
+ $index[$attributeCode] = array($index[$attributeCode]);
+ }
+ $index[$attributeCode][] = $value;
+ }
+ //For other types of products
+ else {
+ $index[$attributeCode] = $value;
+ }
+ }
+ }
+ }
+
+ foreach ($indexData as $entityId => $attributeData) {
+ foreach ($attributeData as $attributeId => $attributeValue) {
+ $value = $this->_getAttributeValue($attributeId, $attributeValue, $storeId);
+ if (!is_null($value) && $value !== false) {
+ $attributeCode = $this->_getSearchableAttribute($attributeId)->getAttributeCode();
+
+ if (isset($index[$attributeCode])) {
+ $index[$attributeCode][$entityId] = $value;
+ } else {
+ $index[$attributeCode] = array($entityId => $value);
+ }
+ }
+ }
+ }
+
+ if (!$this->_engine->allowAdvancedIndex()) {
+ $product = $this->_getProductEmulator()
+ ->setId($productData['entity_id'])
+ ->setTypeId($productData['type_id'])
+ ->setStoreId($storeId);
+ $typeInstance = $this->_getProductTypeInstance($productData['type_id']);
+ if ($data = $typeInstance->getSearchableData($product)) {
+ $index['options'] = $data;
+ }
+ }
+
+ if (isset($productData['in_stock'])) {
+ $index['in_stock'] = $productData['in_stock'];
+ }
+
+ if ($this->_engine) {
+ return $this->_engine->prepareEntityIndex($index, $this->_separator);
+ }
+
+ return Mage::helper('catalogsearch')->prepareIndexdata($index, $this->_separator);
+ }
+
+ /**
+ * Retrieve attribute source value for search
+ *
+ * @param int $attributeId
+ * @param mixed $value
+ * @param int $storeId
+ * @return mixed
+ */
+ protected function _getAttributeValue($attributeId, $value, $storeId)
+ {
+ $attribute = $this->_getSearchableAttribute($attributeId);
+ if (!$attribute->getIsSearchable()) {
+ if ($this->_engine->allowAdvancedIndex()) {
+ if ($attribute->getAttributeCode() == 'visibility') {
+ return $value;
+ } elseif (!($attribute->getIsVisibleInAdvancedSearch()
+ || $attribute->getIsFilterable()
+ || $attribute->getIsFilterableInSearch()
+ || $attribute->getUsedForSortBy())
+ ) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ if ($attribute->usesSource()) {
+ if ($this->_engine->allowAdvancedIndex()) {
+ return $value;
+ }
+
+ $attribute->setStoreId($storeId);
+ $value = $attribute->getSource()->getIndexOptionText($value);
+
+ if (is_array($value)) {
+ $value = implode($this->_separator, $value);
+ } elseif (empty($value)) {
+ $inputType = $attribute->getFrontend()->getInputType();
+ if ($inputType == 'select' || $inputType == 'multiselect') {
+ return null;
+ }
+ }
+ } elseif ($attribute->getBackendType() == 'datetime') {
+ $value = $this->_getStoreDate($storeId, $value);
+ } else {
+ $inputType = $attribute->getFrontend()->getInputType();
+ if ($inputType == 'price') {
+ $value = Mage::app()->getStore($storeId)->roundPrice($value);
+ }
+ }
+
+ $value = preg_replace("#\s+#siu", ' ', trim(strip_tags($value)));
+
+ return $value;
+ }
+
+ /**
+ * Save Product index
+ *
+ * @param int $productId
+ * @param int $storeId
+ * @param string $index
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ protected function _saveProductIndex($productId, $storeId, $index)
+ {
+ if ($this->_engine) {
+ $this->_engine->saveEntityIndex($productId, $storeId, $index);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Save Multiply Product indexes
+ *
+ * @param int $storeId
+ * @param array $productIndexes
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ protected function _saveProductIndexes($storeId, $productIndexes)
+ {
+ if ($this->_engine) {
+ $this->_engine->saveEntityIndexes($storeId, $productIndexes);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve Date value for store
+ *
+ * @param int $storeId
+ * @param string $date
+ * @return string
+ */
+ protected function _getStoreDate($storeId, $date = null)
+ {
+ if (!isset($this->_dates[$storeId])) {
+ $timezone = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE, $storeId);
+ $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $storeId);
+ $locale = new Zend_Locale($locale);
+
+ $dateObj = new Zend_Date(null, null, $locale);
+ $dateObj->setTimezone($timezone);
+ $this->_dates[$storeId] = array($dateObj, $locale->getTranslation(null, 'date', $locale));
+ }
+
+ if (!is_empty_date($date)) {
+ list($dateObj, $format) = $this->_dates[$storeId];
+ $dateObj->setDate($date, Varien_Date::DATETIME_INTERNAL_FORMAT);
+
+ return $dateObj->toString($format);
+ }
+
+ return null;
+ }
+
+
+
+
+
+ // Deprecated methods
+
+ /**
+ * Set whether table changes are allowed
+ *
+ * @deprecated after 1.6.1.0
+ * @param bool $value
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function setAllowTableChanges($value = true)
+ {
+ $this->_allowTableChanges = $value;
+ return $this;
+ }
+
+ /**
+ * Update category products indexes
+ *
+ * deprecated after 1.6.2.0
+ *
+ * @param array $productIds
+ * @param array $categoryIds
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext
+ */
+ public function updateCategoryIndex($productIds, $categoryIds)
+ {
+ return $this;
+ }
+}
diff --git a/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php b/app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php
new file mode 100644
index 0000000..ff3686a
--- /dev/null
+++ app/code/local/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magento.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magento.com for more information.
+ *
+ * @category Mage
+ * @package Mage_CatalogSearch
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+ */
+
+
+/**
+ * Fulltext Collection
+ *
+ * @category Mage
+ * @package Mage_CatalogSearch
+ * @author Magento Core Team <core@magentocommerce.com>
+ */
+class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog_Model_Resource_Product_Collection
+{
+ /**
+ * Name for relevance order
+ */
+ const RELEVANCE_ORDER_NAME = 'relevance';
+
+ /**
+ * Found data
+ *
+ * @var array
+ */
+ protected $_foundData = null;
+
+ /**
+ * Sort order by relevance
+ *
+ * @var null
+ */
+ protected $_relevanceSortOrder = SORT_DESC;
+
+ /**
+ * Sort by relevance flag
+ *
+ * @var bool
+ */
+ protected $_sortByRelevance = false;
+
+ /**
+ * Is search filter applied flag
+ *
+ * @var bool
+ */
+ protected $_isSearchFiltersApplied = false;
+
+ /**
+ * Retrieve query model object
+ *
+ * @return Mage_CatalogSearch_Model_Query
+ */
+ protected function _getQuery()
+ {
+ return Mage::helper('catalogsearch')->getQuery();
+ }
+
+ /**
+ * Add search query filter
+ *
+ * @param $query
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ public function addSearchFilter($query)
+ {
+ return $this;
+ }
+
+ /**
+ * Before load handler
+ *
+ * @return Mage_Catalog_Model_Resource_Product_Collection
+ */
+ protected function _beforeLoad()
+ {
+ if (!$this->_isSearchFiltersApplied) {
+ $this->_applySearchFilters();
+ }
+
+ return parent::_beforeLoad();
+ }
+
+ /**
+ * Get collection size
+ *
+ * @return int
+ */
+ public function getSize()
+ {
+ if (!$this->_isSearchFiltersApplied) {
+ $this->_applySearchFilters();
+ }
+
+ return parent::getSize();
+ }
+
+ /**
+ * Apply collection search filter
+ *
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ protected function _applySearchFilters()
+ {
+ $foundIds = $this->getFoundIds();
+ if (!empty($foundIds)) {
+ $this->addIdFilter($foundIds);
+ } else {
+ /**
+ * @see http://magento.stackexchange.com/questions/140707/magento-1-9-3-every-product-is-displayed-in-the-search-in-full-text-mode
+ */
+ $this->getSelect()->where('FALSE'); // replacement
+ }
+ $this->_isSearchFiltersApplied = true;
+
+ return $this;
+ }
+
+ /**
+ * Get found products ids
+ *
+ * @return array
+ */
+ public function getFoundIds()
+ {
+ if (is_null($this->_foundData)) {
+ /** @var Mage_CatalogSearch_Model_Fulltext $preparedResult */
+ $preparedResult = Mage::getSingleton('catalogsearch/fulltext');
+ $preparedResult->prepareResult();
+ $this->_foundData = $preparedResult->getResource()->getFoundData();
+ }
+ if (isset($this->_orders[self::RELEVANCE_ORDER_NAME])) {
+ $this->_resortFoundDataByRelevance();
+ }
+ return array_keys($this->_foundData);
+ }
+
+ /**
+ * Resort found data by relevance
+ *
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ protected function _resortFoundDataByRelevance()
+ {
+ if (is_array($this->_foundData)) {
+ $data = array();
+ foreach ($this->_foundData as $id => $relevance) {
+ $this->_foundData[$id] = $relevance . '_' . $id;
+ }
+ natsort($this->_foundData);
+ if ($this->_relevanceSortOrder == SORT_DESC) {
+ $this->_foundData = array_reverse($this->_foundData);
+ }
+ foreach ($this->_foundData as $dataString) {
+ list ($relevance, $id) = explode('_', $dataString);
+ $data[$id] = $relevance;
+ }
+ $this->_foundData = $data;
+ }
+ return $this;
+ }
+
+ /**
+ * Set Order field
+ *
+ * @param string $attribute
+ * @param string $dir
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ public function setOrder($attribute, $dir = 'desc')
+ {
+ if ($attribute == 'relevance') {
+ $this->_relevanceSortOrder = ($dir == 'asc') ? SORT_ASC : SORT_DESC;
+ $this->addOrder(self::RELEVANCE_ORDER_NAME);
+ } else {
+ parent::setOrder($attribute, $dir);
+ }
+ return $this;
+ }
+
+ /**
+ * Add sorting by relevance to select
+ *
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ protected function _addRelevanceSorting()
+ {
+ $foundIds = $this->getFoundIds();
+ if (!$foundIds) {
+ return $this;
+ }
+
+ /** @var Mage_CatalogSearch_Model_Resource_Helper_Mysql4 $resourceHelper */
+ $resourceHelper = Mage::getResourceHelper('catalogsearch');
+ $this->_select->order(
+ new Zend_Db_Expr(
+ $resourceHelper->getFieldOrderExpression(
+ 'e.' . $this->getResource()->getIdFieldName(),
+ $foundIds
+ )
+ . ' ' . Zend_Db_Select::SQL_ASC
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * Stub method for compatibility with other search engines
+ *
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
+ */
+ public function setGeneralDefaultQuery()
+ {
+ return $this;
+ }
+
+ /**
+ * Render sql select orders
+ *
+ * @return Varien_Data_Collection_Db
+ */
+ protected function _renderOrders()
+ {
+ if (!$this->_isOrdersRendered) {
+ foreach ($this->_orders as $attribute => $direction) {
+ if ($attribute == self::RELEVANCE_ORDER_NAME) {
+ $this->_addRelevanceSorting();
+ } else {
+ $this->addAttributeToSort($attribute, $direction);
+ }
+ }
+ $this->_isOrdersRendered = true;
+ }
+ return $this;
+ }
+}
--
2.8.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment