Elastic Search Java Api

Java Elasticsearch

предисловие

ПреамбулаМы упомянули API-интерфейсы Rest для Elastic Search для управления индексами. Фактически Rest Api Elastic Search предоставляет все рабочие интерфейсы. В языках программирования можно напрямую использовать Rest Api для вызова всех функций Elastic Search, но это очень неудобно и интуитивно понятно, поэтому Elastic Search официально предоставляет интерфейсы Api для многих языков. Официально предоставленные интерфейсы языков программирования включают:

  • Java
  • JavaScript
  • Groovy
  • PHP
  • .NET
  • Perl
  • Python
  • Ruby

В то же время сообщество программистов также предоставляет API для большого количества языков программирования. В настоящее время существуют в основном

  • B4J
  • Clojure
  • ColdFusion (CFML)
  • Erlang
  • Go
  • Groovy
  • Haskell
  • Java
  • JavaScript
  • kotlin
  • Lua
  • .NET
  • OCaml
  • Perl
  • PHP
  • Python
  • R
  • Ruby
  • Rust
  • Scala
  • Smalltalk
  • Vert.x

Обычно мы разрабатываем на Java. Итак, здесь я расскажу о том, как используются Java API Elastic Search.

Готов к работе

Чтобы проиллюстрировать возможности API Java, мы подготовили сценарий. Здесь мы предполагаем набор авторов, у каждого автора есть идентификатор, имя, пол, возраст, описание нескольких полей. Нам нужно запросить автора по имени, возрасту, ключевым словам в описании,

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

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

Обзор API Java

Elastic Search предоставляет официальные API Java. Здесь есть две категории: Low Level Rest Api (низкоуровневый Rest Api) и High Leve Rest Api (высокоуровневый Rest Api).

Так называемый низкоуровневый API означает не то, что функция относительно слаба, а то, что API относительно близок к базовой реализации. Официальный низкоуровневый API — это первый уровень инкапсуляции оригинального Rest API. Просто инкапсулирует детали вызова Http. Программе все еще нужно собрать строку условия запроса, проанализировать возвращаемую строку результата json и так далее. В то же время ему также необходимо иметь дело с различными методами и заголовками протокола http.

Высокоуровневый API — это дальнейшая инкапсуляция низкоуровневого API. Вам не нужно заботиться о методе интерфейса, заголовке протокола или ручной комбинации строки параметров вызова. определенный анализ возвращаемой строки json. Это более удобно в использовании. Но высокоуровневый API не реализует все функции, которые реализует низкоуровневый API. Поэтому, если вы столкнулись с такой ситуацией, вам также необходимо использовать низкоуровневый API для реализации собственных функций.

Сторонний Java-клиент — это клиент Elastic Search, разработанный сообществом. Официально упомянуты два проекта с открытым исходным кодом на GitHub.Flummi,Jest

Инструкции по использованию Java Low Level Rest API

Преимущество низкоуровневых API в том, что очень мало зависимостей от других библиотек и они полностью функциональны. Недостатком является то, что упаковка недостаточно продвинута, поэтому она все еще очень громоздка в использовании. Давайте сначала посмотрим, как используется низкоуровневый API.

импортировать зависимости

在前面建立的 Maven Java 工程中,要使用 Elastic Search 的低级 Api,首先要引入 低级 Api 的依赖。 Следующее

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>6.1.1</version>
</dependency>

построить клиент

RestClient restClient = RestClient.builder(
        new HttpHost("localhost", 9200, "http"),
        new HttpHost("localhost", 9201, "http")).build();

Мы создаем клиент Elastic Search Rest с помощью статических методов builder(HttpHost... hosts) и builder() объекта RestClient. Где hosts — переменный параметр, используемый для указания IP-адреса, порта и протокола узлов кластера Elastic Cluster.

вызов метода

После установки клиента Rest Api вызывается двумя способами. Один синхронный, другой асинхронный.

Синхронный вызов

Объявление основного метода для синхронного вызова выглядит так:

public Response performRequest(String method, String endpoint, Header... headers) throws IOException

