Перейти к основному содержимому

Руководство по Elasticsearch в Java

· 5 мин. чтения

1. Обзор

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

Поскольку эта статья ориентирована на Java, мы не будем давать подробное пошаговое руководство по настройке Elasticsearch и показывать, как он работает внутри. Вместо этого мы нацелимся на Java-клиент и на то, как использовать основные функции, такие как index , delete , get и search .

2. Настройка

Для простоты мы будем использовать образ докера для нашего экземпляра Elasticsearch, хотя подойдет любой экземпляр Elasticsearch, прослушивающий порт 9200 .

Начнем с запуска нашего экземпляра Elasticsearch:

docker run -d --name es762 -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2

По умолчанию Elasticsearch прослушивает порт 9200 для предстоящих HTTP-запросов. Мы можем убедиться, что он успешно запущен, открыв URL-адрес http://localhost:9200/ в вашем любимом браузере:

{
"name" : "M4ojISw",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "CNnjvDZzRqeVP-B04D3CmA",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "2f4c224",
"build_date" : "2020-03-18T23:22:18.622755Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.8.0-beta1"
},
"tagline" : "You Know, for Search"
}

3. Конфигурация Maven

Теперь, когда у нас есть базовый кластер Elasticsearch, давайте сразу перейдем к Java-клиенту. Прежде всего, нам нужно объявить следующую зависимость Maven в нашем файле pom.xml :

<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.6.2</version>
</dependency>

Вы всегда можете проверить последние версии, размещенные на Maven Central, по ссылке, предоставленной ранее.

4. Java-API

Прежде чем мы перейдем непосредственно к тому, как использовать основные функции Java API, нам нужно инициировать RestHighLevelClient :

ClientConfiguration clientConfiguration =
ClientConfiguration.builder().connectedTo("localhost:9200").build();
RestHighLevelClient client = RestClients.create(clientConfiguration).rest();

4.1. Индексирование документов

Функция index() позволяет сохранить произвольный документ JSON и сделать его доступным для поиска:

@Test
public void givenJsonString_whenJavaObject_thenIndexDocument() {
String jsonObject = "{\"age\":10,\"dateOfBirth\":1471466076564,"
+"\"fullName\":\"John Doe\"}";
IndexRequest request = new IndexRequest("people");
request.source(jsonObject, XContentType.JSON);

IndexResponse response = client.index(request, RequestOptions.DEFAULT);
String index = response.getIndex();
long version = response.getVersion();

assertEquals(Result.CREATED, response.getResult());
assertEquals(1, version);
assertEquals("people", index);
}

Обратите внимание, что для создания и обработки ваших документов можно использовать любую библиотеку JSON Java . Если вы не знакомы ни с одним из них, вы можете использовать помощники Elasticsearch для создания собственных документов JSON :

XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("fullName", "Test")
.field("dateOfBirth", new Date())
.field("age", "10")
.endObject();

IndexRequest indexRequest = new IndexRequest("people");
indexRequest.source(builder);

IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
assertEquals(Result.CREATED, response.getResult());

4.2. Запрос индексированных документов

Теперь, когда у нас есть проиндексированный типизированный документ JSON с возможностью поиска, мы можем продолжить поиск, используя метод search() :

SearchRequest searchRequest = new SearchRequest();
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] searchHits = response.getHits().getHits();
List<Person> results =
Arrays.stream(searchHits)
.map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
.collect(Collectors.toList());

Результаты, возвращаемые методом search() , называются Hits , каждое обращение относится к документу JSON, соответствующему поисковому запросу.

В этом случае список результатов содержит все данные, хранящиеся в кластере. Обратите внимание, что в этом примере мы используем библиотеку FastJson для преобразования строк JSON в объекты Java.

Мы можем улучшить запрос, добавив дополнительные параметры, чтобы настроить запрос с помощью методов QueryBuilders :

SearchSourceBuilder builder = new SearchSourceBuilder()
.postFilter(QueryBuilders.rangeQuery("age").from(5).to(15));

SearchRequest searchRequest = new SearchRequest();
searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
searchRequest.source(builder);

SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

4.3. Получение и удаление документов

Методы get() и delete () позволяют получить или удалить JSON-документ из кластера по его id:

GetRequest getRequest = new GetRequest("people");
getRequest.id(id);

GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
// process fields

DeleteRequest deleteRequest = new DeleteRequest("people");
deleteRequest.id(id);

DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

Синтаксис довольно прост, вам просто нужно указать индекс вместе с идентификатором объекта.

5. Примеры QueryBuilders

Класс QueryBuilders предоставляет множество статических методов, используемых в качестве динамических средств сопоставления для поиска определенных записей в кластере. При использовании метода search() для поиска определенных документов JSON в кластере мы можем использовать построители запросов для настройки результатов поиска.

Вот список наиболее распространенных применений API QueryBuilders .

Метод matchAllQuery() возвращает объект QueryBuilder , соответствующий всем документам в кластере:

QueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();

RangeQuery () сопоставляет документы, в которых значение поля находится в определенном диапазоне:

QueryBuilder matchDocumentsWithinRange = QueryBuilders
.rangeQuery("price").from(15).to(100)

Предоставление имени поля — например , fullName и соответствующего значения — например , John Doe . Метод matchQuery() сопоставляет все документы с этими точными значениями поля:

QueryBuilder matchSpecificFieldQuery= QueryBuilders
.matchQuery("fullName", "John Doe");

Мы также можем использовать метод multiMatchQuery() для создания версии запроса соответствия с несколькими полями:

QueryBuilder matchSpecificFieldQuery= QueryBuilders.matchQuery(
"Text I am looking for", "field_1", "field_2^3", "*_field_wildcard");

Мы можем использовать символ вставки (^), чтобы увеличить определенные поля .

В нашем примере для поля field_2 установлено значение повышения, равное трем, что делает его более важным, чем другие поля. Обратите внимание, что можно использовать подстановочные знаки и запросы регулярных выражений, но с точки зрения производительности остерегайтесь потребления памяти и задержки времени отклика при работе с подстановочными знаками, потому что что-то вроде *_apples может сильно повлиять на производительность.

Коэффициент важности используется для упорядочения результирующего набора попаданий, возвращаемого после выполнения метода search() .

Если вы лучше знакомы с синтаксисом запросов Lucene, вы можете использовать метод simpleQueryStringQuery() для настройки поисковых запросов:

QueryBuilder simpleStringQuery = QueryBuilders
.simpleQueryStringQuery("+John -Doe OR Janette");

Как вы, наверное, догадались, мы можем использовать синтаксис парсера запросов Lucene для создания простых, но мощных запросов . Вот некоторые основные операторы, которые можно использовать вместе с операторами И/ИЛИ/НЕ для построения поисковых запросов:

  • Обязательный оператор ( + ): требует, чтобы определенный фрагмент текста существовал где-то в полях документа.
  • Оператор запрета ( - ): исключает все документы, содержащие ключевое слово, объявленное после символа ( - ).

6. Заключение

В этой быстрой статье мы увидели, как использовать Java API ElasticSearch для выполнения некоторых общих функций, связанных с системами полнотекстового поиска.

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