Uso de la búsqueda Sphinx en PrestaShop

PrestaShop tiene una buena herramienta de búsqueda, pero a veces no se adapta a todas sus necesidades. Además, no es realmente rápida y en tiendas grandes la búsqueda y la indexación podrían ser muy lentas.

Si desea algo mejor, puede usar un gran reemplazo: Sphinx.


De Wikipedia:

Sphinx es un motor de búsqueda de texto completo de código abierto/software libre diseñado para proporcionar funcionalidad de búsqueda de texto completo a aplicaciones cliente.

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

Instalación de Sphinx

He usado Debian 8.1. En otras distribuciones de Linux (y especialmente en Windows) la instalación puede diferir ligeramente.
Si Sphinx está disponible en los repositorios, simplemente instálelo:

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

Si Sphinx no está disponible en los repositorios, puede descargar un instalador del sitio web oficial e instalar las dependencias:

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

Tiene que reemplazar el nombre y la url del instalador con las versiones relevantes actuales. Todas están disponibles en el sitio web oficial.

Configuración de Sphinx

Abra la configuración de Sphinx y edítela:

1
nano /etc/sphinxsearch/sphinx.conf

Necesita editar el bloque predeterminado existente «source src1» (o agregar un nuevo bloque). El resultado debería verse así:

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
}

Primero puede ver la configuración para conectarse a la base de datos (no olvide cambiarla a la relevante). El parámetro sql_query contiene una consulta con los campos para la indexación.

Estas son todas las configuraciones de mi archivo de configuración. Básicamente, el archivo de configuración está bien documentado y todas las opciones se describen allí, por lo que podría personalizar fácilmente su configuración.

A continuación, en el bloque «index definition» necesita configurar el índice:

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
}

A continuación puede ver los bloques «indexer settings» y «searchd settings». Por lo general están bien, puede simplemente omitirlos.

Entonces, hemos configurado la fuente de datos (source PrestaSite) y el índice (index PrestaSite).

Indexación

Inicie el indexador para indexar su base de datos:

1
indexer --all

Lanzamiento de Sphinx

No olvide iniciar/reiniciar searchd:

1
searchd

Y agregue el indexador a crontab:

1
15 * * * * root indexer --all

La indexación se iniciará cada hora.

Configuración de PrestaShop

A continuación puede ver el código para PrestaShop 1.6. Para otras versiones de PrestaShop puede diferir ligeramente.

Cree (o edite) el override de la clase 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);
    }
}

Básicamente, el código es bastante simple. Sobrescribimos la función «find», en la que reemplazamos la búsqueda de PrestaShop por la búsqueda de Sphinx. Sphinx devuelve los ids de los productos y mediante el uso de esos ids consultamos los datos de los productos. La segunda función (getSphinxResults) realiza directamente la búsqueda.

Al final, necesita borrar la caché y eliminar la caché de clases (/cache/class_index.php). ¡Todo listo!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *