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 .