Моя библиотека - социальная сеть любителей книг

Интро

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

И вот при разработке очередного проекта стала проблема поиска. Таблицы в базе не маленькие (от 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.conf



source product
{
        type = mysql
        sql_host = localhost
        sql_user = test
        sql_pass = ******
        sql_db = product
        sql_port = 3306

        sql_query_pre = SET NAMES cp1251
        sql_query_pre = SET CHARACTER SET cp1251

        sql_query = SELECT p.product_id, p.producе_title, p.product_description, t.tag_name FROM `product` p LEFT JOIN `product_tag` pt ON pt.product_id = p.product_id LEFT JOIN `tag` t ON pt.tag_id = t.tag_id

        sql_query_info = SELECT * FROM `product` WHERE `product_id` = $id
        sql_ranged_throttle = 0
}

index product
{
        source = product
        path = /home/larin/data/product
        docinfo = extern
        mlock = 0
        morphology = stem_enru
        min_word_len = 2
        charset_type = sbcs
        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
}

indexer
{
        mem_limit = 32M
}

searchd
{
        address = 127.0.0.1
        port = 3312
        log = /home/larin/log/searchd.log
        query_log = /home/larin/log/query.log
        read_timeout = 5
        max_children = 30
        pid_file = /home/larin/log/searchd.pid
        max_matches = 1000

}

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

Морфология

Spninx поддерживает индексацию (а следовательно и поиск) с учетом морфологии русского и английского языка. Поддержка морфологии реализована при помощи стемминга.

Стемминг – это процесс ведущий к выделению основы слова из сложных словоформ.

Вот строки в конфиге отвечающие за конфигурацию:


#подключение русского и английского стемминга
morphology = stem_enru
#задание таблицы символов, данная настройка для Windows-1251
charset_type = sbcs
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

#важно отметить, что в Sphinx прификсы считаются частью инфикса, т.е. указав индексацию по инфиксам мы автоматически индексируем и по префиксам
min_infix_len = 2

#так должно быть :) Почему? Читайте документацию ;)
enable_star = 1

Индексация

Запускаем создание индекса:
# 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.

Пример скрипта

<?php
require ( "sphinxapi.php" );
//....

$string = @$_GET['string'];
$sphinx = new SphinxClient();
$sphinx->SetServer('localhost', 3312);

//Совпадение любого из слов
$sphinx->SetMatchMode(SPH_MATCH_ANY);

//Результаты отсортированы по релевантности
$sphinx->SetSortMode(SPH_SORT_RELEVANCE);

//Задаем полям веса, для подсчета релевантности
$w = array ('product_title' => 20, 'product_description' => 10, 'tag_name' => 5);
$sphinx->SetFieldWeights($w);

$result = $sphinx->Query($string, '*');
if ($result && is_array($result['matches']))
{
 //Получаем массив ID найденых товаров
 $ids = array_keys($result['matches']);

//А теперь выводим эти товары отсортированные по релевантности
 $id_list = implode(',', $ids);
 $sql = sprintf('SELECT * FROM `product` WHERE `product_id` IN (%s) ORDER BY FIELD(`product_id`, %s)', $id_list, $id_list);

//Все далее выполняем этот запрос и наслаждаемся результатами
}
//....

?>

Итого

Вот и все. И мы в очередной раз (как и в случае с UML и кэшированием) убедились, что все гениальное просто :)



