Utiliser la recherche Sphinx dans PrestaShop

PrestaShop a un bel outil de recherche, mais parfois il ne répond pas à tous vos besoins. De plus, il n’est pas vraiment rapide et sur les grandes boutiques, la recherche et l’indexation peuvent être très lentes.

Si vous voulez quelque chose de mieux, vous pouvez utiliser un excellent remplacement – Sphinx.


De Wikipedia :

Sphinx est un moteur de recherche en texte intégral (Fulltext) open source/logiciel libre conçu pour fournir des fonctionnalités de recherche en texte intégral aux applications clientes.

Site web de Sphinx – http://sphinxsearch.com/

Installation de Sphinx

J’ai utilisé Debian 8.1. Dans d’autres distributions Linux (et en particulier sous Windows), l’installation peut différer légèrement.
Si Sphinx est disponible dans les dépôts, installez-le simplement :

1
2
3
su
apt-get update
apt-get install sphinxsearch

Si Sphinx n’est pas disponible dans les dépôts, vous pouvez télécharger un programme d’installation depuis le site officiel et installer les dépendances :

1
2
3
apt-get install mysql-client unixodbc libpq5
wget http://sphinxsearch.com/files/sphinxsearch_2.2.9-release-1~wheezy_i386.deb
dpkg -i sphinxsearch_2.2.9-release-1~wheezy_i386.deb

Vous devez remplacer le nom et l’URL du programme d’installation par les versions actuelles pertinentes. Elles sont toutes disponibles sur le site officiel.

Configuration de Sphinx

Ouvrez la configuration de Sphinx et modifiez-la :

1
nano /etc/sphinxsearch/sphinx.conf

Vous devez modifier le bloc par défaut existant « source src1 » (ou ajouter un nouveau bloc). Le résultat devrait ressembler à ceci :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
source PrestaSite
{
type = mysql
sql_host = localhost
# PrestaShop database username
sql_user = DBUSER
# PrestaShop database password
sql_pass = DBPASSWORD
# PrestaShop database name
sql_db = DBNAME
sql_port = 3306 # optional, default is 3306
sql_query_pre = SET NAMES utf8

# MySQL query with list of fields for indexation
sql_query =
SELECT id_product, name, description, description_short
FROM ps_product_lang
}

Tout d’abord, vous pouvez voir les paramètres de connexion à la base de données (n’oubliez pas de les modifier en conséquence). Le paramètre sql_query contient une requête avec les champs à indexer.

Ce sont tous les paramètres de ma configuration. Fondamentalement, le fichier de configuration est bien documenté et toutes les options y sont décrites, vous pouvez donc facilement personnaliser votre configuration.

Ensuite, dans le bloc « index definition », vous devez configurer l’index :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
index PrestaSite
{
# data source for indexation
source = PrestaSite

# location for index data
path = /var/lib/sphinxsearch/data/prestasite

# morphology preprocessors
morphology = stem_en

# minimal word length for indexation
min_word_len = 1
}

Ensuite, vous pouvez voir les blocs « indexer settings » et « searchd settings ». Habituellement, ils sont corrects, vous pouvez simplement les ignorer.

Nous avons donc configuré la source de données (source PrestaSite) et l’index (index PrestaSite).

Indexation

Lancez l’indexeur pour indexer votre base de données :

1
indexer --all

Lancement de Sphinx

N’oubliez pas de démarrer/redémarrer searchd :

1
searchd

Et d’ajouter l’indexeur à la crontab :

1
15 * * * * root indexer --all

L’indexation sera lancée toutes les heures.

Configuration de PrestaShop

Ci-dessous, vous pouvez voir le code pour PrestaShop 1.6. Pour les autres versions de PrestaShop, cela peut différer légèrement.

