пятница, 19 апреля 2013 г.

Lucene в .net и словари hunspell

Недавно на работе мне задали такую задачку: замутить в  lucene поиск не только по точному совпадению ключевого слова, а также по его свловоформам.
А, надо сказать, проект у нас написан на .net и порт lucene там довольно старый (lucene 3.0.3). А что это значило для меня? А то, что штатной поддержки морфологии нужного мне языка не было. Я пошерстил интернет в поисках отвеа на вопрос: что же lucene может предложить в вопросах морфологии и стемминга той кучи языков, которую она не поодерживает из коробки?



В итоге выбор пал на связку lucene + hunspell, благо в версии lucene.net 3.0.3 hunspell входит в состав lucene contribs. Методика использования связки примерно такая:


  1. Нужно скачать словарь нужного языка для openoffice/libreoffice. Взять его можно здесь
  2. Далее, из скачанного архива вытаскиваем два файла .aff и .dic и выкладываем их на сервер, в то место, где lucene сможет свободно изх прочитать.
  3. Я преобразовал файлы из iso-какойто-там кодировки в UTF-8. У нас в проекте все данные гоняются в UTF-8, ну и солвари тоже пускай в нем хранятся. Помимо перекодирования нужно отредактировать .aff файл и заменить указзаную в нем кодировку на UTF-8.
  4. Пишем простенький класс-анализатор:
public class LatvianHunspellAnalyzer: Analyzer
    {
        private static readonly HunspellDictionary Dictionary = GetDictionary(@"lv_LV");
        private static HunspellDictionary GetDictionary(String culture)
        {
            using (var affixStream = OpenStream(culture + @".aff"))
            using (var wordStream = OpenStream(culture + @".dic"))
            {
                if (affixStream != null && wordStream != null)
                {
                    return new HunspellDictionary(affixStream, wordStream);
                }
            }
            throw new InvalidOperationException("Missing affix- or word stream.");
        }
        protected static Stream OpenStream(String fileName)
        {
            return new FileStream(@"C:\inetpub\lucene\" + fileName, FileMode.Open);
        }
        public override TokenStream TokenStream(String fieldName, TextReader reader)
        {
            var stream = new StandardTokenizer(Version.LUCENE_29, reader);
            TokenFilter filter = new LowerCaseFilter(stream);
            filter = new HunspellStemFilter(filter, Dictionary);
            return filter;
        }
    }

  1. Те части проекта, которые отвечают за сохранение данных в lucene индекс и чтение оттуда должны использовать этот анализатор (в моем случае LatvianHunspellAnalyzer, вы его можете назвать как угодно).
Вуаля, все ищется так как было задумано. Если чего-то не получится - спрашивайте. 

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

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