Мы все знаем, как хорошо, когда в программном обеспечении, которое мы создаем, есть уровни абстракции. Мы стремимся абстрагировать реализацию от контрактов методов, используя интерфейсы, мы используем n-уровневые архитектуры, чтобы мы могли абстрагироваться и разделять разные системные уровни друг от друга. Это очень хорошо — когда мы меняем одну часть, нам не нужно касаться других частей, которые знали только о контрактах методов, API и т. Д. Почему бы не сделать то же самое с поисковыми запросами? Можем ли мы сделать это в Elasticsearch и Solr? Мы можем, и я покажу вам, как это сделать.
Эта проблема
Представьте, что у нас есть запрос, сложный, с бустами, сортировками, гранями и так далее. Однако в большинстве случаев запрос довольно статичен, когда дело касается его структуры, и единственное, что изменяется, — это один из фильтров в запросе (фактически значение фильтра) и запрос, введенный пользователем. Я полагаю, что такая ситуация может зазвонить тому, кто разработал поисковое приложение. Конечно, мы можем включить весь запрос в само приложение и использовать его повторно. Но в таком случае, например, для изменений в надстройках необходимо развернуть приложение или файл конфигурации. И если более чем одно приложение использует один и тот же запрос, мы должны изменить их все.
Что если бы мы могли внести изменения только на стороне поискового сервера и позволить приложению передавать только необходимые данные? Это было бы неплохо, но это требует от нас некоторой работы на стороне поискового сервера.
Для целей поста в блоге предположим, что мы хотим получить запрос, который:
- ищет документы с условиями, введенными пользователем,
- ограничивает поиски данной категорией,
- отображает побочные эффекты для ценовых диапазонов
Это простой пример, поэтому запросы легко понять. Таким образом, в идеальном мире нам нужно только предоставить запрос пользователя и идентификатор категории для поисковой системы.
Как сделать это с Solr
Когда дело доходит до Apache Solr, мы будем использовать локальные параметры и возможность разыменования параметров. Наш начальный запрос может выглядеть так:
/solr/select?q=blue+jeans&qf=name^100+description^10&defType=edismax&fq=categoryId:12&facet=true&facet.query=price:[*+TO+30]&facet.query=price:[30+TO+100]&facet.query=price:[100+TO+*]
Как видите, запрос прост, как может быть. Теперь давайте изменим его, поэтому нам нужно только указать идентификатор запроса и категории. Сначала нам нужно создать новый обработчик, который сможет обрабатывать наши запросы. Такой обработчик будет включать нашу конфигурацию запроса и может выглядеть так:
<requestHandler name="/search" class="solr.StandardRequestHandler">
<lst name="defaults">
<str name="q">_query_:"{!edismax qf=$queryFields v=$userQuery}"</str>
<str name="queryFields">name^100 description^10</str>
<str name="df">name</str>
<str name="q.op">OR</str>
<bool name="facet">true</bool>
<str name="facet.query">price:[* TO 30]</str>
<str name="facet.query">price:[30 TO 100]</str>
<str name="facet.query">price:[100 TO *]</str>
</lst>
</requestHandler>
И наш измененный запрос будет выглядеть так:
/solr/search?userQuery=blue+jeans&fq=categoryId:12
Как видите, наш запрос был упрощен. Стоит обратить внимание на то, что мы сейчас используем созданный нами обработчик / search . В дополнение к этому, вместо того, чтобы передавать обычный запрос, мы теперь отправляем только userQuery, который содержит запрос, введенный пользователем, и фильтр для идентификатора категории. Мы используем $ userQuery в конфигурации обработчика запросов, чтобы получить значение запроса и передать его Solr. Просто как тот.
Как это происходит с Elasticsearch
Чтобы добиться того же с Elasticsearch, мы будем использовать версию 1.1.0 и функциональность шаблонов запросов. Давайте предположим, что наш запрос выглядит следующим образом:
{
"query": {
"filtered": {
"query": {
"multi_match" : {
"query": "blue jeans",
"fields": [ "name^100", "description^10" ]
}
},
"filter": {
"term" : {
"categoryId": 12
}
}
}
},
"facets": {
"price": {
"range": {
"field": "price",
"ranges": [
{
"to": 30
},
{
"from": 30,
"to": 100
},
{
"from": 100
}
]
}
}
}
}
Теперь давайте изменим его и используем недавно введенный шаблон запроса. Мы создадим новый файл с именем shopQuery.mustache в папке $ ES_HOME / config / scripts . имя шаблона должно заканчиваться расширением .mustache . Имя шаблона, который мы будем использовать, будет именем файла без расширения, поэтому в нашем случае это будет shopQuery . Содержимое файла шаблона будет следующим:
{
"query": {
"filtered": {
"query": {
"multi_match" : {
"query": "{{userQuery}}",
"fields": [ "name^100", "description^10" ]
}
},
"filter": {
"term" : {
"categoryId": {{userCategory}}
}
}
}
},
"facets": {
"price": {
"range": {
"field": "price",
"ranges": [
{
"to": 30
},
{
"from": 30,
"to": 100
},
{
"from": 100
}
]
}
}
}
}
Как видите, мы изменили запрос на {{userQuery}} и значение идентификатора категории на {{userCategory}} . После перезапуска Elasticsearch мы можем использовать следующий запрос для использования шаблона:
curl -XGET "http://localhost:9200/_search/template" -d '{
"template": "shopQuery",
"params": {
"userQuery": "blue jeans",
"userCategory": 12
}
}'
Мы использовали конечную точку / _search / template и указали имя шаблона, используя свойство шаблона в запросе. В дополнение к этому мы указали два параметра в разделе параметров . Кажется, лучше, правда?
Резюме
Как мы видим, мы можем упростить запросы для Solr и Elasticsearch. Однако не все здесь зеленое. В случае Solr нам необходимо перезагрузить конфигурацию для ядра или коллекции, чтобы изменения были видны. Поэтому каждый раз, когда мы хотим изменить запрос или добавить новый, нам нужно изменить конфигурацию и перезагрузить ее. То же самое касается Elasticsearch, по крайней мере, в описанной версии 1.1.0 — нам нужно перезапустить узел, чтобы шаблон стал видимым. Тем не менее, шаблоны поиска в Elasticsearch кажутся более гибкими, потому что мы можем просто установить параметры для любого значения (и не только). Но я полагаю, что это изменится в будущем, и мы сможем использовать API для проталкивания запросов, так же, как мы можем с потеплением запросов. В таком случае шаблоны будут проще в использовании и обслуживании.