Thanks to visit codestin.com
Credit goes to gist.github.com

Skip to content

Instantly share code, notes, and snippets.

@codedokode
Last active January 17, 2025 02:23
Show Gist options
  • Select an option

  • Save codedokode/10539366 to your computer and use it in GitHub Desktop.

Select an option

Save codedokode/10539366 to your computer and use it in GitHub Desktop.

Revisions

  1. codedokode revised this gist Oct 3, 2015. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions sql-test.php
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,10 @@
    <?php
    error_reporting(-1);
    // номер порта берется из конфига сфикса а не наугад
    // Если вместо 127.0.0.1 написать localhost, то под линуксом PDO может приконнектиться к MySQL через юникс-сокет,
    // игнорируя указанный номер порта (MySQL исплоьзует порт 3306 в отличие от сфинкса)
    // Если вместо 127.0.0.1 написать localhost, то под линуксом PDO может приконнектиться к MySQL
    // вместо сфинкса через юникс-сокет, игнорируя указанный номер порта (MySQL использует
    // порт 3306 в отличие от сфинкса)
    // Мануал: http://php.net/manual/ru/ref.pdo-mysql.connection.php#refsect1-ref.pdo-mysql.connection-notes
    $pdo = new PDO('mysql:host=127.0.0.1;port=9306');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  2. codedokode revised this gist Oct 3, 2015. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion sql-test.php
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,9 @@
    <?php
    error_reporting(-1);
    // номер порта берется из конфига сфикса а не наугад
    $pdo = new PDO('mysql:host=localhost;port=9306');
    // Если вместо 127.0.0.1 написать localhost, то под линуксом PDO может приконнектиться к MySQL через юникс-сокет,
    // игнорируя указанный номер порта (MySQL исплоьзует порт 3306 в отличие от сфинкса)
    $pdo = new PDO('mysql:host=127.0.0.1;port=9306');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Ищем слово «курс» в индексе новостей
  3. codedokode renamed this gist Apr 12, 2014. 1 changed file with 0 additions and 0 deletions.
  4. codedokode revised this gist Apr 12, 2014. 3 changed files with 106 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions api-test.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    <?php

    error_reporting(-1);
    require __DIR__.'/api/sphinxapi.php';

    $client = new SphinxClient();

    // данные берем из конфига
    $client->SetServer('localhost', 9312);
    $client->SetConnectTimeout(1);
    $client->SetArrayResult(true);

    $result = $client->Query("черный");
    var_dump($result);

    79 changes: 79 additions & 0 deletions sphinx.conf
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    # Источник данных — MySQL
    # В конфиге источники могут наследоваться. создадим базовый конфиг для любых MySQL-источников
    # чтобы не копипастить данные для подключения
    source base
    {
    type = mysql
    sql_host = localhost
    sql_user = tester
    sql_pass = password
    sql_db = tester
    sql_port = 3306

    # Ставим кодировку при соединении
    sql_query_pre = SET NAMES utf8
    }

    # Теперь настроим источник данных для выборки новостей наследующийся от базового
    source src_news: base
    {
    # Включаем range (выборку больших таблиц по частям)
    sql_range_step = 1000

    # запрос на выборку диапазона id
    sql_query_range = SELECT MIN(id), MAX(id) FROM news

    # запрос на выборку самих новостей для индексации
    # сфинкс понимает даты только в виде числа, так что преобразуем дату в timestamp
    sql_query = \
    SELECT id, topic, header, body, UNIX_TIMESTAMP(added) AS added \
    FROM news WHERE id BETWEEN $start AND $end

    # Сохраняем для каждой новости ее topic и дату в аттрибуты
    sql_attr_uint = topic
    sql_attr_timestamp = added
    }

    # Теперь создаем индекс из данных взятых из источника
    index index_news
    {
    source = src_news
    # где хранить данные
    # не знаю, как писать относительный путь, потому пишу абсолютный
    path = d:/temp/s/data/news
    # где хранить аттрибуты — в индексе (inline) или отдельном файле (extern)
    docinfo = extern
    # Либо sbcs (1-байтовая кодировка) либо utf-8
    charset_type = utf-8
    }

    # Говорим сколько памяти можно использовать при индексации (если недодать то будет ошибка)
    # объем памяти зависит от размера таблицы и опредеояется опытным путем
    indexer
    {
    mem_limit = 20M
    }

    # настройки поискового демона
    searchd
    {
    # на каких портах слушать с бинарным проткоолом
    listen = 9312
    # и с mysql-протоколом
    listen = 9306:mysql41

    # Куда класть логи
    log = d:/temp/s/log/searchd.log
    query_log = d:/temp/s/log/query.log

    read_timeout = 5
    max_children = 30
    pid_file = d:/temp/s/log/searchd.pid
    max_matches = 1000
    seamless_rotate = 1
    preopen_indexes = 1
    unlink_old = 1
    workers = threads # for RT to work
    binlog_path = d:/temp/s/data
    }

    12 changes: 12 additions & 0 deletions sql-test.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    <?php
    error_reporting(-1);
    // номер порта берется из конфига сфикса а не наугад
    $pdo = new PDO('mysql:host=localhost;port=9306');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Ищем слово «курс» в индексе новостей
    // мануал: http://sphinxsearch.com/docs/manual-2.2.1.html#sphinxql-select
    $stmt = $pdo->query("SELECT * FROM index_news WHERE MATCH('курс')");
    $results = $stmt->fetchAll();
    var_dump($results);

  5. codedokode created this gist Apr 12, 2014.
    179 changes: 179 additions & 0 deletions Как установить сфинкс.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,179 @@
    # Как установить и настроить сфинкс.

    Скачиваем сфинкс (берем версию с MySQL и со стеммингом на 15 языков Win32 binaries w/MySQL+PgSQL+libstemmer+id64 support соответствующую битности твоей ОС), распаковываем например в d:\temp\s\

    На этом установка sphinx завершена. В дебиане просто делаем `sudo apt-get install sphinxsearch`.

    Создаем таблицы:

    CREATE TABLE news
    (id INT(10) AUTO_INCREMENT PRIMARY KEY, topic INT(10) NOT NULL, header VARCHAR(200) NOT NULL,
    body TEXT, added TIMESTAMP DEFAULT CURRENT_TIMESTAMP);

    Вбиваем данные:

    INSERT INTO news (topic, header, body) VALUES
    (1, 'Биржевой курс евро приблизился к 46 рублям',
    'Курс евро во второй половине торгов на Московской бирже 21 января приблизился к 46 рублям. К 17:00 по московскому времени европейская валюта подорожала почти на 18 копеек до 45,9795 рубля. Курс доллара к тому же времени вырос на 21 копейку до 33,98 рубля.'),
    (1, 'Найденный в ЮАР алмаз оценили в 15 миллионов долларов',
    'В Южной Африке нашли голубой алмаз, который предварительно оценен в 15-20 миллионов долларов. За эту сумму, по мнению экспертов, камень может быть продан на аукционе, сообщает Reuters.'),
    (2, 'В Петербурге исчез музей-квартира Ленина',
    'Партия «Коммунисты России» потребовала, чтобы власти Санкт-Петербурга восстановили музей, действовавший в последней конспиративной квартире Владимира Ленина. Об этом 21 января сообщается на сайте партии.'),
    (1, 'Hello world', 'Test news for search');

    Пишем конфиг для индексера например в d:\temp\s\sphinx.conf на основе sphinx-min.conf.in, мануала и интуиции (файл `sphinx.conf`, приложен ниже).

    В конфиге упомняуты папки log и data, так что созадем их в d:\temp\s\

    Аттрибуты (`added` и `topic`) — это числа, которые можно сохранить в поисковый индекс и потом использовать их в условии поиска (для поиска в определенной теме или сортировки по дате).

    Уф. Тяжело потрудились. Попробуем теперь запустить индексер, открываем консоль (гайд по использованию консоли https://gist.github.com/codedokode/586dabb540415e0cc3d3 ), переходим в папку сфинкса и печатаем:

    .\bin\indexer --config sphinx.conf --all

    (под линуксом печатаем просто indexer так как там он прописан в системную папку). Если все ОК, выведется текст, и там мы увидим:

    > indexing index 'index_news'...
    >
    > WARNING: collect_hits: mem_limit=20480 kb too low, increasing to 25856 kb
    Мало памяти, надо минимум 25 Мб (и зачем ему столько?)

    > total 4 docs, 1418 bytes
    >
    > total 0.030 sec, 45854 bytes/sec, 129.34 docs/sec
    4 документа из базы проиндексировались

    Ок, индекс создан в папочке data, проверим, работает ли поиск? Набираем

    .\bin\search --config sphinx.conf "курс"

    Вот так печалька, ничего не найдено. Еще бы, виндовая консоль не умеет в utf-8 и коверкает наши буковки. Попробуем английский:

    .\bin\search --config sphinx.conf "hello"

    Сфинкс вернул нам id новости, отлично. Теперь запустим поисковый демон и перейдем к PHP (и к поддержке utf-8). Запускаем демон:

    .\bin\searchd --config sphinx.conf --console

    (`--console` чтобы он не пытался уйти в фоновый режим и его можно было остановить через `Ctrl + C`). Появятся надписи что все хорошо. Из PHP к сфинксу можно подсоединиться 2 способами:

    - через mysql-совместимый протокол подсоединиться к демону как к БД и искать с помощью SQL-запросов
    - использовать sphinx.api.php и бинарный протокол

    Начнем с варианта 1. Пишем `test-sql.php` (код в приложении).

    Запускаем его, из консоли или через браузер и сервер (демон поиска должен быть запущен естественно). Если все верно, в ответ вернется массив вроде такого, с id документа и его аттрибутами:

    array(1) {
    [0] =>
    array(6) {
    'id' =>
    string(1) "1"
    [0] =>
    string(1) "1"
    'topic' =>
    string(1) "1"
    [1] =>
    string(1) "1"
    'added' =>
    string(10) "1390318498"
    [2] =>
    string(10) "1390318498"
    }
    }

    Перейдем к sphinx.api.php (мануал по апи: http://sphinxsearch.com/docs/manual-2.2.1.html#api-reference ). Берем пример кода из папки api и пишем на его основе код `api-test.php` (приложен ниже).

    Запускаем, тоже видим массив с результатами. Хорошо, когда все работает.

    ### Стемминг

    Если мы сейчас попробуем поискать слово с учетом склонения, например «курсом» то ничего не найдем, так как в тексте нет таких слов. Нехорошо. Давай исправлять это при помощи встроенных возможностей сфинкса.

    В сфинксе есть такие возможности преобразования слов при индексировании: lemmatizer (приводит слово в нормальную форму: running -> run), stemmer (отрезает окончания слов, не особо заботясь о логике, business -> busi но зато простой и не требует словаря), фонетические алгоритмы (soundex, metaphone заменяют похожие звуки в слове для поиска с неправильным написанием слова но только в английских словах). Мануал: http://sphinxsearch.com/docs/manual-2.2.1.html#conf-morphology

    Подключим русский и англ. стеммер, прописав в конфиге в секции index_news:

    morphology = stem_ru, stem_en

    Индекс сам себя не обновит, так что останавливаем демон (Ctrl + C), запускаем переиндексацию и перезапускаем демон (когда демон работает в фоновом режиме, можно дописать ключ `--rotate` и индексер сам попросит демона перезагрузить индексы, при этом работа поиска не прервется):

    .\bin\indexer --config sphinx.conf --all

    Теперь попробуем поискать по слову «курса» через PHP-скрипт. Все должно работать.

    Лемматайзер конечно работает качественнее, так что лучше скачать нужные словари и подключить его.

    ### Другие возможности

    Сфинкс умеет еще вырезать HTML-теги и декодировать HTML-сущности (параметр `html_strip`), задавать диапазон символов (`charset_table`), исключения и синонимы (`wordforms` и `exceptions`).

    Можно задать, какие символы являются и не являются частью слова (например чтобы слова разделенные подчеркиванием считались как разные).

    Также, сфинкс может строить сниппеты в результатах поиска (вырезать куски текста рядом с подсвеченным ключевым словом), с помощью функции `BuildExcerpts()` в API.

    ### Autocomplete

    Sphinx можно использовать для реализации автокомплита в поиске. т.е. дополнения по началу слова а также исправления опечаток (мануал http://sphinxsearch.com/blog/2013/05/21/simple-autocomplete-and-correction-suggestion/ ).

    ### Realtime-индексы

    Ок, у нас есть индекс для поиска. Но как обновлять его при добавлении новых данных или удалении старых? Ну с удалением просто, если sphinx вернул нам id несуществующей в таблице записи — значит, она удалена. А как добавлять данные в индекс?

    Для этого используются realtime-индексы. Rt индексы можно обновлять с задержкой в несколько миллисекунд (по крайней мере так говорят авторы), но они не очень эффективно хранят данные и много данных в них лучше не помещать. То, есть мы делаем так:

    - основной индекс, содержит все записи (или все записи старше определенной даты), полностью перестраивается раз в час/в сутки в заивисимости от объема данных
    - RT-индекс, который хранит только новые данные, которых нет в основном индексе, который обновляется в реальном времени и который очищается при переиндексации

    Добавим RT-индекс в конфиг:

    index rt_news
    {
    type = rt
    path = d:/temp/s/data/rt_news

    # Надо описывать все возможные поля для индексирования
    rt_field = header
    rt_field = body

    rt_attr_uint = added
    rt_attr_uint = topic

    # Сколько памяти отведем под индекс (что не поместится, будет храниться на диске)
    rt_mem_limit = 25M
    }

    Ок, теперь перезапускаем индексер и демон. Добавление в Rt-индекс делается SQL-запросами, так что возьмем sql-test.php и чуть-чуть переделаем:

    $st = $pdo->prepare("INSERT INTO rt_news (id, topic, header, body, added) VALUES (?, ?, ?, ?, ?)");
    $st->execute(array(100, 1, 'Проверим realtime index', 'Это текст проверки rt индекса', time()));

    Запустим наш файл. Теперь поиском убедимся, что поиск по слову «проверим» и «realtime» выдает нам результаты. Если ты ищешь через SQL, а не через api, не забудь указать в запросе оба индекса (`SELECT ( FROM index_news, rt_news ...` )).

    Индекс можно обновить через вставку новой записи с таким же id помощью REPLACE, проверим:

    $st = $pdo->prepare("REPLACE INTO rt_news (id, topic, header, body, added) VALUES (?, ?, ?, ?, ?)");
    $st->execute(array(100, 1, 'Черный кот в черном доме', 'здесь ничего нет', time()));

    Убедимся, что теперь ищется слово кот, а по слову realtime резальтотов нет.

    Ну и можно еще удалять записи из RT-индекса, с помощью `DELETE FROM rt_news WHERE id IN(1, 2, 3)`

    ### Поисковые запросы

    Sphinx понимает хитрый синтаксис в поисковых запросах ( http://sphinxsearch.com/docs/manual-2.2.1.html#searching ):

    - оператор ИЛИ: `слово1 | слово2`
    - оператор НЕ: `слово1 -слово2`
    - скобки для группировки
    - и мнгого других опций, которые есть в мануале

    Можно сортировать и группировать результаты по разным аттрибутам.


    ### Доплнительное чтение

    Вот неплохая статья на русском про то, как sphinx обрабатывает слова в тексте и некоторые настройки для этого: http://chakrygin.ru/2013/07/sphinx-search.html