Большинство приложений должно иметь какую-то функцию поиска. Проблема состоит в том, что функции поиска часто являются огромными затратами ресурсов, и они могут снизить производительность нашего приложения, вызывая большую нагрузку на базу данных. Вот почему перенос этой нагрузки на внешний поисковый сервер — отличная идея.
Это первая часть моего учебника по Spring Data Solr. В этом уроке мы реализуем функцию поиска в приложении todo, которое является примером приложения из моего урока Spring MVC Test .
Требования нашей функции поиска просты. Он должен возвращать список записей todo, заголовок или описание которых содержит используемый поисковый термин. Результат поиска
На странице также должна быть ссылка на страницу, используемую для просмотра информации о записи.
Прежде чем мы сможем начать реализацию нашей функции поиска, нам нужно взглянуть на поисковый сервер Solr. Эта запись в блоге предоставляет нам основную информацию о Solr и состоит из трех частей:
- Первая часть дает нам краткое введение в Solr и его модель данных.
- Вторая часть описывает, как мы можем создать схему для нашего экземпляра Solr.
- Последняя часть описывает, как мы можем использовать REST-подобный HTTP API, предоставляемый Solr.
Давайте начнем.
Краткое введение в Solr
Начнем с краткого введения в поисковый сервер Solr. Это введение очень тонкое и дает только ту информацию, которую нам нужно знать, чтобы понять реализацию нашей функции поиска.
Кроме того, хотя Solr сильно зависит от Lucene , эта запись в блоге не делает различий между ними.
Этот раздел описывает
- Модель данных поискового сервера Solr.
- Что происходит, когда новые документы добавляются в Solr.
- Что происходит, когда поисковый запрос выполняется к индексированным данным.
Модель данных
Индекс состоит из документов, которые по сути являются набором полей . Если мы сравним эту модель данных с моделью данных реляционной базы данных, мы заметим следующее сходство:
- Индекс примерно такой же, как таблица базы данных.
- Документ похож на строку таблицы базы данных.
- Поле означает то же, что столбец таблицы базы данных.
Каждое поле документа может быть проиндексировано , сохранено или и то, и другое. Значение этих терминов описано в следующем:
- Индексированное поле — это поле с возможностью поиска и сортировки. Индексированные поля не возвращаются в результатах поиска.
- Сохраненное поле — это поле, значение которого возвращается в результатах поиска.
- Если поле проиндексировано и сохранено , оно доступно для поиска и сортировки. Его значение также возвращается в результатах поиска.
Добавление информации в указатель
Когда новый документ добавляется в Solr, индексированные и сохраненные поля обрабатываются другим способом. Эта разница описана ниже:
- Индексированные поля проходят этап анализа, который обычно разбивает текст на слова и применяет к нему различные преобразования. Результаты этого этапа анализа сохраняются в индексе Solr.
- Значения сохраненных полей сохраняются как есть.
Поиск информации из индекса
Функция поиска может быть разделена на три этапа, которые описаны ниже:
- Обычно поисковый запрос проходит фазу анализа, аналогичную индексированным полям. Цель этого — убедиться, что поисковый запрос соответствует содержимому индекса.
- Solr использует свой индекс для поиска.
- Соответствующие документы возвращаются в запрошенном формате. Каждый документ содержит значения его сохраненных полей.
Создание схемы
Схема используется для настройки следующих вещей:
- Поля документа.
- Как обрабатываются поля документа при добавлении нового документа в индекс.
- Как обрабатываются поля при выполнении поиска по индексу.
Схема настраивается в файле schema.xml , и мы можем создать схему для нашего приложения, выполнив следующие действия:
- Настройте используемые типы полей.
- Настройте поля нашего документа.
- Настройте поля копирования.
- Настройте уникальное ключевое поле нашего документа.
Эти шаги описаны более подробно в следующих подразделах. Скелетная схема нашего экземпляра Solr выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<? xml version = "1.0" encoding = "UTF-8" ?> < schema name = "todo" version = "1.5" > < fields > <!-- Configure fields here --> </ fields > <!-- Configure unique key --> <!-- Configure copy fields here --> < types > <!-- Configure field types here --> </ types > </ schema > |
Примечание . В этом разделе описана схема примера приложения моей записи в блоге « Запуск Solr с Maven» .
Настройка типов полей
Тип поля определяет следующие вещи:
- Тип данных поля.
- Как информация анализируется, когда она добавляется в индекс.
- Как обрабатывается информация при поиске информации из индекса.
Мы можем настроить тип поля с помощью элемента fieldType . Его атрибуты, которые используются в нашей схеме, описаны ниже:
- Атрибут name содержит имя типа поля. Это в основном псевдоним, который используется для объявления типа поля.
- Атрибут class объявляет класс, который реализует рассматриваемый тип поля.
- Атрибут sortMissingLast указывает, как работает сортировка, если значение этого поля отсутствует. Если значение этого атрибута установлено в «true», документы, которые не имеют значения в соответствующем поле, возвращаются последними.
- Атрибут positionIncrementGap объявляет количество пустого пространства, которое помещается между несколькими полями одного и того же документа. Значение этого атрибута используется в многозначных полях, и его идея состоит в том, чтобы предотвратить ложные совпадения в разных полях.
- Атрибут precisionStep используется в ранжированных запросах числовых полей. Мы можем получить больше информации об этом, прочитав документацию по API класса NumericRangeQuery .
Мы можем настроить типы полей нашей схемы, выполнив следующие действия:
- Настройте тип поля для длинных полей
- Настройте тип поля для строковых полей
- Настройте тип поля для полей, содержащих текст
Эти шаги описаны более подробно ниже.
Настройка типа поля для длинных полей
Начнем с настройки простого типа поля для длинных полей, которые не анализируются на этапе индекса или поиска. Мы можем настроить этот тип поля, выполнив следующие действия:
- Установите имя типа поля «long».
- Установите имя реализующего класса в ‘solr.TrieLongField’.
- Установите значение атрибута precisionStep равным нулю.
- Установите значение атрибута positionIncrementGap равным нулю.
Наше объявление типа поля выглядит следующим образом:
1
|
< fieldType name = "long" class = "solr.TrieLongField" precisionStep = "0" positionIncrementGap = "0" /> |
Настройка типа поля для строковых полей
Следующим шагом является настройка простого типа поля для строковых полей, которые не анализируются на этапе индекса или поиска. Мы можем настроить этот тип поля, выполнив следующие действия:
- Установите имя типа поля «строка».
- Установите имя реализующего класса в ‘solr.StrField’.
- Установите для атрибута sortMissingLast значение true.
Объявление нашего строкового поля выглядит следующим образом:
1
|
< fieldType name = "string" class = "solr.StrField" sortMissingLast = "true" /> |
Настройка типа поля для полей, содержащих текст
Последний шаг — настроить тип поля text_general . Мы можем сделать это, выполнив следующие действия:
- Создайте новый тип поля. Установите имя типа поля ‘text_general’. Установите имя реализующего класса в ‘solr.TextField’. Установите значение positionIncrementGap равным 100.
- Создайте новый анализатор, который будет запущен во время индекса. Настройте разделение текста на слова, используя правила разбиения по словам в алгоритме сегментации текста Unicode . Создайте фильтр, который удаляет слова, найденные в файле stopwords.txt, из текста. Преобразовать текст в нижний регистр.
- Создайте новый анализатор, который запускается на этапе запроса. Настройте разделение поискового запроса на слова, используя правила разбиения слов в алгоритме сегментации текста Unicode . Создайте фильтр, который удаляет слова из файла stopwords.txt из поискового запроса. Убедитесь, что применены синонимы, найденные в synonyms.txt . Преобразуйте поисковый запрос в нижний регистр.
Объявление типа поля text_general выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
< fieldType name = "text_general" class = "solr.TextField" positionIncrementGap = "100" > <!-- Configures the analysis done at the index phase --> < analyzer type = "index" > <!-- Uses word break rules of the Unicode Text Segmentation algorith when splitting text into words. --> < tokenizer class = "solr.StandardTokenizerFactory" /> <!-- Removes words found from stopwords.txt file. This filter is case insensitive. --> < filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" enablePositionIncrements = "true" /> <!-- Transforms text to lower case --> < filter class = "solr.LowerCaseFilterFactory" /> </ analyzer > <!-- Configures the analysis done at the query time --> < analyzer type = "query" > <!-- Uses word break rules of the Unicode Text Segmentation algorith when splitting text into words. --> < tokenizer class = "solr.StandardTokenizerFactory" /> <!-- Removes words found from stopwords.txt file. This filter is case insensitive. --> < filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" enablePositionIncrements = "true" /> <!-- Applies synonyms found from the synonyms.txt file. --> < filter class = "solr.SynonymFilterFactory" synonyms = "synonyms.txt" ignoreCase = "true" expand = "true" /> <!-- Transforms text to lower case --> < filter class = "solr.LowerCaseFilterFactory" /> </ analyzer > </ fieldType > |
Мы можем получить больше информации о фазе анализа, прочитав следующие документы:
Настройка полей нашего документа
Мы можем добавить новые поля в наш документ, добавив элементы поля в файл schema.xml . Элемент field имеет много атрибутов, но на данный момент нам необходимо понять значение следующих атрибутов:
- Атрибут name указывает имя поля.
- Индексированный атрибут (true / false) указывает, будет ли поле добавлено в поисковый индекс. Только проиндексированные поля доступны для поиска и сортировки.
- Сохраненный атрибут (true / false) указывает, следует ли возвращать поле в результатах поиска.
- MultiValued (true / false) указывает, может ли поле появляться в документе более одного раза.
- Атрибут type type указывает тип поля.
- Обязательный (true / false) указывает, является ли поле обязательным или нет.
Чтобы максимизировать производительность нашего экземпляра Solr, мы должны следовать следующим рекомендациям:
- Мы не должны хранить поля, которые не являются обязательными в результатах поиска.
- Мы не должны индексировать поля, которые не используются нашей функцией поиска.
Мы можем получить больше информации об оптимальной конфигурации поля, прочитав следующие документы:
Теперь мы готовы настроить фактические поля нашей схемы. Давайте сначала немного поговорим об информации, которую нам нужно показать на странице результатов поиска. Эта информация описана ниже:
- Нам нужен идентификатор записи todo, который используется для создания ссылки на страницу ввода view todo.
- Нам нужен заголовок записи todo, который используется как якорный текст созданной ссылки.
Когда мы знаем, что наше приложение должно иметь возможность искать содержимое заголовка и описания записи todo, мы можем добавить необходимые поля в нашу схему, выполнив следующие действия:
- Добавьте обязательное поле ‘id’ в схему и установите его тип ‘string’. Убедитесь, что это поле проиндексировано и сохранено. Установите значение атрибута multiValued в false.
- Добавьте обязательное поле с названием «title» к схеме и его тип к «text_general». Настройте это поле для индексирования и сохранения. Установите значение многозначного атрибута в false.
- Добавьте поле с именем ‘description’, схема устанавливает его тип ‘text_general’. Настройте это поле для индексации, но не для сохранения. Установите значение атрибута multiValued в false.
- Добавьте поле с именем ‘text’ и установите его тип ‘text_general’. Настройте это поле для индексации, но не для сохранения. Установите для его атрибута multiValue значение true. Это поле является полем, в котором хранится содержимое всех других проиндексированных текстовых полей.
- Добавьте поле с именем _version_ и установите его тип long. Настройте это поле для индексирования и сохранения.
Наши полевые объявления выглядят следующим образом:
1
2
3
4
5
|
< field name = "id" type = "string" indexed = "true" stored = "true" required = "true" multiValued = "false" /> < field name = "title" type = "text_general" indexed = "true" stored = "true" required=“true” multiValued = "false" /> < field name = "description" type = "text_general" indexed = "true" stored = "false" multiValued = "false" /> < field name = "text" type = "text_general" indexed = "true" stored = "false" multiValued = "true" /> < field name = "_version_" type = "long" indexed = "true" stored = "true" /> |
Настройка полей копирования
Мы используем поля копирования для копирования содержимого полей заголовка и описания в текстовое поле. Мы можем настроить поля копирования с помощью элемента copyField . Мы можем создать необходимую конфигурацию, выполнив следующие действия:
- Создайте поле копирования, которое копирует значение поля title в поле text.
- Создайте поле копирования, которое копирует значение поля «описание» в поле «текст».
Объявление наших полей копирования выглядит следующим образом:
1
2
|
< copyField source = "title" dest = "text" /> < copyField source = "description" dest = "text" /> |
Настройка поля уникального ключа нашего документа
Уникальный ключ — это поле, которое должно быть уникальным для всех документов. Необязательно указывать уникальный ключ документа, но если мы решим это сделать, это означает, что индекс не может содержать два документа, которые имеют одинаковое значение в поле, настроенном как уникальный ключ.
В нашем случае мы используем поле «id» в качестве уникального ключа нашего документа. Мы можем сделать эту конфигурацию, добавив следующий XML в файл schema.xml :
1
|
< uniqueKey >id</ uniqueKey > |
Использование REST-подобного HTTP API
Solr предлагает REST-подобный HTTP API, который мы можем использовать для добавления информации в Solr и выполнения поисковых запросов по его индексу. Оба эти варианта использования описаны ниже.
Примечание . В этом разделе предполагается, что мы используем пример приложения из моей записи в блоге « Запуск Solr с Maven» .
Добавление информации в Solr
Мы можем добавить новую информацию в Solr, выполнив следующие действия:
- Отправьте запрос POST на URL-адрес «http: // localhost: 8983 / solr / update / json? Commit = true».
- Установите тип содержимого запроса ‘application / json’.
- Отправка добавленной информации в теле запроса в виде JSON.
Содержимое нашего тела запроса выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
|
[ { "id" : "1" , "title" : "Write introduction to Solr" , "description" : "This blog entry provides an introduction to Solr search server" }, { "id" : "2" , "title" : "Implement example application" , "description" : "This application demonstrates the usage of spring-data-solr." } ] |
Теперь мы добавили два документа в наш индекс Solr, используя REST-подобный API, предоставленный Solr.
Однако полезно знать, что есть и другие варианты, которые мы можем использовать для добавления информации в индекс Solr. Эти параметры описаны в следующих документах:
- Документы POST JSON
- Импорт записей из базы данных
- Загрузить данные из файла CSV
- Индекс двоичных документов
- Используйте SolrJ
Поиск информации из индекса Solr
Теперь мы готовы к поиску информации, хранящейся в индексе нашего экземпляра Solr. Мы можем выполнять поисковые запросы по индексу Solr, следуя этим рекомендациям:
- Поисковый запрос выполняется путем отправки запроса GET по URL-адресу ‘http: // localhost: 8983 / solr / todo / select’.
- Строка запроса должна быть установлена как значение параметра запроса q .
- Формат результатов запроса должен быть установлен как значение параметра запроса wt .
Давайте продолжим и выясним, как мы можем составить список всех документов, найденных по индексу, и выполнить простой поисковый запрос по индексированным данным.
Поиск всех документов индекса
Мы можем перечислить все документы в формате JSON, выполнив следующие действия:
- Отправьте запрос GET на URL ‘http: // localhost: 8983 / solr / todo / select’.
- Установите значение параметра запроса q в «*. *»
- Установите значение параметра запроса wt в ‘json’.
Когда мы отправляем запрос GET на URL ‘http: // localhost: 8983 / solr / todo / select? Q = *% 3A * & wt = json’, мы должны получить следующий JSON:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
{ "responseHeader" : { "status" : 0 , "QTime" : 1 , "params" :{ "wt" : "json" , "q" : "*:*" } }, "response" :{ "numFound" : 2 , "start" : 0 , "docs" :[ { "id" : "1" , "title" : "Write introduction to Solr" , "_version_" : 1425949176574771200 }, { "id" : "2" , "title" : "Implement example application" , "_version_" : 1425949176662851584 } ] } } |
Поиск информации из индекса
Мы можем искать все документы, название или описание которых содержит слово «приложение», выполнив следующие действия:
- Отправьте запрос GET на URL ‘http: // localhost: 8983 / solr / todo / select’.
- Установите значение параметра запроса q в ‘application’
- Установите значение параметра запроса wt в ‘json’.
Когда мы отправляем запрос GET на URL «http: // localhost: 8983 / solr / todo / select? Q = application & wt = json», мы должны получить следующий JSON:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
{ "responseHeader" :{ "status" : 0 , "QTime" : 7 , "params" :{ "wt" : "json" , "q" : "application" } }, "response" :{ "numFound" : 1 , "start" : 0 , "docs" :[ { "id" : "2" , "title" : "Implement example application" , "_version_" : 1425949176662851584 } ] } } |
Конец
Теперь мы получили информацию, необходимую для понимания концепций, описанных в следующих частях моего учебника по Spring Data Solr. Эта запись в блоге научила нас трем вещам:
- Мы знаем основы модели данных Solr.
- Мы знаем, как мы можем настроить схему нашего экземпляра Solr.
- Мы знаем, как мы можем добавлять документы в индекс Solr и искать информацию из него с помощью HTTP API Solr.