Мы все знаем, как хорошо, когда в программном обеспечении, которое мы создаем, есть уровни абстракции. Мы стремимся абстрагировать реализацию от контрактов методов, используя интерфейсы, мы используем 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 для проталкивания запросов, так же, как мы можем с потеплением запросов. В таком случае шаблоны будут проще в использовании и обслуживании.