Иногда 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.