Статьи

ElasticSearch Учебник для начинающих

1. Введение

В этом примере мы покажем, как использовать Elasticsearch , распределенный механизм базы данных для поиска и анализа свободного текста, основанный на Apache Lucene, с простым Java-клиентом на основе maven .

Мы будем использовать последнюю версию Elasticsearch, то есть ES v6.1.2, при написании этого поста. Для этого примера мы используем следующие технологии:

  • Maven 3
  • Java 8
  • Elasticsearch 6.1.2

Elasticsearch очень хорошо известен благодаря своей способности общаться через RESTful API. Это означает, что мы будем использовать API для взаимодействия с базой данных вместе с методами HTTP, такими как GET, POST, PUT и DELETE. Это хорошо масштабируемая распределенная база данных, которая обеспечивает отличную реализацию с Apache Lucene . Некоторые дополнительные функции Elasticsearch:

  • С общим размером зависимости всего около 300 КБ, Elasticsearch очень легкий
  • Elasticsearch ориентирован исключительно на выполнение запросов. Это означает, что независимо от того, какие операции выполняются с базой данных, они высоко оптимизированы и масштабируемы.
  • Это очень отказоустойчивая система. Если в кластере умирает один узел Elasticsearch, главный сервер очень быстро выявляет проблему и максимально быстро направляет входящие запросы на новый узел.
  • Особенность Elasticsearch заключается в индексируемых текстовых данных, которые можно искать на основе токенов и фильтров.

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

  • Подсчет операций, таких как общее и среднее
  • Выполнение транзакционных запросов с откатами
  • Управление записями, которые будут уникальными для нескольких заданных терминов

Это означает, что Elasticsearch — это база данных с высокой степенью использования, но она отлично подходит для своих собственных доменов.

2. Предпосылки

Вы должны установить Java на своем компьютере, чтобы продолжить, потому что Maven — это инструмент Java. Вы можете скачать Java здесь .

После установки Java в вашей системе вы должны установить maven. Вы можете скачать Maven отсюда .

Наконец, вам нужно установить Elasticsearch. Вы можете скачать его отсюда и следовать инструкциям для вашей ОС. Обратите внимание, что мы будем использовать v6.1.2 для этого урока. Другие версии теперь могут работать точно так же. Вы можете убедиться, что ES работает, открыв этот URL в вашем браузере:

1
localhost:9200

Вы должны получить ответ вроде:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
{
  "name": "wKUxRAO",
  "cluster_name": "elasticsearch",
  "cluster_uuid": "gvBXz7xsS5W4zlZuiADelw",
  "version": {
    "number": "6.1.2",
    "build_hash": "5b1fea5",
    "build_date": "2018-01-10T02:35:59.208Z",
    "build_snapshot": false,
    "lucene_version": "7.1.0",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
  },
  "tagline": "You Know, for Search"
}

Обратите внимание, elasticsearch является именем кластера по умолчанию в Elasticsearch.

3. Настройка проекта

Мы будем использовать один из многих архетипов Maven для создания примера проекта для нашего примера. Чтобы создать проект, выполните следующую команду в каталоге, который вы будете использовать в качестве рабочей области:

1
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=jcg-elasticsearch-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

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

Обратите внимание, что теперь у вас будет новый каталог с тем же именем, что и artifactId внутри выбранного каталога. Теперь не стесняйтесь открывать проект в вашей любимой IDE.

4. Maven Зависимости

Для начала нам нужно добавить соответствующие зависимости Maven в наш проект. Мы добавим следующую зависимость в наш файл pom.xml:

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<properties>
  <elasticsearch.version>6.1.2</elasticsearch.version>
  <jackson.version>2.9.4</jackson.version>
  <java.version>1.8</java.version>
</properties>
 
<dependencies>
  <dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>${elasticsearch.version}</version>
  </dependency>
 
  <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>${elasticsearch.version}</version>
  </dependency>
 
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
  </dependency>
 
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${jackson.version}</version>
  </dependency>
 
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Найдите последнюю зависимость Elasticsearch здесь .

Обратите внимание, что мы использовали Джексона только в качестве стандартной библиотеки JSON для Java в нашем коде.

5. Создание запросов к базе данных

Теперь мы готовы начать сборку нашего проекта и добавить в него больше компонентов.

5.1 Создание модели

Мы начнем с добавления в наш проект очень простой модели Person. Его определение будет очень стандартным, как:

Person.java

01
02
03
04
05
06
07
08
09
10
11
12
13
public class Person {
 
    private String personId;
    private String name;
 
    //standard getters and setters
 
    @Override
    public String toString() {
        return String.format("Person{personId='%s', name='%s'}",
            personId, name);
    }
}

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

