<commit_before><?php

class Algolia_Algoliasearch_Model_Resource_Fulltext extends Mage_CatalogSearch_Model_Resource_Fulltext
{
    /**
     * Algolia search engine instance
     *
     * @var null|Algolia_Algoliasearch_Model_Resource_Engine
     */
    protected $_engine = NULL;

    /**
     * Algolia search helper
     *
     * @var null|Algolia_Algoliasearch_Helper_Data
     */
    protected $_helper = NULL;

    public function _construct()
    {
        parent::_construct();
        $this->_helper = Mage::helper('algoliasearch');
    }

    /**
     * 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)
    {
        // Fallback to default catalog search if Algolia search is disabled
        if ( ! $this->_helper->isEnabled()) {
            return parent::prepareResult($object, $queryText, $query);
        }

        if (!$query->getIsProcessed() || true) {
            $answer = Mage::helper('algoliasearch')->query(Mage::helper('algoliasearch')->getIndexName(Mage::app()->getStore()->getId()), $queryText, array(
                'hitsPerPage' => 1000, // retrieve all the hits (hard limit is 1000)
                'attributesToRetrieve' => 'objectID',
                'attributesToHighlight' => '',
                'attributesToSnippet' => '',
                'tagFilters' => 'product'
            ));

            $data = array();
            foreach ($answer['hits'] as $i => $hit) {
                $objectIdParts = preg_split('/_/', $hit['objectID']);
                $productId = isset($objectIdParts[1]) ? $objectIdParts[1] : NULL;
                if ($productId) {
                    $data[] = array(
                        'query_id' => $query->getId(),
                        'product_id' => $productId,
                        'relevance' => 1000 - $i,
                    );
                }
            }
            if ($data) {
                $this->_getWriteAdapter()->insertOnDuplicate(
                    $this->getTable('catalogsearch/result'),
                    $data,
                    array('relevance')
                );
            }
            $query->setIsProcessed(1);
        }

        return $this;
    }

    /**
     * Save multiply product indexes.
     *
     * @param int   $storeId
     * @param array $productIndexes
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    protected function _saveProductIndexes($storeId, $productIndexes)
    {
        // Fallback to default catalog search if Algolia search is disabled
        if ( ! $this->_helper->isEnabled()) {
            return parent::_saveProductIndexes($storeId, $productIndexes);
        }

        Mage::helper('algoliasearch')->rebuildStoreProductIndex($storeId, array_keys($productIndexes), $productIndexes);
        return $this;
    }

    /**
     * Delete entity(ies) index
     *
     * @param null|string    $entity catalog|product
     * @param null|int       $storeId
     * @param null|int|array $entityId
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function cleanEntityIndex($entity, $storeId = NULL, $entityId = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'cleanEntityIndex'))) {
            $this->_engine->cleanEntityIndex($entity, $storeId, $entityId);
        }
        return $this;
    }

    /**
     * Rebuild index for the specified categories
     *
     * @param null|int       $storeId
     * @param null|int|array $categoryIds
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function rebuildCategoryIndex($storeId = NULL, $categoryIds = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'rebuildCategoryIndex'))) {
            $this->_engine->rebuildCategoryIndex($storeId, $categoryIds);
        }
        return $this;
    }

    /**
     * Rebuild index for the specified products
     *
     * @param null|int       $storeId
     * @param null|int|array $productIds
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function rebuildProductIndex($storeId = NULL, $productIds = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'rebuildProductIndex'))) {
            $this->_engine->rebuildProductIndex($storeId, $productIds);
        }
        return $this;
    }

    /**
     * Retrieve Searchable attributes
     *
     * @param string $backendType
     * @return array
     */
    public function getSearchableAttributes($backendType = NULL)
    {
        return $this->_getSearchableAttributes($backendType);
    }

