Рубрики
Без рубрики

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

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

Автор оригинала: baeldung.

1. введение

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

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

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

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

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

2.2. Неанализуемые поля

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

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

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

3. Запрос соответствия

запрос соответствия принимает текст, цифры и даты.

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

  • логический
  • фраза и
  • phrase_prefix

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

3.1. Сопоставление С Булевыми Операторами

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

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title","Search engines").operator(Operator.AND))
  .build();
SearchHits
articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog"));

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

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "Engines Solutions"))
  .build();
SearchHits
articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); assertEquals("Search engines", articles.getSearchHit(0).getContent().getTitle());

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

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

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

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

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

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

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

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

5. Поиск фраз

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

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

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

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

6. Запрос На Несколько Совпадений

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

NativeSearchQuery 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));
SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation);

SearchRequest searchRequest = 
  new SearchRequest().indices("blog").types("article").source(builder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

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 и изучить API Spring Data Elasticsearch, чтобы использовать эти запросы в своем коде.

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