public Response performRequest(String method, String endpoint, Map<String, String> params, Header... headers) throws IOException

public Response performRequest(String method, String endpoint, Map<String, String> params,
                                   HttpEntity entity, Header... headers) throws IOException

Это три перегруженных метода.Метод параметра представляет метод Rest Api, например PUT, GET, POST, DELETE и т. д.; конечная точка параметра представляет собой адрес параметра Rest Api из поля ip:port в Rest URL-адрес API После этого; params — это параметр, передаваемый в виде параметров URL-адреса; entity — параметр, передаваемый через тело http; headers — переменный параметр, который может быть передан в соответствующей информации http-заголовка.

Например, я хочу просмотреть информацию об индексе author_test, мы можем использовать следующий код, чтобы получить

Response response = restClient.performRequest("GET", "/author_test");

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

String queryJson = "{\n" +
        "    \"query\": {\n" +
        "        \"match\": {\n" +
        "            \"des\": \"软件\"\n" +
        "        }\n" +
        "    }\n" +
        "}";
Response response = restClient.performRequest("POST",
        "/author_test/_search",
        new HashMap<String, String>(),
        new NStringEntity(queryJson,ContentType.APPLICATION_JSON));

Асинхронный звонок

Параметры асинхронного вызова и синхронного вызова одинаковы, но асинхронный вызов не имеет возвращаемого значения, но имеет объект обратного вызова отзывчика в параметре, который автоматически вызывается после завершения вызова. Этот объект обратного вызова - это интерфейс, который программист должен использовать самостоятельно.

Объявление метода для асинхронного вызова выглядит так:

public void performRequestAsync(String method, String endpoint, ResponseListener responseListener, Header... headers)

public void performRequestAsync(String method, String endpoint, Map<String, String> params,
                                    ResponseListener responseListener, Header... headers)

public void performRequestAsync(String method, String endpoint, Map<String, String> params,
                                    HttpEntity entity, ResponseListener responseListener, Header... headers) 

Например, я хочу запросить все документы, содержащие «программное обеспечение» в индексе author_test, в асинхронном вызове, код реализован следующим образом

String queryJson = "{\n" +
        "    \"query\": {\n" +
        "        \"match\": {\n" +
        "            \"des\": \"软件\"\n" +
        "        }\n" +
        "    }\n" +
        "}";


restClient.performRequestAsync("POST",
        "/author_test/_search",
        new HashMap<String, String>(),
        new NStringEntity(queryJson, ContentType.APPLICATION_JSON), new ResponseListener() {
            public void onSuccess(Response response) {
                try {
                    String responseData = readResposne(response);
                    System.out.println("******* search success ******");
                    System.out.println(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            public void onFailure(Exception exception) {
                exception.printStackTrace();
            }
        });

Инструкции по использованию Java High Level Rest API

Высокоуровневый API Elastic Search для Java более абстрактен, чем низкоуровневый API. Однако лично мне трудно им пользоваться. И расширенный API не поддерживает все функции Rest API. Официально поддерживается расширенными APIсписок функций.从这里看,如果你只是做查询,用高级 Api 接口还是够用的。

импортировать зависимости

В созданном ранее проекте Maven Java для использования низкоуровневого API Elastic Search мы должны сначала ввести зависимость низкоуровневого API. Следующее

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.1.1</version>
</dependency>

построить клиент

RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));

Подобно низкоуровневому интерфейсу, сначала создайте объект RestClientBuilder с помощью метода static method builder(HttpHost... hosts) объекта RestClient, а затем используйте его в качестве параметра конструктора объекта RestHighLevelClient для создания нового высокоуровневого объекта. клиентский объект. Где hosts — переменный параметр, используемый для указания IP-адреса, порта и протокола узлов кластера Elastic Cluster.

вызов метода

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

SearchRequest searchRequest = new SearchRequest("author_test");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("des", "软件"));
sourceBuilder.from(0);
sourceBuilder.size(5);
searchRequest.source(sourceBuilder);
SearchResponse response = restClient.search(searchRequest);

Другие вызовы интерфейса могут найти соответствующий API.Описание документаЧто нужно сделать

полный код

В последней главе публикуется полный код.

код инициализации

Эта часть кода отвечает за инициализацию индекса и индексацию документов для теста. Следует отметить, что ранее мы говорили, что Elastic Search — это система квази-реального времени, поэтому после индексации документов, если вы сделаете запрос сразу, вы не сможете запросить данные, и требуется небольшая задержка.

package com.x9710.es.test;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class IndexInitUtil {
    public RestClient initLowLevelClient() {
        // 通过 ip 、port 和协议建立 Elastic Search 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("10.110.2.53", 9200, "http")).build();


        try {
            initIndex(restClient);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return restClient;
    }

    public RestHighLevelClient initHighLevelClient() {
        // 通过 ip 、port 和协议建立 Elastic Search 客户端
        RestHighLevelClient highLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("10.110.2.53", 9200, "http"))
        );

        RestClient restClient = RestClient.builder(
                new HttpHost("10.110.2.53", 9200, "http")).build();

        try {
            initIndex(restClient);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                restClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        return highLevelClient;
    }

    private void initIndex(RestClient restClient) {

        String authIndexDefine = "{\n" +
                "\t\"settings\" : {\n" +
                "        \"index\" : {\n" +
                "            \"number_of_shards\" : 6,\n" +
                "            \"number_of_replicas\" : 0\n" +
                "        }\n" +
                "    },\n" +
                "    \"mappings\": {\n" +
                "        \"doc\": {\n" +
                "            \"properties\": {\n" +
                "            \t\"id\": {\"type\": \"text\"},\n" +
                "                \"name\": {\"type\": \"text\"},\n" +
                "                \"sex\": {\"type\": \"text\"},\n" +
                "                \"age\": {\"type\": \"integer\"},\n" +
                "                \"des\":{\n" +
                "                \t\"type\":\"text\",\n" +
                "                \t\"analyzer\": \"ik_max_word\",\n" +
                "\t\t\t\t\t\"search_analyzer\": \"ik_max_word\"\n" +
                "                }\n" +
                "            }\n" +
                "        }\n" +
                "    }\n" +
                "}";

        HttpEntity authorIndexEntity = new NStringEntity(authIndexDefine, ContentType.APPLICATION_JSON);
        //初始化要索引的 author 文档列表
        List<HttpEntity> authorDocs = new ArrayList<HttpEntity>();
        authorDocs.add(new NStringEntity(" {\n" +
                "\t\"id\":\"A1001\",\n" +
                "\t\"name\":\"任盈盈\",\n" +
                "\t\"age\":24,\n" +
                "\t\"sex\":\"女\",\n" +
                "\t\"des\":\"IT软件工程师,擅长Java和软件架构\"\n" +
                " }", ContentType.APPLICATION_JSON));
        authorDocs.add(new NStringEntity(" {\n" +
                "\t\"id\":\"A1002\",\n" +
                "\t\"name\":\"风清扬\",\n" +
                "\t\"age\":47,\n" +
                "\t\"sex\":\"男\",\n" +
                "\t\"des\":\"IT软件技术经理,擅长技术管理过程控制\"\n" +
                " }", ContentType.APPLICATION_JSON));


        try {
            //创建 author_test 索引
            restClient.performRequest("PUT", "/author_test", new HashMap<String, String>(), authorIndexEntity);

            //索引 author_index 文档
            for (int i = 0; i < authorDocs.size(); i++) {
                restClient.performRequest("POST", "/author_test/doc", new HashMap<String, String>(), authorDocs.get(i));
            }
            //注意索引文档完成后,做一个小的延迟,保证后续查询能查到数据
            Thread.currentThread().sleep(1000);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Тестовый образец низкоуровневого API

package com.x9710.es.test;

import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.codehaus.jettison.json.JSONObject;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseListener;
import org.elasticsearch.client.RestClient;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * Elastic Search 低级 Api 测试类
 *
 * @author 杨高超
 * @since 2018-01-11
 */
public class LowLeveApiTest {
    RestClient restClient = null;

    @Before
    public void before() {
        restClient = new IndexInitUtil().initLowLevelClient();
    }

    @Test
    public void testLocateAuthorIndex() {
        try {
            Response response = restClient.performRequest("GET", "/author_test");
            String responseData = readResposne(response);
            Assert.assertTrue(new JSONObject(responseData).has("author_test"));
            System.out.println(responseData);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.assertTrue(false);
        }
    }


    @Test
    public void testQueryAuthDoc() {
        try {
            String queryJson = "{\n" +
                    "    \"query\": {\n" +
                    "        \"match\": {\n" +
                    "            \"des\": \"Java\"\n" +
                    "        }\n" +
                    "    }\n" +
                    "}";
            Response response = restClient.performRequest("POST",
                    "/author_test/_search",
                    new HashMap<String, String>(),
                    new NStringEntity(queryJson, ContentType.APPLICATION_JSON));

            String responseData = readResposne(response);
            JSONObject responseJson = new JSONObject(responseData);
            Assert.assertTrue(responseJson.has("hits")
                    && responseJson.getJSONObject("hits").getInt("total") == 1);
            System.out.println(responseData);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.assertTrue(false);
        }
    }

    @Test
    public void testQueryAuthDocAsy() {
        try {
String queryJson = "{\n" +
        "    \"query\": {\n" +
        "        \"match\": {\n" +
        "            \"des\": \"软件\"\n" +
        "        }\n" +
        "    }\n" +
        "}";


restClient.performRequestAsync("POST",
        "/author_test/_search",
        new HashMap<String, String>(),
        new NStringEntity(queryJson, ContentType.APPLICATION_JSON), new ResponseListener() {
            public void onSuccess(Response response) {
                try {
                    String responseData = readResposne(response);
                    System.out.println("******* search success ******");
                    System.out.println(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            public void onFailure(Exception exception) {
                exception.printStackTrace();
            }
        });
        } catch (Exception e) {
            e.printStackTrace();
            Assert.assertTrue(false);
        }
    }


    @After
    public void after() {
        try {
            if (restClient != null) {
                restClient.performRequest("DELETE", "/author_test");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (restClient != null) {
                try {
                    restClient.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String readResposne(Response response) throws Exception {
        BufferedReader brd = new BufferedReader(new BufferedReader(new InputStreamReader(response.getEntity().getContent())));
        String line;
        StringBuilder respongseContext = new StringBuilder();

        while ((line = brd.readLine()) != null) {
            respongseContext.append(line).append("\n");
        }
        //rd.close();
        if (respongseContext.length() > 0) {
            respongseContext.deleteCharAt(respongseContext.length() - 1);
        }
        return respongseContext.toString();
    }
}

Продвинутый образец теста API

package com.x9710.es.test;

import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * Elastic Search 高级 Api 测试类
 *
 * @author 杨高超
 * @since 2018-01-11
 */
public class HighLevelApiTest {
    RestHighLevelClient restClient = null;

    @Before
    public void before() {
        restClient = new IndexInitUtil().initHighLevelClient();
    }


    @Test
    public void testQueryAuthDoc() {
        try {
SearchRequest searchRequest = new SearchRequest("author_test");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("des", "软件"));
sourceBuilder.from(0);
sourceBuilder.size(5);
searchRequest.source(sourceBuilder);
SearchResponse response = restClient.search(searchRequest);
            Assert.assertTrue(response.getHits().getTotalHits() == 2);
            System.out.println(response.toString());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.assertTrue(false);
        }
    }


    @After
    public void after() {
        try {
            if (restClient != null) {
                restClient.indices().deleteIndex(new DeleteIndexRequest("author_test"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (restClient != null) {
                try {
                    restClient.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

постскриптум

Как упоминалось ранее, сообщество также предоставило множество клиентских библиотек Elastic Search, но времени на их изучение нет. Если кто-то использовал его и нашел его полезным, я хотел бы порекомендовать его.

Первоначально опубликовано вкороткая книганачальство.