1. Общ преглед
В тази статия ще се потопим в някои ключови концепции, свързани с пълнотекстовите търсачки, със специален фокус върху Elasticsearch.
Тъй като това е статия, ориентирана към Java, няма да даваме подробен урок стъпка по стъпка за това как да настроим Elasticsearch и да покажем как работи под капака. Вместо това ще насочим Java клиента и как да използваме основните функции като индекс , изтриване , изтегляне и търсене .
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 заявки. Можем да проверим дали е стартиран успешно, като отворим // 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, трябва да инициираме 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 results = Arrays.stream(searchHits) .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) .collect(Collectors.toList());
Резултатите, върнати от метода search () , се наричат Hits , всеки Hit се отнася до JSON документ, съответстващ на заявка за търсене.
В този случай списъкът с резултати съдържа всички данни, съхранявани в клъстера. Имайте предвид, че в този пример използваме библиотеката FastJson, за да конвертираме JSON Strings в 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)
Предоставяне на име на поле - напр. Пълно име и съответната стойност - напр. Джон Доу , методът 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 може да окаже огромно влияние върху производителността.
The coefficient of importance is used to order the result set of hits returned after executing the search() method.
If you are more familiar with the Lucene queries syntax, you can use the simpleQueryStringQuery() method to customize search queries:
QueryBuilder simpleStringQuery = QueryBuilders .simpleQueryStringQuery("+John -Doe OR Janette");
As you can probably guess, we can use the Lucene's Query Parser syntax to build simple, yet powerful queries. Here're some basic operators that can be used alongside the AND/OR/NOT operators to build search queries:
- The required operator (+): requires that a specific piece of text exists somewhere in fields of a document.
- Операторът за забрана ( - ): изключва всички документи, които съдържат ключова дума, декларирана след символа ( - ).
6. Заключение
В тази бърза статия видяхме как да използваме Java API на ElasticSearch, за да изпълним някои от често срещаните функции, свързани с пълнотекстовите търсачки.
Можете да проверите примера, предоставен в тази статия в проекта GitHub.