5.2 Определение параметров соединения

Мы будем использовать параметры подключения по умолчанию для подключения к Elasticsearch. По умолчанию ES использует два порта: 9200 и 9201.

Параметры подключения

01
02
03
04
05
06
07
08
09
10
11
//The config parameters for the connection
private static final String HOST = "localhost";
private static final int PORT_ONE = 9200;
private static final int PORT_TWO = 9201;
private static final String SCHEME = "http";
 
private static RestHighLevelClient restHighLevelClient;
private static ObjectMapper objectMapper = new ObjectMapper();
 
private static final String INDEX = "persondata";
private static final String TYPE = "person";

Помимо параметров конфигурации соединения, мы также определили приведенные выше параметры индекса, чтобы определить, где хранятся наши данные Person.

Как упомянуто в параметрах выше, Elasticsearch использует два порта, 9200 и 9201. Первый порт, 9200, используется сервером запросов Elasticsearch, с помощью которого мы можем запрашивать базу данных напрямую через API RESTful. Второй порт, 9201, используется сервером REST, с которым внешние клиенты могут подключаться и выполнять операции.

5.3 Создание соединения

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

Синглтон метод получения объекта соединения

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
 * Implemented Singleton pattern here
 * so that there is just one connection at a time.
 * @return RestHighLevelClient
 */
private static synchronized RestHighLevelClient makeConnection() {
 
    if(restHighLevelClient == null) {
        restHighLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost(HOST, PORT_ONE, SCHEME),
                        new HttpHost(HOST, PORT_TWO, SCHEME)));
    }
 
    return restHighLevelClient;
}

Обратите внимание, что здесь мы реализовали шаблон Singleton Design, чтобы не создавать несколько соединений для ES, что экономит много памяти.

Из-за наличия RestHighLevelClient соединение с Elasticsearch является поточно- ориентированным . Лучшее время для инициализации этого соединения будет при запросе приложения или при первом обращении к клиенту. После инициализации этого клиента подключения его можно использовать для выполнения любых поддерживаемых API.

5.4 Закрытие соединения

Как и в более старых версиях Elasticsearch, мы использовали TransportClient и закрывали его, как только закончили с нашими запросами, также необходимо закрыть соединение, когда взаимодействие с БД завершится и с RestHighLevelClient. Вот как это можно сделать:

Закрыть соединение

1
2
3
4
private static synchronized void closeConnection() throws IOException {
    restHighLevelClient.close();
    restHighLevelClient = null;
}

Мы также присвоили null объекту RestHighLevelClient, чтобы шаблон Singleton мог оставаться согласованным.

5.5 Вставка данных

Мы можем вставить данные в базу данных, преобразовав ключи и значения в Hashmap . База данных ES принимает значения только в форме HashMap. Давайте посмотрим фрагмент кода о том, как этого можно достичь:

POST-запрос

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private static Person insertPerson(Person person){
    person.setPersonId(UUID.randomUUID().toString());
    Map<String, Object> dataMap = new HashMap<String, Object>();
    dataMap.put("personId", person.getPersonId());
    dataMap.put("name", person.getName());
    IndexRequest indexRequest = new IndexRequest(INDEX, TYPE, person.getPersonId())
            .source(dataMap);
    try {
        IndexResponse response = restHighLevelClient.index(indexRequest);
    } catch(ElasticsearchException e) {
        e.getDetailedMessage();
    } catch (java.io.IOException ex){
        ex.getLocalizedMessage();
    }
    return person;
}

Выше мы использовали класс UUID Java для создания уникального идентификатора объекта. Таким образом, мы можем контролировать, как создаются идентификаторы объекта.

5.6 Создание запроса GET

Закончив вставку данных в базу данных, мы можем подтвердить операцию, отправив запрос GET на сервер базы данных Elasticsearch. Давайте посмотрим фрагмент кода о том, как это можно сделать:

ПОЛУЧИТЬ запрос

01
02
03
04
05
06
07
08
09
10
11
private static Person getPersonById(String id){
    GetRequest getPersonRequest = new GetRequest(INDEX, TYPE, id);
    GetResponse getResponse = null;
    try {
        getResponse = restHighLevelClient.get(getPersonRequest);
    } catch (java.io.IOException e){
        e.getLocalizedMessage();
    }
    return getResponse != null ?
            objectMapper.convertValue(getResponse.getSourceAsMap(), Person.class) : null;
}

В этом запросе мы просто предоставили основную информацию об объекте, с которым он может быть идентифицирован, т. Е. Индекс, Тип и его уникальный идентификатор. Кроме того, мы получаем на самом деле карту значений, выраженную этим выражением:

Получение карты