Créez (ou modifiez) la surcharge de la classe Search :
/override/classes/Search.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
class Search extends SearchCore {
    public static function find($id_lang, $expr, $page_number = 1, $page_size = 1, $order_by = 'position',
        $order_way = 'desc', $ajax = false, $use_cookie = true, Context $context = null)
    {
        if (!$context) {
            $context = Context::getContext();
        }
        $db = Db::getInstance(_PS_USE_SQL_SLAVE_);

        // TODO : smart page management
        if ($page_number < 1) $page_number = 1;
        if ($page_size < 1) $page_size = 1;

        if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way))
            return false;

        if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) {
            return false;
        }

        $start = ($page_number - 1) * $page_size;

        // Sphinx search, get ids of found products
        $sphinx_results = self::getSphinxResults($expr, $start, $page_size);

        $result = null;
        $total = 0;
        // get products by id if something found
        if (is_array($sphinx_results) AND sizeof($sphinx_results) AND isset($sphinx_results['total']) AND $sphinx_results['total'] > 0) {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity,
                    pl.`description_short`, pl.`available_now`, pl.`available_later`, pl.`link_rewrite`, pl.`name`,
                    MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` manufacturer_name, MAX(product_attribute_shop.`id_product_attribute`) id_product_attribute,
                    DATEDIFF(
                        p.`date_add`,
                        DATE_SUB(
                            NOW(),
                            INTERVAL '
.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY
                        )
                    ) > 0 new
                    FROM '
._DB_PREFIX_.'product p
                    '
.Shop::addSqlAssociation('product', 'p').'
                    INNER JOIN `'
._DB_PREFIX_.'product_lang` pl ON (
                        p.`id_product` = pl.`id_product`
                        AND pl.`id_lang` = '
.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').'
                    )
                    LEFT JOIN `'
._DB_PREFIX_.'product_attribute` pa ON (p.`id_product` = pa.`id_product`)
                    '
.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
                    '
.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'
                    LEFT JOIN `'
._DB_PREFIX_.'manufacturer` m ON m.`id_manufacturer` = p.`id_manufacturer`
                    LEFT JOIN `'
._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'.
                Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
                    LEFT JOIN `'
._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang.')
                    WHERE p.`id_product` IN('
.implode(',', $sphinx_results['results']).')
                    GROUP BY product_shop.id_product'
;
            $result = $db->executeS($sql);

            // results count
            $total = $sphinx_results['total'];
        }

        if (!$result) {
            $result_properties = array();
        } else {
            $result_properties = Product::getProductsProperties((int)$id_lang, $result);
        }

        return array('total' => $total, 'result' => $result_properties);
    }

    protected static function getSphinxResults($search_query, $page_number, $page_size)
    {
        $results = array();
        $total = 0;

        if(!$search_query)
            return null;

        // connect to Sphinx database
        $link = mysqli_connect('127.0.0.1', '', '', '', '9306');
        if($link)
        {
            $query = 'SELECT * FROM `PrestaSite` WHERE MATCH(''.pSQL($search_query).'') limit '.(int)$page_number.', '.(int)$page_size.';';
            if ($result = $link->query($query))
            {
                while($query_results = $result->fetch_array())
                {
                    $results = array_merge($results, $query_results);
                }

                /* clear result */
                $result->close();
            }

            // get count of results
            $query_total = 'SELECT count(*) FROM `PrestaSite` WHERE MATCH(''.pSQL($search_query).'');';
            if ($result = $link->query($query_total))
            {
                $total = (int)$result->fetch_array()[0];
                if($total > 1000)
                    $total = 1000;
            }

            mysqli_close($link);
        }

        return array('results' => $results, 'total' => $total);
    }
}

Fondamentalement, le code est assez simple. Nous surchargeons la fonction « find », dans laquelle nous remplaçons la recherche de PrestaShop par la recherche Sphinx. Sphinx renvoie les ID des produits et, en utilisant ces ID, nous interrogeons les données des produits. La deuxième fonction (getSphinxResults) effectue directement la recherche.

À la fin, vous devez vider le cache et supprimer le cache de classe (/cache/class_index.php). Tout est terminé !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *