О локальном поиске замолвите слово

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

    Но у всех этих решений не было того, что мне нужно:

    • Централизованная установка
    • Поисковая выдача с учётом прав доступа
    • Поиск по содержимому документа
    • Морфология

    И я решил сделать своё.

    Раскрою по пунктам, что я имею в виде для избегания разности толкований и недопониманий.
    Централизованная установка – клиент-серверное исполнение. У всех перечисленных выше решений одна фундаментальная проблема – каждый пользователь делает свой локальный поисковый индекс, что в случае больших объёмов хранилищ затягивает индексирование, растёт профиль пользователя на машине и вообще неудобно в случае прихода нового сотрудника или переезда на новую машину.

    Поисковая выдача в учётом прав – тут всё просто – выдача должна соответствовать правам сотрудника на файловый ресурс. А то получится, что даже если у сотрудника нет прав на ресурс, но он может всё почитать из поискового кеша. Неудобненько получится, согласитесь? Поиск по содержимому документа – поиск по тексту документа, тут всё очевидно, как мне кажется и разночтений быть не может.

    Морфология – ещё проще. Указали в запросе «нож» и получили, как «нож», так и «ножи», «ножевой» и «ножик». Желательно, чтобы это работало для русского и английского языков.
    С постановкой задачи определились, можно переходить к реализации.

    В качестве именно поисковой машины я выбрал систему Sphinx, а язык разработки интерфейса C# и .net и в итоге проект получил название Vidocq (Видок) по имени французского сыщика. Ну типа найдёт всё и вот это вот всё.

    Архитектурно приложение выглядит следующим образом:

    Поисковый робот рекурсивно обходит файловый ресурс и обрабатывает файлы по заданному списку расширений. Обработка заключается в получении содержимого файла, сжатию текста – из текста убираются кавычки, запятые, лишние пробелы и прочее, дальше содержимое помещается в базу (MS SQL), делается отметка о дате помещения и робот идёт дальше.

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

    На C# была разработана форма, которая общалась со Сфинксом через MySQL-коннектор. Сфинкс отдаёт массив файлов в соответствии с запросом, дальше массив фильтруется на право доступа того пользователя, который осуществляет поиск, выдача форматируется и показывается пользователю.

    О файле нам понадобится хранить следующую информацию:

    • Id файла
    • Имя файла
    • Путь к файлу
    • Содержимое файла
    • Расширение
    • Дата добавления в базу

    Это всё делается одной таблицей и в неё всё будет складывать поисковый робот. Дата добавления необходима для того, чтобы когда робот в следующий обход сравнивал дату изменения файла с датой помещения в базу и если даты различаются, то обновить информацию о файле.

    Дальше настраивать саму поисковую машину. Я не буду описывать весь конфиг, он будет доступен в архиве проекта, но освещу лишь основные моменты.

    Основной запрос, формирующий базу

    source documents: documents_base

    {
    	sql_query = \
    	select \
    	DocumentId as 'Id', \
    	DocumentPath as 'Path', \
    	DocumentTitle as 'Title', \
    	DocumentExtention as 'Extension', \
    	DocumentContent as 'Content' \
    	from \
    	VidocqDocs
    }

    Настройка морфологии через лемматайзер.

    index documents
    {
    	source 			= documents
    	path 			= D:/work/VidocqSearcher/Sphinx/data/index
        morphology 		= lemmatize_ru_all, lemmatize_en_all
    }

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

    d:\work\VidocqSearcher\Sphinx\bin\indexer.exe documents --config D:\work\VidocqSearcher\Sphinx\bin\main.conf –rotate

    Тут путь к индексатору дальше имя индекса в который поместить обработанное, путь к конфигу и Флаг –rotate означает, что индексация будет выполняться наживую, т.е. при работающей службе поиска. После завершения индексации индекс будет заменен на обновлённый.

    Проверяем работу в консоли. В качестве интерфейса можно использовать клиента MySQL, взятого, например, из комплекта веб-сервера.

    mysql -h 127.0.0.1 -P 9306

    после этого запрос select id from documets; должен вернуть список проиндексированных документов, если, конечно, вы перед этим запустили саму службу Sphinx и всё сделали правильно.
    Хорошо, консоль это прекрасно, но мы же не будем заставлять пользователей вбивать руками команды, верно?

    Я набросал вот такую вот форму



    И вот с результатами поиска



    При щелчке по конкретному результату открывается документ.

    Как реализовано.

    using MySql.Data.MySqlClient;
    string connectionString = "Server=127.0.0.1;Port=9306";
                var query = "select id, title, extension, path, snippet(content, '" + textBoxSearch.Text.Trim() + "', 'query_mode=1') as snippet from documents " +
                    "where ";
                if (checkBoxTitle.IsChecked == true && checkBoxContent.IsChecked == true)
                {
                    query += "match ('@(title,content)" + textBoxSearch.Text.Trim() + "')";
                }
                
                if (checkBoxTitle.IsChecked == false && checkBoxContent.IsChecked == true)
                {
                    query += "match ('@content" + textBoxSearch.Text.Trim() + "')";
                }
                
                if (checkBoxTitle.IsChecked == true && checkBoxContent.IsChecked == false)
                {
                    query += "match ('@title" + textBoxSearch.Text.Trim() + "')";
                }
                
                
    
                if (checkBoxWord.IsChecked == true && checkBoxText.IsChecked == true)
                {
                    query += "and extension in ('.docx', '.doc', '.txt');";
                }
                if (checkBoxWord.IsChecked == true && checkBoxText.IsChecked == false)
                {
                    query += "and extension in ('.docx', '.doc');";
                }
    
                if (checkBoxWord.IsChecked == false && checkBoxText.IsChecked == true)
                {
                    query += "and extension in ('.txt');";
                }

    Да, тут быдлокод, но это MVP.

    Собственно, тут формируется запрос к Сфинксу в зависимости от выставленных чексбоксов. Чекбоксы указывают на тип файлов в которых искать и область поиска.

    Дальше запрос уходит в Сфинкса, а потом разбирается полученный результат.

    using (var command = new MySqlCommand(query, connection))
                {
                    connection.Open();
    
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var id = reader.GetUInt16("id");
                            var title = reader.GetString("title");
                            var path = reader.GetString("path");
                            var extension = reader.GetString("extension");
                            var snippet = reader.GetString("snippet");
                            bool isFileExist = File.Exists(path);
                            if (isFileExist == true)
                            {
                                System.Windows.Controls.RichTextBox textBlock = new RichTextBox();
                                textBlock.IsReadOnly = true;
                                string xName = "id" + id.ToString();
                                textBlock.Name = xName;
                                textBlock.Tag = path;
                                textBlock.GotFocus += new System.Windows.RoutedEventHandler(ShowClickHello);
                                snippet = System.Text.RegularExpressions.Regex.Replace(snippet, "<.*?>", String.Empty);
                                Paragraph paragraph = new Paragraph();
                                paragraph.Inlines.Add(new Bold(new Run(path + "\r\n")));
                                paragraph.Inlines.Add(new Run(snippet));
                                textBlock.Document = new FlowDocument(paragraph);
                                StackPanelResult.Children.Add(textBlock);
                            }
                            else
                            {
                                counteraccess--;
                            }
                        }
                    }
                }

    На этом же этапе формируется выдача. Каждый элемент выдачи – richtextbox с событием открытия документа на клик. Элементы помещаются на StackPanel и перед этим идёт проверка доступности файла пользователю. Таким образом, в выдачу не попадёт файл, недоступный пользователю.
    Преимущества такого решения:

    • Индексация происходит централизованно
    • Чёткая выдача с учётом прав доступа
    • Настраиваемый поиск по типам документов

    Разумеется, для полноценной работы такого решения в компании должно быть соответствующим образом организован файловый архив. В идеале должны быть настроены перемещаемые профиля пользователей и прочее. И да, я знаю про наличие SharePoint, Windows Search и скорее всего ещё нескольких решений. Дальше можно бесконечно долго дискутировать о выборе платформы разработки, поисковой машине Sphinx, Manticore или Elastic и так далее. Но мне было интересно решить задачу тем инструментарием в котором я немного разбираюсь. Сейчас оно работает в режиме MVP, но я развиваю его.

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

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

    Как считаете, стоит ли развивать проект?

    • 49,2%Да, стоит31
    • 27,0%Нет, не надо17
    • 23,8%Воздержусь, пожалуй15
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 19

      +2
      Если вы разбираетесь, видел такой интересный вопрос в одной телеграмм группе на который никто не смог ответить.
      Есть видео (название, описание, тэги) они находятся в эластике.
      И есть супер частая, супер обновляемая статистика которая находится в mongodb.

      И допустим по запросу «коты» с лимитом 10 (для пагинации), эластик возвращает и нет проблем. всего подходящий записей по слову «коты» 100млн.

      Но тут появляется проблема отсортировать эти записи по статистике. Как это сделать?

      1) не будешь же ты делать выбрать 100 млн из эластика, а потом писать запрос для mongo selec from x where ID in(100млн записей) order by field.
      2) не будешь же ты в эластик пихать это поле статистики, т.к. она супер часто обновляется, эластик утонет в абсолютно бесполезной переиндексации документов.
        +8

        Насколько я знаю, в таких случаях статистику в эластике (точнее, в query side, не важно, эластик ли это) начинают обновлять не на каждый чих, а с запаздыванием (eventual consistency). Пользователю почти всегда все равно, смотрит он на текущие данные по лайкам видео с котиками, или пятисекундной давности (особенно учитывая, что за время между поиском и переходом на само видео статистика все равно поменяется).

        0
        Total Commander --> Alt + F7.
        скриншот: klikr.org/674aaa95de4c64b7bae2268d3fc8.png
        Ничего лучше для локального поиска я еще не встречал.
        Похоже все велосипеды уже придуманы до нас. ¯\_(ツ)_/¯
          0
          Велосипеды к сожалению прос... утеряны. Раньше, на старом месте работы, пользовался программой «Персональный поиск 2.6.0» от Яндекса. При базе (свалке) разнообразных документов от сотен людей и количеством под 100000 и более файлов, только им и спасался. практически любой документ можно было найти за несколько минут, а не убивать на это часы. Не будь его, я бы точно на Alt+F7 повесился.
          Персональный поиск 2.6.0
          Разработчик: Яндекс
          Сайт программы: desktop.yandex.ru
          Размер дистрибутива: 4,54 Мбайт
          Работа под управлением: Windows 98/Me/NT/2000/XP/Vista
          Способ распространения: freeware (http://download.yandex.ru/desktop/YandexDesktopSetup.exe)
          Цена: бесплатно
          «Персональный поиск» — простая утилита для быстрого поиска документов на русском языке с учетом морфологии. В ней реализованы возможности проведения поиска в документах основных форматов (DOC, XLS, PPT, SWF, PDF, RTF, HTML TXT, MHT и др.), архивах (CHM, DjVu, ZIP, RAR и TAR), аудиофайлах (MP3 и OGG), а также сообщениях электронной почты (Outlook, Outlook Express, Thunderbird и TheBat!).

          Но к сожаление это сейчас заброшенный проект, хотя установщик и скачивается, в некоторых местах утверждается что ему на замену пришла Алиса, но такого функционала локального индексированного поиска я у неё не видел.
          Вот тут статья Утилиты для локального поиска в КомпьютерПресс 8'2010
            0
            Но к сожаление это сейчас заброшенный проект,

            Сейчас есть например Recoll www.lesbonscomptes.com/recoll (он правда в первую очередь под Linux но Windows-версия тоже есть).
            У macOS есть вполне себе расширяемый Spotlight.
              0
              Спасибо за наводку, посмотрим как он с русской морфологией справляется, плюс как кодировки переваривает. Ну и вообще насколько удобный в работе.
            0
            Автор пишет о централизованном и заранее проиндексированном хранилище документов:
            Централизованная установка – клиент-серверное исполнение. У всех перечисленных выше решений одна фундаментальная проблема – каждый пользователь делает свой локальный поисковый индекс, что в случае больших объёмов хранилищ затягивает индексирование, растёт профиль пользователя на машине и вообще неудобно в случае прихода нового сотрудника или переезда на новую машину.
              0
              Поиск по индексу существенно отличается от прямого поиска по содержимому перебором (чем и занимается Total Commander) как минимум скоростью. Насколько я полагаю, в статье идёт речь не о локальном поиске, а о поиске по корпоративному файловому серверу, поэтому, поиск по индексу, в сравнении с поиском по содержимому, также снизит нагрузку на сеть и на HDD сервера.
              Хотя, вы правы, для домашнего пользования Total Commander не имеет конкурентов.
              +1
              Очень очень сыро. Советую посмотреть в сторону поисковой утилиты Everything. Было бы круто иметь что-то подобное, но при том расширяемое с собственными модулями и, возможно, open source.

              p.s.
              У меня код отображается не как код, а как текст.
                +1

                Everything не индексирует содержимое.

                +1
                Программа «Архивариус 3000» похожее делает
                  0
                  Архиариус я упомянул, спасибо.
                  0
                  Идея интересная. Думаю, издательства, копирайтеры и иже с ними оценили бы такое. Я бы не стал привязываться к расширению файла, а искал бы «побайтно» — выбрать штук 10 разных кодировок и если у файла не указана кодировка — искать надпись в байтном представлении UTF8/16/cp1251 и т.п.
                  Интересно было бы посмотреть бенчмарки по скорости.
                    0

                    Я бы смотрел в сторону определения маймтайпа и/или кодировки на лету и конвертации в плэйнтекст в utf8 для индексации

                      0
                      Бывает так, что весь файл конвертировать в плейн невозможно — бОльшую часть составляют бинарные данные. Поэтому лучше конвертировать искомую строку в байты нужной кодировки, кмк.
                    0

                    Microsoft Search Server же!
                    Вяло обновляется, но до сих пор рабочий, и, если я не ошибаюсь, даже бесплатный.
                    Даже статья тут есть.

                      0
                      Был такой проект — Yandex.Desktop — устанавливлся на комп, долго индексировал файлы, быстро искал в базе, недолго доиндексировал. Понимал doc, xls, rtf, pdf, html, eml, djvu, openoffice.
                      До сих пор пользую.
                      Извиняюсь, если задел за больное.
                      Ну а свой проект — конечно нужен, особенно если будет free, даже если не open.
                        0
                        О каком больном речь, что Вы!
                          0
                          Лучше еще не встречал. Жаль, что закрыли проект. Очень помогает не утонуть в продуктах жизнедеятельности офисных бумагомарателей.

                          Где-то на хабре писали, что хорошего решения для хранения документов и поиска — пока просто нет. Так что подобное начинание нужно и важно.

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

                        Самое читаемое