Запросы Elasticsearch с данными Spring

Запросы Elasticsearch с данными Spring

1. Вступление

Вprevious article мы продемонстрировали, как настроить и использовать Spring Data Elasticsearch для проекта. В этой статье мы рассмотрим несколько типов запросов, предлагаемых Elasticsearch, а также поговорим об анализаторах полей и их влиянии на результаты поиска.

2. Анализаторы

Все сохраненные строковые поля по умолчанию обрабатываются анализатором. Анализатор состоит из одного токенизатора и нескольких токен-фильтров, и перед ним обычно стоят один или несколько символьных фильтров.

Анализатор по умолчанию разбивает строку по общим разделителям слов (таким как пробелы или знаки пунктуации) и помещает каждый токен в нижний регистр. Он также игнорирует общие английские слова.

Elasticsearch также может быть сконфигурирован так, чтобы рассматривать поле как проанализированное и не анализированное одновременно.

Например, в классеArticle предположим, что мы храним поле заголовка как стандартное анализируемое поле. Это же поле с суффиксомverbatim будет сохранено как непроанализированное поле:

@MultiField(
  mainField = @Field(type = Text, fielddata = true),
  otherFields = {
      @InnerField(suffix = "verbatim", type = Keyword)
  }
)
private String title;

Здесь мы применяем аннотацию@MultiField, чтобы сообщить Spring Data, что мы хотели бы, чтобы это поле было проиндексировано несколькими способами. Основное поле будет иметь имяtitle и будет проанализировано в соответствии с описанными выше правилами.

Но мы также предоставляем вторую аннотацию@InnerField, которая описывает дополнительную индексацию поляtitle. Мы используемFieldType.keyword, чтобы указать, что мы не хотим использовать анализатор при выполнении дополнительной индексации поля, и что это значение должно быть сохранено с использованием вложенного поля с суффиксомverbatim.

2.1. Анализируемые поля

Давайте посмотрим на пример. Предположим, что статья с названием «Spring Data Elasticsearch» добавлена ​​в наш индекс. Анализатор по умолчанию разбивает строку на пробелы и генерирует лексемы в нижнем регистре: «spring», «data“, и«elasticsearch ».

Теперь мы можем использовать любую комбинацию этих терминов для соответствия документу:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "elasticsearch data"))
  .build();

2.2. Неанализированные поля

Неанализированное поле не маркируется, поэтому может быть сопоставлено только целиком при использовании запросов на совпадение или термин:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch"))
  .build();

Используя запрос на совпадение, мы можем искать только по полному заголовку, который также учитывает регистр.

3. Соответствующий запрос

match query принимает текст, числа и даты.

Существует три типа запросов на совпадение:

  • логический

  • phrase и

  • фраза_префикс

В этом разделе мы рассмотрим запрос соответствияboolean.

3.1. Сопоставление с логическими операторами

boolean - тип запроса соответствия по умолчанию; вы можете указать, какой логический оператор использовать (по умолчаниюor):

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title","Search engines").operator(AND))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class);

Этот запрос вернет статью с заголовком «Поисковые системы», указав два термина из заголовка с операторомand. Но что произойдет, если мы будем искать с оператором по умолчанию (or), когда соответствует только один из терминов?

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "Engines Solutions"))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); assertEquals("Search engines", articles.get(0).getTitle());

Статья «Search engines» по-прежнему соответствует, но она будет иметь более низкую оценку, потому что не все термины совпадают.

Сумма баллов каждого соответствующего термина складывается в общую сумму баллов каждого результирующего документа.

Могут быть ситуации, когда документ, содержащий редкий термин, введенный в запрос, будет иметь более высокий ранг, чем документ, который содержит несколько общих терминов.

3.2. Нечеткость

Когда пользователь делает опечатку в слове, все еще можно сопоставить ее с поиском, указав параметрfuzziness, который допускает неточное сопоставление.

Для строковых полейfuzziness означает расстояние редактирования: количество односимвольных изменений, которые необходимо внести в одну строку, чтобы сделать ее такой же, как другую строку.

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "spring date elasticsearch")
  .operator(AND)
  .fuzziness(Fuzziness.ONE)
  .prefixLength(3))
  .build();

Параметрprefix_length используется для повышения производительности. В этом случае мы требуем, чтобы первые три символа точно совпадали, что уменьшает количество возможных комбинаций.

Фазовый поиск более строгий, хотя вы можете управлять им с помощью параметраslop. Этот параметр сообщает фразе запроса, насколько далеко друг от друга могут находиться термины, хотя документ считается соответствующим.

Другими словами, он представляет количество раз, которое вам нужно переместить термин, чтобы привести запрос в соответствие:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1))
  .build();

Здесь запрос будет соответствовать документу с заголовком «Spring Data Elasticsearch», потому что мы установили slop равным единице.

6. Multi Match Query

Если вы хотите выполнить поиск в нескольких полях, вы можете использоватьQueryBuilders#multiMatchQuery(), где вы указываете все поля для сопоставления:

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(multiMatchQuery("tutorial")
    .field("title")
    .field("tags")
    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS))
  .build();

Здесь мы ищем совпадения в поляхtitle иtags.

Обратите внимание, что здесь мы используем стратегию оценки «лучшие поля». Максимальная оценка среди полей будет считаться оценкой документа.

7. Скопления

В нашем классеArticle мы также определили полеtags, которое не анализируется. Мы могли бы легко создать облако тегов, используя агрегацию.

Помните, что, поскольку поле не проанализировано, теги не будут маркированы:

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
  .field("tags")
  .order(Terms.Order.count(false));
SearchResponse response = client.prepareSearch("blog")
  .setTypes("article")
  .addAggregation(aggregation)
  .execute().actionGet();

Map results = response.getAggregations().asMap();
StringTerms topTags = (StringTerms) results.get("top_tags");

List keys = topTags.getBuckets()
  .stream()
  .map(b -> b.getKeyAsString())
  .collect(toList());
assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);

8. Резюме

В этой статье мы обсудили разницу между анализируемыми и неанализированными полями, а также то, как это различие влияет на поиск.

Мы также узнали о нескольких типах запросов, предоставляемых Elasticsearch, таких как запрос на совпадение, запрос на совпадение фраз, запрос на полнотекстовый поиск и логический запрос.

Elasticsearch предоставляет много других типов запросов, таких как гео-запросы, запросы сценариев и составные запросы. Вы можете прочитать о них вElasticsearch documentation и изучить Spring Data Elasticsearch API, чтобы использовать эти запросы в своем коде.

Вы можете найти проект, содержащий примеры, использованные в этой статье, вthe GitHub repository.

Related