    /**
     * Retrieve attribute source value for search
     *
     * @param string $attributeCode
     * @param mixed $value
     * @param int $storeId
     * @param null|string $entity
     * @return mixed
     */
    public function getAttributeValue($attributeCode, $value, $storeId, $entity = 'catalog_category')
    {
        $attribute = Mage::getSingleton('eav/config')->getAttribute($entity, $attributeCode);
        if ( ! $attribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract) {
            return NULL;
        }

        if ($attribute->usesSource()) {
            $attribute->setStoreId($storeId);
            $value = $attribute->getSource()->getOptionText($value);
        }
        if ($attribute->getBackendType() == 'datetime') {
            $value = $this->_getStoreDate($storeId, $value);
        }

        $inputType = $attribute->getFrontend()->getInputType();
        if ($inputType == 'price') {
            $value = Mage::app()->getStore($storeId)->roundPrice($value);
        }

        if (is_array($value)) {
            return array_map(array($this, '_cleanData'), $value);
        } elseif (empty($value) && ($inputType == 'select' || $inputType == 'multiselect')) {
            return NULL;
        }

        return $this->_cleanData($value);
    }

    protected function _cleanData($value)
    {
        return preg_replace("#\s+#siu", ' ', trim(strip_tags($value)));
    }
}
<commit_msg>Lock query entity for transaction to prevent FK error while updating search results.<commit_after><?php

class Algolia_Algoliasearch_Model_Resource_Fulltext extends Mage_CatalogSearch_Model_Resource_Fulltext
{
    /**
     * Algolia search engine instance
     *
     * @var null|Algolia_Algoliasearch_Model_Resource_Engine
     */
    protected $_engine = NULL;

    /**
     * Algolia search helper
     *
     * @var null|Algolia_Algoliasearch_Helper_Data
     */
    protected $_helper = NULL;

    public function _construct()
    {
        parent::_construct();
        $this->_helper = Mage::helper('algoliasearch');
    }

    /**
     * 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)
    {
        try {
            $this->beginTransaction();
            if ( ! $this->lockQueryForTransaction($query)) {
                return $this;
            }

            // Fallback to default catalog search if Algolia search is disabled
            if ( ! $this->_helper->isEnabled()) {
                return parent::prepareResult($object, $queryText, $query);
            }

            if (!$query->getIsProcessed() || true) {
                $answer = Mage::helper('algoliasearch')->query(Mage::helper('algoliasearch')->getIndexName(Mage::app()->getStore()->getId()), $queryText, array(
                    'hitsPerPage' => 1000, // retrieve all the hits (hard limit is 1000)
                    'attributesToRetrieve' => 'objectID',
                    'attributesToHighlight' => '',
                    'attributesToSnippet' => '',
                    'tagFilters' => 'product'
                ));

                $data = array();
                foreach ($answer['hits'] as $i => $hit) {
                    $objectIdParts = preg_split('/_/', $hit['objectID']);
                    $productId = isset($objectIdParts[1]) ? $objectIdParts[1] : NULL;
                    if ($productId) {
                        $data[] = array(
                            'query_id' => $query->getId(),
                            'product_id' => $productId,
                            'relevance' => 1000 - $i,
                        );
                    }
                }
                if ($data) {
                    $this->_getWriteAdapter()->insertOnDuplicate(
                        $this->getTable('catalogsearch/result'),
                        $data,
                        array('relevance')
                    );
                }
                $query->setIsProcessed(1);
            }

            $this->commit();
        } catch (Exception $e) {
            $this->rollBack();
            Mage::logException($e);
        }

        return $this;
    }

    /**
     * Lock query entity for transaction to prevent FK error while updating search results.
     * Use fulltext resource model instead of query resource model to avoid additional override.
     *
     * @param int|Mage_CatalogSearch_Model_Query $queryId
     * @return bool true if query entity was locked
     */
    public function lockQueryForTransaction(Mage_CatalogSearch_Model_Query $queryId)
    {
        if ($queryId instanceof Mage_CatalogSearch_Model_Query) {
            $queryId = $queryId->getId();
        }
        $adapter = $this->_getReadAdapter();
        $select = $adapter->select()->forUpdate(TRUE)
            ->from(array('query' => $this->getTable('catalogsearch/search_query')), array('query_id'))
            ->where('query.query_id = ?', $queryId);
        return ($queryId == $adapter->fetchOne($select));
    }