Комментарии (58) на запись «Sphinx - настоящее быстрого поиска»

  1. Артём Курапов MonsterID Icon Артём Курапов | 09.04.2008 в 15:29

    SET NAMES cp1251

    Ай яй яй, 2008й год на дворе, а таблички или страница ещё не в utf8?

  2. Артём Курапов MonsterID Icon Артём Курапов | 09.04.2008 в 15:31

    Артём Курапов | 09.04.2008 в 23:29

    Хм.. видимо у вас сервак ой как далеко от Европы, раз часовой пояс такой

  3. admin MonsterID Icon admin | 09.04.2008 в 15:42

    Артем, а в чем плюс UTF? Чем эта таблица лучше cp1251?

  4. Sam MonsterID Icon Sam | 09.04.2008 в 17:16

    UTF лучше и лучше на эту тему не спорить ;)

    Кстати, с Lucene если Sphinx сравнить, как он? Мы пользуем у себя именно Lucene.

  5. admin MonsterID Icon admin | 09.04.2008 в 17:37

    UTF лучше и лучше на эту тему не спорить ;)

    А чем лучше? Все же хочется конкретики. Я спорить не буду - просто хочу услышать пару доводов )

    Кстати, с Lucene если Sphinx сравнить, как он? Мы пользуем у себя именно Lucene.

    Если сравнивать по скорости, то Sphinx значительно быстрее.
    И кстати, у меня Lucene отказался искать по русским текстам. Поискал на форумах - все говорят проблемы у него с русским, а у вас нормально?

  6. admin MonsterID Icon admin | 09.04.2008 в 17:48

    Вот, например, цитата с сайта Артёма Курапова:

    Конкурентов MnoGoSearch (140 МБ/сек) и Lucene, Sphinx обходит с 2200 МБ/сек.

  7. Sam MonsterID Icon Sam | 09.04.2008 в 18:10

    Ага. Занятно. Надо будет его попробовать.

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

    Так. Про плюсы UTF:

    1. Многоязычность.
    2. Спецсимволы вроде стрелочек.
    3. Нет проблем с XMLHttpRequest.

    Есть и ещё, но сходу так и не вспомню: последний раз с 1251 работал очень давно.

  8. admin MonsterID Icon admin | 09.04.2008 в 18:18

    1. Многоязычность.
    2. Спецсимволы вроде стрелочек.
    3. Нет проблем с XMLHttpRequest.

    Занятные плюсы, правда мне многоязычность не нужна :) Со спец символами тоже проблем нет, а с AJAX ни разу не було проблем - сначала пользовался библиотекой Димы Котерова, теперь jQuery

    А как MySQL работает с UTF8? В поле varchar(255) строку какой длины я смогу записать? 255 символов?

  9. Sam MonsterID Icon Sam | 09.04.2008 в 19:28

    Да.

  10. Артём Курапов MonsterID Icon Артём Курапов | 10.04.2008 в 11:43

    UTF это многоязычие. Сначала все проекты так говорят что всё только на одном языке.. Разрастаются, все каталоги, статьи, контакты в такой кодировке, а потом возникают всякие мелочи с другими языками - имена пользователей что-бы поддерживали всякие ÕÜÖÄ тоже.. Потом что-бы почта когда рассылается, не корёжилась от этих символов, потом что-бы где-нибудь в админке в таблице была “правильная” сортировка.. Вот и получается что программеры делают мелкие хаки, ставят ХТМЛ-коды для этих спец символов, потом сортируют данные вручную в обход БД. А когда таблиц уже эдак 100 и общий размер перевалил 10 гигов, то никому этим заниматься уж никак не хочется.

  11. kmmbvnr MonsterID Icon kmmbvnr | 11.04.2008 в 07:08

    Sphinx не умеет дополнять индекс, требуется полная перестройка.

    А xapian не пробовали? В нем тоде русский

  12. admin MonsterID Icon admin | 11.04.2008 в 10:22

    Sphinx не умеет дополнять индекс, требуется полная перестройка.

    Кто вам такое сказал? Прочтите внимательнее документацию - он все умеет =)

  13. proft MonsterID Icon proft | 12.04.2008 в 16:15

    Спасибо за пост - познавательно!

    ЗЫ: В последнем коменте очепятка ПроЧтите :)

  14. admin MonsterID Icon admin | 12.04.2008 в 20:45

    @proft
    Спасибо!

    @kmmbvnr
    И еще пара вопросов:
    1) Как у xapian с русской морфологией?
    2) И какой стемминг он использует для русского и английского языка?
    3) Можно ли использовать сразу два стеммера?

    Все эти вопросы возникли из-за плохой документации на сайте xapian.

  15. cyberfox MonsterID Icon cyberfox | 16.04.2008 в 20:41

    Спасибо за пост, я как раз планировал использовать этот SE для одного проекта, а тут ты еще и мануал своевременно запостил :)

  16. naigovan MonsterID Icon naigovan | 21.05.2008 в 17:39

    *ворчит* А я сразу говорил UTF8 упрощщает жизнь!

  17. admin MonsterID Icon admin | 21.05.2008 в 18:18

    *ворчит* А я сразу говорил UTF8 упрощщает жизнь!

    Да, глупости все это, на большинстве проектов UTF на фик не нужен

  18. nmike MonsterID Icon nmike | 24.06.2008 в 14:38
    Sphinx не умеет дополнять индекс, требуется полная перестройка.

    Кто вам такое сказал? Прочтите внимательнее документацию - он все умеет =)

    таки к сожалению нет. в документации следующее

    3.4. Indexes

    It is planned to implement more index types, including the type which would be updateable in real time.

    там есть так называемый дельта индекс, но мне вот непонятно, а что если поиск не тупой влоб, а параметризованный, а параметры изменились именно в основной части таблицы - тогда увы и ах… пересобирать его заново.

    хотя, если немного пошаманить, можно найти выход и из этой ситуации. задержки можно минимизировать.

  19. Pavel MonsterID Icon Pavel | 27.06.2008 в 17:32

    Спасибо за статью. Позновательно.

    а UTF8 - однозначно! 2008 год! :)

  20. ring0 MonsterID Icon ring0 | 21.08.2008 в 12:56

    Для опций нужен двойной дефис:
    indexer ––config /home/larin/sphinx.conf ––all

  21. ring0 MonsterID Icon ring0 | 21.08.2008 в 12:57

    а понял, это так Wordpress режет

  22. admin MonsterID Icon admin | 21.08.2008 в 13:32

    ring0, я поправил в твоем комменте дефисы, теперь все ОК. “ndash” нам поможет =)

  23. Pol_uha MonsterID Icon Pol_uha | 29.08.2008 в 13:12

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

    Уменя 37 строка это: if ($result && is_array($result['matches']))

    т.е. получается массив не существует так как ничего не находит(((
    кто нить сталкивался с трудностями поиска на русском?

  24. Pol_uha MonsterID Icon Pol_uha | 29.08.2008 в 13:45

    вот еще заметил
    при индексации при тех настройках что описаны вые выводится: 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

    Что может быть???

  25. admin MonsterID Icon admin | 31.08.2008 в 14:33

    Какая версия MySQL?

  26. Pol_uha MonsterID Icon Pol_uha | 02.09.2008 в 16:56

    С версией разобрался поставил 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
    }

    index
    {
    mem_limit = 32M
    }

    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

    }

  27. Pol_uha MonsterID Icon Pol_uha | 16.09.2008 в 19:07

    //А теперь выводим эти товары отсортированные по релевантности
    $id_list = implode(',', $ids);
    $sql = sprintf('SELECT * FROM `product` WHERE `product_id` IN (%s) ORDER BY FIELD(`product_id`, %s)', $id_list, $id_list);

    //Все далее выполняем этот запрос и наслаждаемся результатами

    а этот запрос выполняем каким методом сфинкса???

  28. Larin MonsterID Icon Larin | 16.09.2008 в 21:45

    Этот запрос выполняется вашим классом работы с БД, ну или попросту функцией mysql_query() )))

  29. Pol_uha MonsterID Icon Pol_uha | 17.09.2008 в 14:28

    ))) спасибо, вот только тогда не очень понятно а в чем прирост скорости? Если получается выполняется аж 2 запроса. Первый сам сфинкс в своих индексах, а потом еще и мы в БД?
    Простите за глупые вопросы.)))

  30. Larin MonsterID Icon Larin | 17.09.2008 в 15:00

    ))) спасибо, вот только тогда не очень понятно а в чем прирост скорости? Если получается выполняется аж 2 запроса. Первый сам сфинкс в своих индексах, а потом еще и мы в БД?
    Простите за глупые вопросы.)))

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

    При индексации? Так этот процесс запускается много реже, по сравнению с обращениями к поиску. У меня на некоторых сайтах переиндексация запускается всего раз в сутки )

    Pol_uha, что еще не понятно?

  31. Pol_uha MonsterID Icon Pol_uha | 17.09.2008 в 16:35

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

  32. Larin MonsterID Icon Larin | 17.09.2008 в 16:57

    В Sphinx храниться индекс по данным, а не сами данные. Понимаете разницу?

    Рад, что блог нравится и помогает )

  33. Pol_uha MonsterID Icon Pol_uha | 17.09.2008 в 17:31

    Понимаю)))
    А можете помоч в такой задачке:
    Есть таблица с названиями животных и с id городов в которых они находятся)))
    Проиндексированы соответственно 2 таблица: эта и с городами.
    Человек ввел черепаха москва или черепахи в москве и т.д.
    как мне сделать взаимодействие этих 2-х таблиц? Т.е. Находятся все черепахи и проверяются id городов в которых они живут и сравниваются с id из таблицы городов откуда берутся названия и сравниваются со 2-м словом, в данном случае “Москва”?
    ПЛЗ!!!))))

  34. Larin MonsterID Icon Larin | 18.09.2008 в 14:18

    И так, есть 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 сделан для случая когда не у всех зверушек задан город обитания.

  35. Pol_uha MonsterID Icon Pol_uha | 24.09.2008 в 15:59

    Привет))) Ссори еще раз. А не подскажешь, как в сфинксе сделать абсолютный поиск. Т.е. при вводе “черный гиппопотам”
    он не выдавал все записи где написано “черный” и “гиппопотам” отдельно, а только где есть фраза целиком?

  36. Larin MonsterID Icon Larin | 24.09.2008 в 17:24

    Привет!
    $sphinx->SetMatchMode(SPH_MATCH_ALL);

    З.Ы. А вообще бывает полезно читать официальную документацию )))

  37. Евгений MonsterID Icon Евгений | 25.09.2008 в 13:25

    Проблема аналогичная - не ищет по русски. Скрипт из примера ругается Notice: Undefined index: matches in /usr/local/www/tomsk.fm/data/sphinx.php on line 45

    Данные в базе и страничка с тестовым скриптом в utf-8

  38. Pol_uha MonsterID Icon Pol_uha | 25.09.2008 в 16:31

    Евгений, а у тебя чего то типа такого при индексации не возникает
    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

    вообще проверь что мускуль от 4.1

  39. Руслан Дворквой MonsterID Icon Руслан Дворквой | 30.12.2008 в 01:37

    Инкрементальный поиск поддерживается или нет? можно подробно об этом написать?

    Ею можно управлять? например добавилось 15 страниц, как их добавить в индекс?

    Какая нагрузка на сервер, при создании индекса например в электронном магазине, товаров 20 тыс. по времени? ориентировочно.

  40. hekcfy MonsterID Icon hekcfy | 25.02.2009 в 23:07

    Еще бы в пример добавить метод BuildExcerpts для формирования сниппетов и новичкам будет ваще шоколадно.

  41. Меня зовут — Анатолий Ларин » Архив блога » Sphinx и Django - замечательное рядом | 27.03.2009 в 17:38

    [...] уже писал поисковом движке Sphinx:Sphinx - настоящее быстрого поиска и использовании его в [...]

  42. adw0rd MonsterID Icon adw0rd | 15.04.2009 в 15:55

    Абсолютный Минимум, который Каждый Разработчик Программного Обеспечения Обязательно Должен Знать о Unicode и Наборах Символов

  43. Руслан Дворковой MonsterID Icon Руслан Дворковой | 21.06.2009 в 14:02

    Автор, а вы на комментарии вообще отвечаете? или нет?

  44. Larin MonsterID Icon Larin | 22.06.2009 в 13:52

    @Руслан Дворковой
    Руслан, простите, из-за больших потоков спама, иногда не замечаю реальных людей )

    Инкрементальный поиск поддерживается или нет? можно подробно об этом написать?
    Ею можно управлять? например добавилось 15 страниц, как их добавить в индекс?

    Поддерживается. Это подробно описано в документации. Там все просто и разобраться можно даже без знания английского.

    Какая нагрузка на сервер, при создании индекса например в электронном магазине, товаров 20 тыс. по времени? ориентировочно.

    Этого вам не скажет никто. Все зависит от запросов. Сам Sphinx почти не создает нагрузки. Самая большая нагрузка создается при индексации и полностью зависит от сложности и “тяжести” запросов.
    Для облегчения жизни сервера, индексировать можно итерационно, по 50-100 записей за заход. Пример, подраздел Ranged queries

    Все, если остались вопросы - пишите.

  45. Руслан Дворковой MonsterID Icon Руслан Дворковой | 22.06.2009 в 16:15

    Спасибо большое за ответы, реально порадовали они

  46. Elvis MonsterID Icon Elvis | 16.04.2010 в 19:03

    Всем привет, спасибо за статью все классно , у меня проблема такая … если ищу так “nord холодильники” отлично работает, ищем так “nord холодильник” тоже все гуд,
    ищем так “холодильники nord” все хорошо , а вот если так “холодильник nord” то все не чего не находит :( может кто знает что с этим делать ? заранее спасиб :)

  47. Elvis MonsterID Icon Elvis | 16.04.2010 в 19:15

    Ребята извините разобрался спасибо ! просто звёзды убрал из $cl->Query(”*$search*”, “*”); от так $cl->Query(”$search”, “*”); все заработало ! :) спасибо

  48. Larin MonsterID Icon Larin | 18.04.2010 в 21:06

    Elvis,
    и Вам спасибо! Сами спросили, сами - ответили! =)

  49. А.Н. MonsterID Icon А.Н. | 25.06.2010 в 08:00

    А, если нужно искать независимо по каждой сущности или вместе, как тогда делать?
    Например, есть три таблицы: клиенты, машины, договора.
    Искать нужно договора, например, по серии, ФИО клиента (из другой таблицы) и номеру ТС (из третьей таблицы).
    Нужно искать клиентов. И нужно искать ТС (например по номеру и ФИО владельца из другой таблицы).

    Как это сделать? Нужно использовать три индекса? Или один индекс стремя источниками?

  50. А.Н. MonsterID Icon А.Н. | 25.06.2010 в 08:02

    Да, плюс к тому, если используется SphinxQL.

  51. Larin MonsterID Icon Larin | 25.06.2010 в 08:10

    @А.Н.
    Три различных индекса, по отдельности - будете искать по какому-либо одному индексу, и вместе - по всем )

  52. А.Н. MonsterID Icon А.Н. | 25.06.2010 в 18:08

    Так получается, что нужен ещё один индекс по ТС и клиентам, вместе взятым?

    Сколько же тогда времени займёт их создание? :-(

  53. Larin MonsterID Icon Larin | 25.06.2010 в 18:13

    Знаете, очень тяжело разговаривать не видя проблемы перед глазами ) Скорее всего надо сесть и вечерок подумать… посмотреть на проблему с иной стороны.

    А если же создание индексов занимает существенное время то стоит строить delta-индексы. А полный индекс перестраивать, например, раз в день или неделю.

  54. А.Н. MonsterID Icon А.Н. | 26.06.2010 в 09:29

    Проблема в том, что у меня уже нет недели. :-)
    Я сейчас столько думать не могу. Мне уже надо было сделать (месяц как).

    Сейчас делаю поиск.

    Вся проблема в том, что обычный MySQL поиск очень медленно работает.

  55. А.Н. MonsterID Icon А.Н. | 26.06.2010 в 09:29

    Запрос получается вида следующего:

    “select dogovor.ID_DOGOVOR as CNT_ID_DOGOVOR, dogovor.DOG_SER as CNT_DOG_SER, dogovor.DOGNUMB as CNT_DOGNUMB, dogovor.DATE_DOG_CREATE as CNT_DATE_DOG_CREATE, dogovor.DATE_DOG_INPUT as CNT_DATE_DOG_INPUT, dogovor.DATE_START as CNT_DATE_START, dogovor.DATE_END as CNT_DATE_END, dogovor.START_USE as CNT_START_USE, dogovor.END_USE as CNT_END_USE, dogovor.START_USE1 as CNT_START_USE1, dogovor.END_USE1 as CNT_END_USE1, dogovor.START_USE2 as CNT_START_USE2, dogovor.END_USE2 as CNT_END_USE2, dogovor.ZNAK_SER as CNT_ZNAK_SER, dogovor.ZNAK_NO as CNT_ZNAK_NO, dogovor.COMMENT as CNT_COMMENT,
    dogovor.ID_CLIENT as CNT_ID_CLIENT, dogovor.ID_DOGOVOR_TYPE as CNT_ID_DOGOVOR_TYPE, dogovor.ID_INSURANCE_COMPANY as CNT_ID_INSURANCE_COMPANY, dogovor.TRANSIT as CNT_TRANSIT, dogovor.ID_CAR as CNT_ID_CAR, dogovor.ID_TERRITORY_USE as CNT_ID_TERRITORY_USE, dogovor.UNLIMITED_DRIVERS as CNT_UNLIMITED_DRIVERS, dogovor.INS_SUM as CNT_INS_SUM, dogovor.INS_PREM as CNT_INS_PREM, dogovor.KOEF_TER as CNT_KOEF_TER, dogovor.KOEF_BONUSMALUS as CNT_KOEF_BONUSMALUS, dogovor.KOEF_STAG as CNT_KOEF_STAG, dogovor.KOEF_UNLIMITED as CNT_KOEF_UNLIMITED, dogovor.KOEF_POWER as CNT_KOEF_POWER, dogovor.KOEF_PERIOD_USE as CNT_KOEF_PERIOD_USE, dogovor.KOEF_SROK_INS as CNT_KOEF_SROK_INS, dogovor.KOEF_KN as CNT_KOEF_KN, dogovor.BASE_SUM as CNT_BASE_SUM,
    dogovor.ID_INSURANCE_CLASS as CNT_ID_INSURANCE_CLASS, dogovor.DATE_WRITE as CNT_DATE_WRITE, dogovor.DATE_BEGIN as CNT_DATE_BEGIN, dogovor.ID_PREV_DOG as CNT_ID_PREV_DOG, dogovor.DATE_INSERT as CNT_DATE_INSERT, dogovor.DATE_UPDATE as CNT_DATE_UPDATE, dogovor.USER_INSERT_NAME as CNT_USER_INSERT_NAME, dogovor.USER_UPDATE_NAME as CNT_USER_UPDATE_NAME ,
    car.ARENDA as CAR_ARENDA, car.ID_CAR as CAR_ID_CAR, car.ID_PURPOSE_TYPE as CAR_ID_PURPOSE_TYPE, car.ID_CAR_TYPE as CAR_ID_CAR_TYPE, car.ID_PRODUCTER_TYPE as CAR_ID_PRODUCTER_TYPE, car.ID_CLIENT as CAR_ID_CLIENT, car.PTS_DATE as CAR_PTS_DATE, car.DATE_INSERT as CAR_DATE_INSERT, car.CAR_MARK as CAR_CAR_MARK, car.DATE_UPDATE as CAR_DATE_UPDATE, car.CAR_MODEL as CAR_CAR_MODEL, car.VIN_NUM as CAR_VIN_NUM,
    car.YEAR_ISSUE as CAR_YEAR_ISSUE, car.POWER_KVT as CAR_POWER_KVT, car.POWER_LS as CAR_POWER_LS, car.MAX_KG as CAR_MAX_KG, car.NUM_PLACES as CAR_NUM_PLACES, car.SHASSI as CAR_SHASSI, car.KUSOV as CAR_KUSOV, car.GOS_NUM as CAR_GOS_NUM, car.PTS_SER as CAR_PTS_SER, car.PTS_NO as CAR_PTS_NO, car.FOREING as CAR_FOREING, car.COMMENTS as CAR_COMMENTS, car.USER_INSERT_NAME as CAR_USER_INSERT_NAME, car.USER_UPDATE_NAME as CAR_USER_UPDATE_NAME ,
    client.ID_CLIENT as CLN_ID_CLIENT, client.SURNAME as CLN_SURNAME, client.NAME as CLN_NAME, client.MIDDLENAME as CLN_MIDDLENAME, client.INN as CLN_INN, client.DOC_SER as CLN_DOC_SER,
    client.POSTINDEX as CLN_POSTINDEX, client.BIRTHDAY as CLN_BIRTHDAY, client.ID_CITY as CLN_ID_CITY, client.ID_REGION as CLN_ID_REGION, client.ID_COUNTRY as CLN_ID_COUNTRY, client.TOWN as CLN_TOWN, client.STREET as CLN_STREET, client.HOME as CLN_HOME, client.KORPUS as CLN_KORPUS, client.FLAT as CLN_FLAT, client.HOME_PHONE as CLN_HOME_PHONE, client.DOC_NUM as CLN_DOC_NUM, client.ID_SEX as CLN_ID_SEX, client.ID_FAMILY_STATE as CLN_ID_FAMILY_STATE, client.ID_CLIENT_TYPE as CLN_ID_CLIENT_TYPE, client.ID_TYPE_DOC as CLN_ID_TYPE_DOC, client.GROSS_VIOLATIONS as CLN_GROSS_VIOLATIONS, client.ID_INSURANCE_CLASS as CLN_ID_INSURANCE_CLASS, client.LICENCE_SER as CLN_LICENCE_SER, client.LICENCE_NO as CLN_LICENCE_NO, client.START_DRIVING_DATE as CLN_START_DRIVING_DATE,
    client.DATE_INSERT as CLN_DATE_INSERT, client.DATE_UPDATE as CLN_DATE_UPDATE, client.IMPORT_DATE as CLN_IMPORT_DATE, client.WRITER_NAME as CLN_WRITER_NAME, client.ID_TYPE_LOSSED as CLN_ID_TYPE_LOSSED, client.CELL_PHONE as CLN_CELL_PHONE, client.BUSINESS_PHONE as CLN_BUSINESS_PHONE, client.LAST_CALL_DATE as CLN_LAST_CALL_DATE, client.REGION_CODE as CLN_REGION_CODE, client.COMMENTS as CLN_COMMENTS, client.USER_INSERT_NAME as CLN_USER_INSERT_NAME, client.USER_UPDATE_NAME as CLN_USER_UPDATE_NAME , CONCAT_WS(” “, DOG_SER, DOGNUMB) as FULL_NUM, CONCAT_WS(” “, carmark.MARK, car.CAR_MODEL) as FULL_MODEL, CONCAT_WS(” “, client.surname, client.name, client.middlename) as FULL_NAME , CONCAT_WS(”, “, if(client.town = ”, null, client.town), if(client.street = ”, null, client.street), if(client.home = ”, null, client.home), if(client.korpus = ”, null, client.korpus), if(client.flat = ”, null, client.flat)) as FULL_ADDRESS
    from client_types, carmark, car_type, dogovor_type, dogovor
    join client on dogovor.ID_CLIENT = client.ID_CLIENT
    join car on dogovor.ID_CAR = car.ID_CAR
    where (dogovor.ID_DOGOVOR_TYPE = dogovor_type.ID_DOGOVOR_TYPE) and
    (DOGNUMB like ‘%123%’ )
    or
    (
    ((SURNAME like ‘%Иванов%’ ) and (client_types.ID_CLIENT_TYPE_GROUP = 1 and client_types.ID_CLIENT_TYPE = client.ID_CLIENT_TYPE))
    )
    or
    (
    (car.ID_CAR_TYPE = car_type.ID_CAR_TYPE and car.CAR_MARK = carmark.ID_CARMARK) and
    (VIN_NUM like ‘%123%’ )
    )

  56. А.Н. MonsterID Icon А.Н. | 26.06.2010 в 09:39

    Т.е., используется 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)?

  57. А.Н. MonsterID Icon А.Н. | 26.06.2010 в 11:21

    Да, ещё с запросом я стормозил. Но даже с IN, всё-равно, очень медленно. :-(

  58. Александр MonsterID Icon Александр | 07.08.2010 в 13:04

    Анатолий, спасибо, как раз назрела надобность использовать поисковый движок такого рода в проекте, Ваша статья оказалась очень кстати.

Оставить комментарий


Copyright, 1983 – 2010