Поиск на сайте с помощью phpMorphy

Всем привет!

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

Тут между делом за поиск зацепились и Василий сказал, что использует Sphinx. Я со сфинксом столкнулся еще году в 2009-ом и тогда его с наскоку не победил, но для себя уяснил четко: он весьма не прост. Но когда в свое время решил на своем сайте сделать поиск, нужно было что-то искать на замену, ведь важная часть поиска — это именно морфологический поиск.

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

И вот в результате появился компонент modSearch. Исходники его я выложил: github.com/MODX-Club/modsearch

Возможно будет интересно покопать отдельный моменты по реализации. Часть из них опишу.

1. Плагин. Срабатывает на сохранение документа (событие OnDocFormSave).
К сожалению, в самом классе документа нет вызова ивента при сохранении, чтобы плагин срабатывал на любое изменение, пусть даже через API
$doc = $modx->getObject("modResource", $id);
$doc->set("content", $newContent);
$doc->save();
То есть если обновить вот так, то плагин не сработает, он сработает только на сохранение документа через админку.
Для сравнения в классе modUser прописаны вызовы при сохранении: github.com/modxcms/revolution/blob/2.x/core/model/modx/moduser.class.php#L92-L114
То есть на пользователя можно навесить плагин, который всегда сработает где и как бы не был сохранен пользователь (исключая случаи выполнения чистого SQL).
Не буду говорить, что это еще один недостаток MODX.

2. Создание поисковых индексов. github.com/MODX-Club/modsearch/blob/master/core/components/modsearch/model/modsearch/modsearch.class.php#L16
В системной настройке modsearch.index_fields можно перечислить поля, которые будут использоваться для создания поисковых индексов. При создании индексов скрипт берет эту системную настройку и из документа дергает эти поля. По-умолчанию это pagetitle, longtitle, content.
Здесь отдельно отмечу один из минусов моего решения: здесь нет никакого ранжирования. То есть не важно в заголовке слова были найдены или нет, вес их одинаковый. Или было найдено, или нет. С этой точки зрения у Василия конечно же лучше реализовано.


3. Переобход всех документов, чтобы создать для них индексы (выполняется в консоли).
github.com/MODX-Club/modsearch/blob/master/core/components/console/files/global/modxclub.ru/modsearch/resources/update_indexes.php

4. Скрипт для поиска документов через процессор компонента modxSite (так же выполняется в консоли).
github.com/MODX-Club/modsearch/blob/master/core/components/console/files/global/modxclub.ru/modsearch/resources/getdata.php

А вот здесь вот формируется итоговый запрос на выборку документов: github.com/MODX-Club/modxSite/blob/master/core/components/modxsite/processors/site/web/resources/getdata.class.php#L64

5. Установка «на лету» связи modResource->SearchIndexes.
github.com/MODX-Club/modsearch/blob/master/core/components/modsearch/model/modsearch/metadata.mysql.php

Могу точно сказать, что с небольшими модификациями это можно использовать и с минишопом (делал на паре сторонних сайтов) и на каталоге в несколько тысяч товаров вполне приемлемо ищет, в том числе и по скорости.
Fi1osof
01 августа 2019, 12:06
modx.pro
1
2 476
+11

Комментарии: 16

Василий Наумкин
01 августа 2019, 12:24
+5
mSearch2 с самого начала работает на phpMorphy.