    /**
     * Save multiply product indexes.
     *
     * @param int   $storeId
     * @param array $productIndexes
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    protected function _saveProductIndexes($storeId, $productIndexes)
    {
        // Fallback to default catalog search if Algolia search is disabled
        if ( ! $this->_helper->isEnabled()) {
            return parent::_saveProductIndexes($storeId, $productIndexes);
        }

        Mage::helper('algoliasearch')->rebuildStoreProductIndex($storeId, array_keys($productIndexes), $productIndexes);
        return $this;
    }

    /**
     * Delete entity(ies) index
     *
     * @param null|string    $entity catalog|product
     * @param null|int       $storeId
     * @param null|int|array $entityId
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function cleanEntityIndex($entity, $storeId = NULL, $entityId = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'cleanEntityIndex'))) {
            $this->_engine->cleanEntityIndex($entity, $storeId, $entityId);
        }
        return $this;
    }

    /**
     * Rebuild index for the specified categories
     *
     * @param null|int       $storeId
     * @param null|int|array $categoryIds
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function rebuildCategoryIndex($storeId = NULL, $categoryIds = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'rebuildCategoryIndex'))) {
            $this->_engine->rebuildCategoryIndex($storeId, $categoryIds);
        }
        return $this;
    }

    /**
     * Rebuild index for the specified products
     *
     * @param null|int       $storeId
     * @param null|int|array $productIds
     * @return Algolia_Algoliasearch_Model_Resource_Fulltext
     */
    public function rebuildProductIndex($storeId = NULL, $productIds = NULL)
    {
        if ($this->_helper->isEnabled($storeId) && is_object($this->_engine) && is_callable(array($this->_engine, 'rebuildProductIndex'))) {
            $this->_engine->rebuildProductIndex($storeId, $productIds);
        }
        return $this;
    }

    /**
     * Retrieve Searchable attributes
     *
     * @param string $backendType
     * @return array
     */
    public function getSearchableAttributes($backendType = NULL)
    {
        return $this->_getSearchableAttributes($backendType);
    }

    /**
     * Retrieve attribute source value for search
     *
     * @param string $attributeCode
     * @param mixed $value
     * @param int $storeId
     * @param null|string $entity
     * @return mixed
     */
    public function getAttributeValue($attributeCode, $value, $storeId, $entity = 'catalog_category')
    {
        $attribute = Mage::getSingleton('eav/config')->getAttribute($entity, $attributeCode);
        if ( ! $attribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract) {
            return NULL;
        }

        if ($attribute->usesSource()) {
            $attribute->setStoreId($storeId);
            $value = $attribute->getSource()->getOptionText($value);
        }
        if ($attribute->getBackendType() == 'datetime') {
            $value = $this->_getStoreDate($storeId, $value);
        }

        $inputType = $attribute->getFrontend()->getInputType();
        if ($inputType == 'price') {
            $value = Mage::app()->getStore($storeId)->roundPrice($value);
        }

        if (is_array($value)) {
            return array_map(array($this, '_cleanData'), $value);
        } elseif (empty($value) && ($inputType == 'select' || $inputType == 'multiselect')) {
            return NULL;
        }

        return $this->_cleanData($value);
    }

    protected function _cleanData($value)
    {
        return preg_replace("#\s+#siu", ' ', trim(strip_tags($value)));
    }
}

{'fraction_non_alphanumeric': 0.12983990408350377, 'fraction_numerical': 0.002115805063826786, 'mean_word_length': 2.2389218821379626, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 128, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1342011', 'n_tokens_mistral': 4019, 'n_tokens_neox': 3910, 'n_words': 757}