Sphinx - справжнє швидкого пошуку анатолій Ларін

У наш час важко уявити хороший сайт без функції пошуку. При чому не просто пошуку, а з підтримкою морфології. У більшості випадків вистачить підтримки російської та англійської мови.

І ось при розробці чергового проекту стала проблема пошуку. Таблиці в базі не маленькі (від 100 000 записів), та плюс шукати ще потрібно відразу за кількома, так що варіант зі звичайним LIKE '% запит%' відпало саме собою.

Раніше я користувався пошуковим движком mnoGoSearch. але згадавши кострубатість його API (іноді скаладивалось враження, що його писали індуси з 5-ю класами парафіяльної школи) відкинув і цей варіант.

У підсумку, залишилося 2 варіанти:

  1. написати пошук по основі побудови своїх індексів і зберігати ці індекси, наприклад, в BerkleyDB;
  2. не вигадувати велосипед і скористатися одним з найшвидших пошукових движків - Sphinx.

Я вибрав другий варіант :)

До слова, Sphinx - це проект нашого співвітчизника Андрія Аксьонова. Варто зауважити, що у Андрія вистачає сил і часу не тільки постійно покращувати проект, а й активно брати участь в підтримці користувачів, опреатівно відповідаючи на питання на англомовному та російськомовному форумі. За що йому величезне спасибі!

І так, приступимо до практики. Установка стандартна і не викликає ніяких проблем.

Завантажуємо архів з вихідними кодами (остання версія на момент написання статті), розпаковуємо і встановлюємо:

# ./configure
# make
# Make install (під root)

Під FreeBSD виконавши ці 3 команди я отримав продукт готовий до роботи. А ось в Debian довелося ще хвилину повозитися (правда, в перший раз це зайняло півгодини :) і виконати:

# Sudo aptitude install libmysql ++ - dev libmysqlclient15-dev checkinstall

після цього всі прекрасно запрацювало.

підготовка

Тепер все готово для створення індексу (я припускаю, що у вас є якийсь проект з готовою БД). Наша база має наступну структуру:

Будемо шукати за назвою продукту, його опису і тегами. Таким чином нам потрібно створити індекс по цих полях. Для цього створимо 2 каталогу / home / larin / data / - тут будемо зберігати файли індексу і / home / larin / log / - тут логи і конфігураційний файл /home/larin/sphinx.conf

Ось і вся конфігурація, Sphinx готовий почати індексувати ваші дані з величезною швидкістю :) Для повнішої інформації по конфігурації зайдіть на офіційний сайт в розділ документації. Вона англійською мовою, але труднощів викликати не повинна, написана просто і доступно.

Морфологія

Spninx підтримує індексацію (а отже і пошук) з урахуванням морфології російської та англійської мови. Підтримка морфології реалізована за допомогою стемінг.

Стемінг - це процес веде до виділенню основи слова зі складних словоформ.

Ось рядки в конфіги відповідають за конфігурацію:

Індексація

Запускаємо створення індексу:
# Indexer -config /home/larin/sphinx.conf -all

Все, індекс створений! Давайте шукати!

Для налагодження і перевірки працездатності індексу підійде утиліта search. Приклад використання:
# Search -config /home/larin/sphinx.conf шукана фраза

А ось для реальної роботи, потрібно запустити демон сфінкса - searchd:
# Searchd -config /home/larin/sphinx.conf

Все, демон успішно стартував (до речі, не погано було б додати його в автозавантаження :). і тепер ми з ним можемо працювати з наших PHP-скриптів, через офіційне API. Необхідна бібліотека sphinxapi.php знаходиться всередині завантаженого архіву до каталозі api.

приклад скрипта

От і все. І ми в черговий раз (як і у випадку з UML і кешуванням) переконалися, що все геніальне просто :)

Соррі, все зробив як було описано і на англ пішов пошук на ура, а на російській вообсче не шукає.
Видає: Notice: Undefined index: matches in /bhome/part3/03/avenirru/avenir.ru/www/test.php on line 37

У мене 37 рядок це: if ($ result is_array ($ result [ 'matches']))

