1. Обзор
В этой статье мы рассмотрим некоторые ключевые концепции, связанные с полнотекстовыми поисковыми системами, с особым акцентом на Elasticsearch.
Поскольку это статья, ориентированная на Java, мы не собираемся давать подробное пошаговое руководство по настройке Elasticsearch и показывать, как он работает под капотом. Вместо этого мы будем ориентироваться на клиент Java и на то, как использовать основные функции, такие как index , delete , get и search .
2. Настройка
Для простоты мы будем использовать образ docker для нашего экземпляра Elasticsearch, через экземпляр Elasticsearch, прослушивающий порт 9200, будет делать .
Мы начинаем с запуска нашего экземпляра Elasticsearch:
docker run -d --name es762 -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
По умолчанию Elasticsearch прослушивает порт 9200 для предстоящих HTTP-запросов. Мы можем убедиться, что он успешно запущен, открыв http://localhost:9200/ URL в вашем любимом браузере:
{ "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 файл:
org.elasticsearch elasticsearch 7.6.2
Вы всегда можете проверить последние версии, размещенные в Maven Central, по ссылке, предоставленной ранее.
4. Java API
Прежде чем мы перейдем прямо к тому, как использовать основные функции Java API, нам нужно инициировать Сброс клиента высокого уровня :
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); }
Обратите внимание, что для создания и обработки документов можно использовать любую библиотеку Java JSON . Если вы не знакомы ни с одним из них, вы можете использовать помощники 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(); Listresults = Arrays.stream(searchHits) .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) .collect(Collectors.toList());
Результаты , возвращаемые методом search () , называются Хитами , каждый Hit относится к документу JSON, соответствующему поисковому запросу.
В этом случае список results содержит все данные, хранящиеся в кластере. Обратите внимание, что в этом примере мы используем библиотеку FastJson для преобразования JSON Строк в объекты Java.
Мы можем улучшить запрос, добавив дополнительные параметры, чтобы настроить запрос с помощью методов Query Builders :
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 из кластера, используя его идентификатор:
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. Примеры построителей запросов
Класс Query Builders предоставляет множество статических методов, используемых в качестве динамических сопоставителей для поиска определенных записей в кластере. При использовании метода search() для поиска конкретных документов JSON в кластере мы можем использовать построители запросов для настройки результатов поиска.
Вот список наиболее распространенных применений API Query Builders .
Метод matchAllQuery() возвращает объект QueryBuilder , соответствующий всем документам в кластере:
QueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
Запрос range() соответствует документам, в которых значение поля находится в определенном диапазоне:
QueryBuilder matchDocumentsWithinRange = QueryBuilders .rangeQuery("price").from(15).to(100)
Предоставляя имя поля – например, Полное имя и соответствующее значение – например, 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 имеет значение boost, равное трем, что делает его более важным, чем другие поля. Обратите внимание, что можно использовать подстановочные знаки и запросы регулярных выражений, но с точки зрения производительности остерегайтесь потребления памяти и задержки времени отклика при работе с подстановочными знаками, потому что что-то вроде *_apples может оказать огромное влияние на производительность.
Коэффициент важности используется для упорядочения результирующего набора обращений, возвращаемых после выполнения метода s earch () .
Если вы более знакомы с синтаксисом запросов Lucene, вы можете использовать метод simple Query String Query() для настройки поисковых запросов:
QueryBuilder simpleStringQuery = QueryBuilders .simpleQueryStringQuery("+John -Doe OR Janette");
Как вы, вероятно, догадываетесь, мы можем использовать синтаксис синтаксического анализатора запросов Lucene для создания простых, но мощных запросов . Вот некоторые основные операторы, которые можно использовать вместе с операторами И/ИЛИ/НЕ для построения поисковых запросов:
- Требуемый оператор ( + ): требует, чтобы определенный фрагмент текста существовал где-то в полях документа.
- Оператор запрета ( – ): исключает все документы, содержащие ключевое слово, объявленное после символа ( – ).
6. Заключение
В этой краткой статье мы рассмотрели, как использовать API Java Elasticsearch для выполнения некоторых общих функций, связанных с полнотекстовыми поисковыми системами.
Вы можете ознакомиться с примером, приведенным в этой статье в проекте GitHub .