Korzystanie z wyszukiwarki Sphinx w PrestaShop

PrestaShop posiada całkiem dobre narzędzie wyszukiwania, ale czasami nie spełnia ono wszystkich Twoich potrzeb. Nie jest też zbyt szybkie, a w dużych sklepach wyszukiwanie i indeksacja mogą być bardzo powolne.

Jeśli potrzebujesz czegoś lepszego, możesz użyć świetnego zamiennika – Sphinx.


Z Wikipedii:

Sphinx to darmowy silnik wyszukiwania pełnotekstowego o otwartym kodzie źródłowym, zaprojektowany w celu zapewnienia funkcjonalności wyszukiwania pełnotekstowego dla aplikacji klienckich.

Strona internetowa Sphinx – http://sphinxsearch.com/

Instalacja Sphinx

Użyłem Debiana 8.1. W innych dystrybucjach Linuksa (a zwłaszcza w Windowsie) instalacja może się nieznacznie różnić.
Jeśli Sphinx jest dostępny w repozytoriach, po prostu go zainstaluj:

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

Jeśli Sphinx nie jest dostępny w repozytoriach, możesz pobrać instalator z oficjalnej strony i zainstalować zależności:

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

Musisz zastąpić nazwę i adres URL instalatora aktualnymi wersjami. Wszystkie są dostępne na oficjalnej stronie internetowej.

Konfiguracja Sphinx

Otwórz konfigurację Sphinx i edytuj ją:

1
nano /etc/sphinxsearch/sphinx.conf

Musisz edytować istniejący domyślny blok „source src1” (lub dodać nowy blok). Wynik powinien wyglądać tak:

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
# Nazwa użytkownika bazy danych PrestaShop
sql_user = DBUSER
# Hasło bazy danych PrestaShop
sql_pass = DBPASSWORD
# Nazwa bazy danych PrestaShop
sql_db = DBNAME
sql_port = 3306 # opcjonalnie, domyślnie 3306
sql_query_pre = SET NAMES utf8

# Zapytanie MySQL z listą pól do indeksacji
sql_query =
SELECT id_product, name, description, description_short
FROM ps_product_lang
}

Najpierw widzisz ustawienia połączenia z bazą danych (nie zapomnij zmienić ich na właściwe). Parametr sql_query zawiera zapytanie z polami do indeksacji.

To są wszystkie ustawienia z mojej konfiguracji. Zasadniczo plik konfiguracyjny jest dobrze udokumentowany i wszystkie opcje są w nim opisane, więc możesz łatwo dostosować swoją konfigurację.

Następnie, w bloku „index definition” musisz skonfigurować indeks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
index PrestaSite
{
# źródło danych do indeksacji
source = PrestaSite

# lokalizacja danych indeksu
path = /var/lib/sphinxsearch/data/prestasite

# preprocesory morfologiczne
morphology = stem_en

# minimalna długość słowa do indeksacji
min_word_len = 1
}

Następnie widzisz bloki „indexer settings” i „searchd settings”. Zazwyczaj są one w porządku, możesz je po prostu pominąć.

Więc skonfigurowaliśmy źródło danych (source PrestaSite) i indeks (index PrestaSite).

Indeksacja

Uruchom indeksator, aby zaindeksować swoją bazę danych:

1
indexer --all

Uruchomienie Sphinx

Nie zapomnij uruchomić/zrestartować searchd:

1
searchd

I dodaj indeksator do crontab:

1
15 * * * * root indexer --all

Indeksacja będzie uruchamiana co godzinę.

Konfiguracja PrestaShop

Poniżej znajduje się kod dla PrestaShop 1.6. W innych wersjach PrestaShop może się on nieznacznie różnić.

Utwórz (lub edytuj) nadpisanie klasy 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);
    }
}

Zasadniczo kod jest dość prosty. Nadpisujemy funkcję „find”, w której zastępujemy wyszukiwanie PrestaShop wyszukiwaniem Sphinx. Sphinx zwraca identyfikatory produktów, a za pomocą tych identyfikatorów pobieramy dane produktów. Druga funkcja (getSphinxResults) bezpośrednio wykonuje wyszukiwanie.

Na koniec musisz wyczyścić pamięć podręczną i usunąć pamięć podręczną klas (/cache/class_index.php). Gotowe!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *