Статьи

Разработка собственного фильтра Solr

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

Предположения

Предположим, нам нужен фильтр, который позволил бы нам перевернуть каждое слово, которое мы имеем в данном поле. Таким образом, если вход «solr.pl», выход будет «lp.rlos». Это не самый сложный пример, но для этой записи этого будет достаточно. Еще одна вещь — я решил опустить описание того, как настроить вашу IDE, как скомпилировать код, собрать jar и все в таком духе. Мы сосредоточимся только на коде.

Дополнительная информация

Код, представленный в этом посте, был создан с использованием библиотек Solr 3.6 , хотя у вас не должно быть особых проблем с его компиляцией с помощью бинарных файлов Solr 4. Имейте в виду, что могут потребоваться небольшие изменения (в случае, если что-то изменится до выпуска Solr 4.0).

То, что нам нужно

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

Фильтр

Для реализации нашего фильтра мы расширим класс TokenFilter из org.apache.lucene.analysis и переопределим метод incrementToken . Этот метод возвращает логическое значение — если значение все еще доступно для обработки в потоке токенов, этот метод должен возвращать значение true , если токен в потоке токенов не следует подвергать дальнейшему анализу, этот метод должен возвращать значение false . Реализация должна выглядеть следующим образом:

package pl.solr.analysis;

import java.io.IOException;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;

public final class ReverseFilter extends TokenFilter {
  private CharTermAttribute charTermAttr;

  protected ReverseFilter(TokenStream ts) {
    super(ts);
    this.charTermAttr = addAttribute(CharTermAttribute.class);
  }

  @Override
  public boolean incrementToken() throws IOException {
    if (!input.incrementToken()) {
      return false;
    }

    int length = charTermAttr.length();
    char[] buffer = charTermAttr.buffer();
    char[] newBuffer = new char[length];
    for (int i = 0; i < length; i++) {
      newBuffer[i] = buffer[length - 1 - i];
    }
    charTermAttr.setEmpty();
    charTermAttr.copyBuffer(newBuffer, 0, newBuffer.length);
    return true;
  }
}

Описание вышеуказанной реализации

Несколько слов о некоторых строках кода в приведенной выше реализации:

  • Строка 9 — класс, который расширяет класс TokenFilter и будет использоваться в качестве фильтра, должен быть помечен как окончательный (требование Lucene).
  • Строка 10 — атрибут потока токенов, который позволяет нам получать и изменять текстовое содержимое термина. Если бы мы хотели, наш фильтр мог бы использовать более одного атрибута потока, например, один подобный атрибут для получения и изменения позиции в потоке токена или полезной нагрузки. Список реализации интерфейса атрибутов можно найти в Lucene API (т.е. http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/util/Attribute.html ).
  • Строки 12 — 15 — конструктор, который принимает поток токенов в качестве аргумента и затем добавляет ( строка 14 ) соответствующий атрибут потока токенов.
  • Строки 18 — 30реализация метода incrementToken .
  • Строки 19 — 21 — проверить, доступен ли токен для обработки. Если нет, верните false .
  • Строка 23 — получение размера буфера, содержимое которого мы хотим изменить.
  • Строка 24 — получение буфера, в котором у нас есть слово, которое мы хотим изменить. Термин «текст» хранится в виде массива « char» и, таким образом, лучшим будет использовать его, а не создавать объект String .
  • Строки 25 — 28 — создать новый буфер и обратить реальный.
  • Строка 29 — очистить исходный буфер (необходим в случае использования методов добавления ).
  • Строка 30 — скопировать сделанные нами изменения в буфер атрибута потока токенов.
  • Строка 31 — вернуть true , чтобы сообщить, что токен доступен для дальнейшей обработки.

Фабрика фильтров

Как я писал ранее, чтобы Solr мог использовать наш фильтр, нам нужно реализовать класс фабрики фильтров. Потому что у нас нет никаких специальных значений конфигурации, и поэтому заводская реализация должна быть очень простой. Мы расширим класс BaseTokenFilterFactory из пакета org.apache.solr.analysis . Реализация может выглядеть следующим образом:

package pl.solr.analysis;

import org.apache.lucene.analysis.TokenStream;
import org.apache.solr.analysis.BaseTokenFilterFactory;

public class ReverseFilterFactory extends BaseTokenFilterFactory {
  @Override
  public TokenStream create(TokenStream ts) {
    return new ReverseFilter(ts);
  }
}

Как видите, реализация фабрики фильтров проста — нам нужно было переопределить только один метод create, в котором мы создаем наш фильтр и возвращаем его.

конфигурация

После компиляции и подготовки файла jar мы копируем jar в каталог, который Solr сможет увидеть. Мы можем сделать это, создав каталог lib в домашнем каталоге Solr, а затем добавив следующую запись в файл solrconfig.xml :

<lib dir="../lib/" regex="*.jar" />

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

<fieldType name="text_reversed" class="solr.TextField">
  <analyzer>
    <tokenizer class="solr.WhitespaceTokenizerFactory"/>
    <filter class="pl.solr.analysis.ReverseFilterFactory" />
  </analyzer>
</fieldType>

Стоит отметить, что в качестве значения атрибута class тега filter мы указываем полное имя пакета и класса фабрики, которую мы создали, а не сам фильтр. Важно помнить об этом, иначе Solr будет выдавать ошибки.

Это работает ?

Чтобы показать вам, как это работает, я привел следующий снимок экрана панели администрирования Solr:

Подводить итоги

Как вы можете видеть из приведенного выше примера, создание собственного фильтра не является сложной задачей. Конечно, идея фильтра была очень простой, и поэтому его реализация была простой. Я надеюсь, что этот пост будет полезен, когда придет время, когда вам нужно создать свой собственный фильтр для Solr.