Василий сказал, что использует Sphinx
Только здесь, на modx.pro. Думал, что он получше mSearch2 работать будет, но пока что вышло, что это не так.
    Fi1osof
    01 августа 2019, 12:32
    0
    Ясно. Спасибо за уточнение.

    P.S. про здесь сфинкс — я так и думал. Но не знал, что phpMorphy у тебя в mSearch2 используется.
      Евгений Борисов
      11 августа 2019, 20:42
      +1
      А как насчет связки phpMorphy + Sphinx? Так же интересно узнать про опыт использования phpMorphy2 для нормализации контента под индексацию.
        Василий Наумкин
        12 августа 2019, 04:14
        0
        А как насчет связки phpMorphy + Sphinx?
        Не совсем понятно, как и зачем их связывать.

        Sphinx — это отдельно крутящий демон, которому ты отдаёшь поисковый запрос и он делает всю работу. Включая, например, подсветку найденных слов.

        Выглядит это примерно как SQL запрос, только не в БД, а в Sphinx
        $query = (new SphinxQL($this->conn))
            ->select('id', 'comment', 'weight() AS weight')
            ->from('topics')
            ->limit($start, $limit)
            ->option('field_weights', [
                'pagetitle' => 100,
                'content' => 50,
                'comment' => 10,
            ])
            ->groupBy('id')
            ->option('max_matches', 1000000)
            ->match(['pagetitle', 'content', 'comment'], $string);
        
        $result = $query->execute();

        В mSearch2 же это всё делается вручную, используя формы слов от phpMorphy. Наверное можно просклонять слова в phpMorphy, потом поискать их в Sphinx, потом отдельно отранжировать и соединить ответы — но вряд ли это что-то улучшит, а вот усложнит — наверняка.

        Так же интересно узнать про опыт использования phpMorphy2
        Я такого не нашёл. Есть только pymorphy2, но он на Python и его ни разу не использовал.
          Евгений Борисов
          12 августа 2019, 07:48
          0
          Я знаю как работает Sphinx. Вопрос был в стеммерах и использовании в связке. Но я уже понял, что это не пробовалось.

          Я такого не нашёл. Есть только pymorphy2, но он на Python и его ни разу не использовал.
          Его и имел в виду
      Сергей Шлоков
      01 августа 2019, 13:14
      +1
      Возможно я тебя удивлю, но в MODX у таблицы ресурсов есть могучий полнотекстовый индекс, который лопатить 5 полей ресурса при операциях CRUD. Почему никто не хочет его использовать? Даже разработчики. Why? ;)
        Fi1osof
        01 августа 2019, 13:22
        0
        Нет, ты меня совсем не удивишь этим. Но на мой костыль у меня есть несколько причин:
        1. innoDB не умеет в fulltext habr.com/ru/post/114572/ А я часто перевожу базу данных на innoDB, потому что MyIsam не умеет в foreign key, да еще и постоянно разваливается.
        2. Если я расширяю таблицу и добавляю кастомные поля, то и индекс надо расширять.
        3. Я могу хотеть не учитывать некоторые колонки.
        4. Я могу хотеть добавить индексы сразу по нескольким таблица.
          Сергей Шлоков
          01 августа 2019, 13:53
          0
          1. Уже давно умеет ))

          Вот и я приблизительно также аргументировал PR с выпиливанием этого монстра. Но Джейсон сказал, что чем больше индексов тем лучше. Хрен с ним, что некоторые не используются. Когда-нибудь кому-нибудь пригодится. Fuck the fuel economy and sql optimization!
            Fi1osof
            01 августа 2019, 13:57
            0
            1. Уже давно умеет ))
            Но так и я этот компонент писал давно :)
            На тот момент я получал сообщение «Не могу создать такой индекс».
        Василий Столейков
        01 августа 2019, 14:14
        0
        Спасибо за статью!
        Я как раз хотел приступить к реализации морфологического поиска по своей кастомной таблице — тут много полезных моментов я нашел для себя!
          Fi1osof
          01 августа 2019, 14:27
          0
          Пожалуйста!
          Konstantin
          01 августа 2019, 19:15
          0
          на каталоге в несколько тысяч товаров вполне приемлемо ищет
          А несколько, это сколько? У меня msearch стоит на сайте с > 75 тысяч товаров (76 673 на данный момент), и поиск работает вполне приемлемо.
          0.1394129: Total time
          25 661 440: Memory usage
            Fi1osof
            01 августа 2019, 19:33
            0
            Как говорят программисты: работает? не лезь!
            Так что если работает: то замечательно.

            Но встречный вопрос: так работает из коробки или какая кастомизация выполнялась? Если кастомизация, то неплохо бы описать ее в отдельной статье. Но предполагаю, что кастомизации никакой особо не было (судя по объему потребляемой памяти). Значит просто сервер помощнее взят? Какие характеристики железа?

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

            Со своей стороны вот один из моих топиков про оптимизацию: modxclub.ru/blog/bolshoy-magazin-na-modx-revolution/172.html
            Конкретно в этом топике ничего нет касаемого modSearch, но я когда тесты проводил, скорость при поиске не сильно отличалась.
              Konstantin
              01 августа 2019, 19:54
              0
              Из коробки, никакой оптимизации – evrorazbor.by, скорость устраивает, но были вопросы к точности результатов, поэтому для этого же сайта, но только российской версии используется другой evrorazbor.ru – чуть быстрее, но точность результатов гораздо лучше. Можно проверить разницу на запросе «акпп bmw», с ним хорошо видна разница.
              неплохо бы описать ее в отдельной статье
              Вариант для российской версии сайта сделал @Володя, так что это к нему.
              Значит просто сервер помощнее взят? Какие характеристики железа?
              6 x 2,4+ ГГц, 8192 МБ
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          16