前文咱们提到过Elastic Search 操做索引的 Rest Api。实际上 Elastic Search 的 Rest Api 提供了全部的操做接口。在编程语言中能够直接这么使用 Rest Api 能够调用 Elastic Search 的全部功能,可是很是的不方便和直观,因此Elastic Search 官方也为不少语言提供了访问的 Api 接口。官方提供的编程语言接口包括:html
同时编程社区也提供了大量的编程语言的 Api。目前主要有java
平时咱们都是用 Java 进行开发。因此这里我会谈谈 Elastic Search 的 Java Api 的使用方式git
为了说明 Java Api 的功能,咱们准备了一个场景。在这里咱们假定有一批做者,每一个做者都有标识、姓名、性别、年龄,描述着几个字段。咱们须要经过姓名、年龄、描述中的关键词来查询做者,程序员
在这里,程序主要经过 JUnit 测试用例的方式来运行,因此首先引入了 JUnit 的依赖github
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
复制代码
Elastic Search 提供了官方的 Java Api。这里包括两类,一类是 Low Level Rest Api(低级 Rest Api)和 High Leve Rest Api(高级 Rest Api)。apache
所谓低级 Api 并非功能比较弱,而是指 Api 离底层实现比较近。官方提供的低级 Api 是对原始的 Rest Api 的第一层封装。只是把 Http 调用的细节封装起来。程序仍是要本身组装查询的条件字符串、解析返回的结果 json 字符串等。同时也要处理 http 协议的 各类方法、协议头等内容。编程
高级 api 是在低级 api 上的进一步封装,不用在在乎接口的方法,协议头,也不用人工组合调用的参数字符串,同时对返回的 json 字符串有必定的解析。使用上更方便一些。可是高级 api 并无实现全部低级 api 实现的功能。因此若是遇到这种状况,还须要利用低级 api 来实现本身功能。json
第三方 Java 客户端是有社区本身开发的 Elastic Search 客户端。官方提到了两个开源在 GitHub 上的项目 Flummi、Jestapi
低级 Api 的优点在于依赖的其余库很是少,并且功能完备。缺点在于封装不够高级,因此使用起来仍是很是的繁琐。咱们这里先来看看低级的 api 是怎么使用的。bash
在前面创建的 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();
复制代码
咱们经过 RestClient 对象的静态方法 builder(HttpHost... hosts) 和 builder()创建一个 Elastic Search 的 Rest 客户端。其中 hosts 是一个可变参数,用来指定 Elastic Cluster 集群的节点的 ip、端口、协议。
创建了客户端之后,经过两类方法来调用 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
复制代码
这是三个重载的方法,参数 method 表明的是 Rest Api 的方法,例如 PUT、GET、POST、DELETE等;参数 endpoint 表明的是 Rest Api 参数的地址,从 Rest Api 的 URL 的 ip:port 字段以后开始;params 是经过 url 参数形式传递的参数;entity 是经过 http body 传递的参数;headers 是一个可变参数,能够传入对应的 http 头信息。
例如,我要查看一个索引 author_test 的信息,咱们能够用以下的代码来获取
Response response = restClient.performRequest("GET", "/author_test");
复制代码
再好比,咱们要查看一个索引 author_test 中 des 字段中包含软件的文档信息,咱们能够用以下代码来获取:
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));
复制代码
异步调用和同步调用的参数是同样的,可是异步调用没有返回值,而是在参数中有一个 ResponseListener 回调对象,在调用完成后自动调用。这个回调对象是一个接口,须要程序员本身来实现。
异步调用的方法声明以下所示:
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 索引中 des 中包含 “软件” 的全部文档,则代码实现以下
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();
}
});
复制代码
Elastic Search 的 Java 高级 Api 相对低级 Api 来讲,抽象程度更高一些。不过我我的以为仍是挺难用的。并且高级 Api 并不支持全部的 Rest Api 的功能。官方有高级 Api 支持的功能列表。从这里看,若是你只是作查询,用高级 Api 接口仍是够用的。
在前面创建的 Maven Java 工程中,要使用 Elastic Search 的低级 Api,首先要引入 低级 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")));
复制代码
和低级接口相似,先经过 RestClient 对象的静态方法 builder(HttpHost... hosts)方法创建一个 RestClientBuilder 对象,而后做为 RestHighLevelClient 对象构造函数的参数,来建立一个新的高级客户端对象。其中 hosts 是一个可变参数,用来指定 Elastic Cluster 集群的节点的 ip、端口、协议。
这里用高级接口来实现低级接口中第一个查询的功能。代码以下
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();
}
}
}
复制代码
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();
}
}
复制代码
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 的客户端库,可是没有时间去研究。若是有人用过以为好用,但愿推荐。
原文发表在简书上。