1
getResponse.getSourceAsMap()

На самом деле это объектMapper Джексона, который используется для преобразования этой карты в объект POJO, который может быть легко использован в нашей программе, и таким образом, нам не нужно каждый ключ формировать карту, что будет утомительным процессом, когда вы сможете просто есть объект POJO.

5.7 Обновление данных

Мы можем легко сделать запрос на обновление Elasticsearch, сначала идентифицировав ресурс по его индексу, типу и уникальному идентификатору. Затем мы можем использовать новый объект HashMap для обновления любого количества значений в объекте. Вот пример кода:

PUT Query

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private static Person updatePersonById(String id, Person person){
    UpdateRequest updateRequest = new UpdateRequest(INDEX, TYPE, id)
            .fetchSource(true);    // Fetch Object after its update
    try {
        String personJson = objectMapper.writeValueAsString(person);
        updateRequest.doc(personJson, XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest);
        return objectMapper.convertValue(updateResponse.getGetResult().sourceAsMap(), Person.class);
    }catch (JsonProcessingException e){
        e.getMessage();
    } catch (java.io.IOException e){
        e.getLocalizedMessage();
    }
    System.out.println("Unable to update person");
    return null;
}

Обратите внимание на то, что мы сделали выше в следующем утверждении:

PUT Query

1
updateRequest.doc(personJson, XContentType.JSON);

Здесь мы не передали какого-либо конкретного свойства объекта, который необходимо обновить, вместо этого мы передали полный объект JSON, который заменит каждый ключ, присутствующий для этого объекта.

Мы также проверили возможные ошибки через операторы catch. В реальных приложениях вы захотите аккуратно обработать эти ошибки и сделать документированные журналы.

5.8 Удаление данных

Наконец, мы можем удалить данные, просто указав ресурс по его индексу, типу и уникальному идентификатору. Давайте посмотрим фрагмент кода о том, как это можно сделать:

УДАЛИТЬ Запрос

1
2
3
4
5
6
7
8
private static void deletePersonById(String id) {
    DeleteRequest deleteRequest = new DeleteRequest(INDEX, TYPE, id);
    try {
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest);
    } catch (java.io.IOException e){
        e.getLocalizedMessage();
    }
}

Опять же, в запросе DELETE выше мы только что упомянули, как мы можем идентифицировать объект.

5.9 Запуск приложения

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

метод main ()

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args) throws IOException {
 
    makeConnection();
 
    System.out.println("Inserting a new Person with name Shubham...");
    Person person = new Person();
    person.setName("Shubham");
    person = insertPerson(person);
    System.out.println("Person inserted --> " + person);
 
    System.out.println("Changing name to `Shubham Aggarwal`...");
    person.setName("Shubham Aggarwal");
    updatePersonById(person.getPersonId(), person);
    System.out.println("Person updated  --> " + person);
 
    System.out.println("Getting Shubham...");
    Person personFromDB = getPersonById(person.getPersonId());
    System.out.println("Person from DB  --> " + personFromDB);
 
    System.out.println("Deleting Shubham...");
    deletePersonById(personFromDB.getPersonId());
    System.out.println("Person Deleted");
 
    closeConnection();
}

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

Выход программы

1
2
3
4
5
6
7
8
9
Inserting a new Person with name Shubham...
Person inserted --> Person{personId='bfc5ba80-832a-4925-9b8d-525a4e420cb0', name='Shubham'}
Changing name to `Shubham Aggarwal`...
Unable to update person
Person updated --> Person{personId='bfc5ba80-832a-4925-9b8d-525a4e420cb0', name='Shubham Aggarwal'}
Getting Shubham...
Person from DB -->Person{personId='bfc5ba80-832a-4925-9b8d-525a4e420cb0', name='Shubham Aggarwal'}
Deleting Shubham...
Person Deleted

Конечно, идентификаторы могут отличаться. Обратите внимание, что мы закрыли соединение после того, как закончили с запросами. Это помогает JVM вернуть память, которая была сохранена соединением ES.

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

В этом уроке мы изучили, как мы можем использовать Elasticsearch вместе с простым Java-клиентом, который использует REST-клиент . Выбор использования клиента REST для его использования в реальных приложениях должен быть изучен на масштабируемом примере. Это выбор, который нам нужно сделать, пока мы начинаем разработку приложения.

Узнайте больше о Elasticsearch в нашем курсе Elasticsearch .

7. Загрузите полный исходный код

Это было руководство по клиенту REST ElasticSearch и запросам с Java, где мы взаимодействовали с базой данных Elasticsearch с помощью операций RESTful.

Скачать
Вы можете скачать полный исходный код этого примера здесь: Elasticsearch Example