тобто виходить масив не існує так як нічого не знаходить (((
кто нить стикався з труднощами пошуку російською?

ось ще помітив
при індексації при тих налаштуваннях що описані ші виводиться: indexing index 'news' ...
ERROR: index 'news': sql_query_pre [0]: Unknown system variable 'NAMES' (DSN = mysql: // avenir_ru: ***@baze.avenir.ru: 64000 / avenir_ru).
total 0 docs, 0 bytes
total 0.051 sec, 0.00 bytes / sec, 0.00 docs / sec

Каже що не розуміє рядок: sql_query_pre = SET NAMES cp1251

Яка версія MySQL?

З версією розібрався поставив 5 і ця помилка пропала. Але пошук російською не йде, ток англ

ось мій конф, допоможіть поправити що не так (таблиці і БД в utf8):
source news
type = mysql
sql_host = mysql.baze.avenir.ru
sql_user = avenir_ru
sql_pass = ******
sql_db = avenir_ru
sql_port = 64547

sql_query_pre = SET CHARACTER_SET_RESULTS = utf8
sql_query_pre = SET NAMES utf8
sql_query_pre = SET CHARACTER SET utf8

sql_query = SELECT n.news_id, n.title, n.body FROM `news` n

sql_query_info = SELECT * FROM `news` WHERE` news_id` = $ id
sql_ranged_throttle = 0
>

index news
source = news
path = / bhome / part3 / 03 / avenirru / sphinx / sphinks / var / data / news
docinfo = extern
mlock = 0
morphology = stem_ru, stem_en
min_word_len = 2
charset_type = utf-8
charset_table = 0..9, A..Z-> a..z, _, a..z, U + A8-> U + B8, U + B8, U + C0..U + DF-> U + E0..U + FF, U + E0..U + FF
min_infix_len = 2
enable_star = 1
>

searchd
address = 127.0.0.1
port = 3312
log = /bhome/part3/03/avenirru/sphinx/sphinks/var/searchd.log
query_log = /bhome/part3/03/avenirru/sphinx/sphinks/var/query.log
read_timeout = 5
max_children = 30
pid_file = /bhome/part3/03/avenirru/sphinx/sphinks/var/searchd.pid
max_matches = 1000

// А тепер виводимо ці товари відсортовані за релевантністю
$ Id_list = implode ( ',', $ ids);
$ Sql ​​= sprintf ( 'SELECT * FROM `product` WHERE` product_id` IN (% s) ORDER BY FIELD ( `product_id`,% s)', $ id_list, $ id_list);

// Все далі виконуємо цей запит і насолоджуємося результатами

а цей запит виконуємо яким методом сфінкса.

Цей запит виконується вашим класом роботи з БД, ну або просто функцією mysql_query ())))

))) Спасибо, ось тільки тоді не дуже зрозуміло а в чому приріст швидкості? Якщо виходить виконується аж 2 запити. Перший сам сфінкс в своїх індексах, а потім ще і ми в БД?
Вибачте за дурні питання.)))

))) Спасибо, ось тільки тоді не дуже зрозуміло а в чому приріст швидкості? Якщо виходить виконується аж 2 запити. Перший сам сфінкс в своїх індексах, а потім ще і ми в БД?
Вибачте за дурні питання.)))

Приріст в тому, що Sphinx на відміну від MySQL шукає записи в своєму індексі з величезною швидкістю і по всім полям.
А ми виконуємо всього один запит (той що з IN) який виконується дуже швидко. тому пошук йде по первинному ключу в таблиці. Де ви побачили 2 запити.

При індексації? Так цей процес запускається багато рідше, в порівнянні зі зверненнями до пошуку. У мене на деяких сайтах переіндексація запускається лише раз на добу)

Pol_uha. що ще не зрозуміло?

А якщо ВСЕ потрібні мені поля в таблиці були проіндексовані, чому сфінкс просто відразу не бере їх звідти і не виводить, а доводиться ще робити запит в БД?
Спасибі вам величезне, блог супер. Читаю інші статті.)))

У Sphinx зберігатися індекс за даними, а не самі дані. Розумієте різницю?

Радий, що блог подобається і допомагає)

Розумію)))
А можете помоч в такий завданню:
Є таблиця з назвами тварин і з id міст в яких вони знаходяться)))
Проіндексовані відповідно 2 таблиця: ця і з містами.
Людина ввів черепаха москва або черепахи в москві і т.д.
як мені зробити взаємодію цих 2-х таблиць? Тобто Знаходяться всі черепахи і перевіряються id міст в яких вони живуть і порівнюються з id з таблиці міст звідки беруться назви і порівнюються з 2-м словом, в даному випадку "Москва"?
ПЛЗ. ))))

І так, є 2 таблиці:
town [town_id, town_title] і
animal [animal_id, animal_title, town_id]

Відповідно, для правильного створення індексу треба їх об'єднати. Оскільки шукати ми будемо тварин то первинним ключем в результатах запиту у нас буде animal_id.
отримаємо:
sql_query = SELECT animal_id, animal_title, town_title FROM `animal` a LEFT JOIN` town` t ON a.town_id = t.town_id

Все))) Тепер індекс буде побудований вірно: пошук буде вестися за назвою міста і за назвою звірятка.

З.И. LEFT JOIN зроблений для випадку коли не у всіх звіряток заданий місто проживання.

Привіт))) сварки ще раз. А чи не підкажеш, як в сфінкса зробити абсолютний пошук. Тобто при введенні "чорний гіпопотам"
він не видавав всі записи де написано "чорний" і "гіпопотам" окремо, а тільки де є фраза цілком?

Вся проблема в тому, що звичайний MySQL пошук дуже повільно працює.

Запит виходить виду наступного:

Тобто використовується like '% ...%'. У документації написано, що при використанні like '% ...%' індекси не працюють, а використовується якийсь "Turbo Boyer-Moore algorythm" (начебто).
Працює дуже повільно. Я жодного разу не дочекався закінчення пошуку навіть за двома сутностей.

Пошук за договором - це пошук по трьом. Вирішив Sphinx використовувати. SphinxQL тому, що простіше переписати для нього.

Зараз, як я зрозумів, треба зробити три джерела:
1.) Іст. клієнтів.
2.) Іст. ТС.
3.) Іст. договорів.

І три індексу:
1.) Для клієнта потрібен індекс з 1.
2.) ТС має власника, тобто по МС потрібен індекс з 1 і 2.
3.) Для договорів з 1, 2, 3.

Проблема полягає в тому, що, по-перше, у себе я так і не дочекався закінчення створення індексу, хоча б по клієнтам (але в таблиці всього лише

15500 записів).
Тестовий індекс по таблиці зі 149 записами створюється.
Чому ж так довго? Що я роблю не так?
По-друге, хотілося б отримувати дані, а не ID.
Можливо це зробити засобами Sphinx (щоб він, наприклад, робив запит до MySQL)?