【腾讯云 Elasticsearch Service】高可用,可伸缩,云端全托管。集成X-Pack高级特性,适用日志分析/企业搜索/BI分析等场景
在今天的文章中,我们来介绍如何使用 Java 来访问 Elasticsearch。
首先,我们必须在我们的系统中安装 Elasticsearch。如果大家还没有安装好 Elasticsearch 的话,可以参考我之前的文章“如何在Linux,MacOS及Windows上进行安装Elasticsearch”。当然这个安装的步骤也需要我们安装相对应的 Java。
Maven 配置
针对 Java 的开发,我们必须在pom.xml中配置相应的Elasticsearch的信息。Mavev dependency 定义如下:
代码语言:javascript复制 <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.3.1</version> </dependency>
这也是目前截止最新的 Elasticsearch 的版本。您可以随时使用之前提供的链接查看 Maven Central 托管的最新版本。
完成数据库的查询
建立一个简单的 model
代码语言:javascript复制package com.javacodegeeks.example; public class Person { private String personId; private String name; private String number; public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getPersonId() { return personId; } public void setPersonId(String personId) { this.personId = personId; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return String.format("Person{personId='%s', name='%s', number='%s}", personId, name, number); }}
在这里,我们定义了一个简单的 Person Model。
定义连接参数
我们将使用默认连接参数与 Elasticsearch 建立连接。 默认情况下,ES 使用两个端口:9200 和 9201
代码语言:javascript复制 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 = "_doc";
这里我们定义了一个叫做 persondata 的 index,它的 type 是 _doc。在最新的版本中,每个 index 只支持一个 type。
如上面参数中所述,Elasticsearch 使用两个端口 9200 和 9201.第一个端口 9200 由 Elasticsearch 查询服务器使用,我们可以使用它通过 RESTful API 直接查询数据库。 第二个端口 9201 由 REST 服务器使用,外部客户端可以使用该端口连接并执行操作。
建立一个连接
我们将创建一个与 Elasticsearch 数据库建立连接的方法。 在建立与数据库的连接时,我们必须提供两个端口,因为只有这样,我们的应用程序才能连接到 Elasticsearch 服务器,我们将能够执行数据库操作。 以下是建立连接的代码。
代码语言:javascript复制 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; }
在这里,我们建立一个 RestHighLevelClient 的实例。具体的参数,可以参官方文档 Java High Level REST Client 。
请注意,我们在此处实现了 Singleton Design 模式,因此不会为 ES 创建多个连接,从而节省大量内存。
由于存在 RestHighLevelClient,与 Elasticsearch 的连接是线程安全的。 初始化此连接的最佳时间是应用程序请求或向客户端发出第一个请求时。 初始化此连接客户端后,可以使用它来执行任何支持的API。
关掉一个连接
就像在早期版本的 Elasticsearch 中一样,我们使用 TransportClient,一旦完成查询就关闭它,一旦数据库交互完成 RestHighLevelClient,也需要关闭连接。 以下是如何做到这一点:
代码语言:javascript复制 private static synchronized void closeConnection() throws IOException { restHighLevelClient.close(); restHighLevelClient = null; }
我们还为 RestHighLevelClient 对象分配了 null,以便 Singleton 模式可以保持一致。
插入一个文档
我们可以通过将键和值转换为 HashMap 将数据插入数据库。 ES 数据库仅接受 HashMap 形式的值。 让我们看看如何实现这一目标的代码片段:
代码语言:javascript复制 private static Person insertPerson(Person person) { person.setPersonId(UUID.randomUUID().toString()); Map<String, Object> dataMap = new HashMap<String, Object>(); dataMap.put("name", person.getName()); dataMap.put("number", person.getNumber()); IndexRequest indexRequest = new IndexRequest(INDEX) .id(person.getPersonId()).source(dataMap); try { IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); } catch(ElasticsearchException e) { e.getDetailedMessage(); } catch (java.io.IOException ex){ ex.getLocalizedMessage(); } /* // The following is another way to do it // More information https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-high-document-index.html String id = UUID.randomUUID().toString(); person.setPersonId(id); IndexRequest request = new IndexRequest(INDEX); request.id(id); String jsonString = "{" ""name":" """ person.getName() """ "}"; System.out.println("jsonString: " jsonString); request.source(jsonString, XContentType.JSON); try { IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT); } catch(ElasticsearchException e) { e.getDetailedMessage(); } catch (java.io.IOException ex){ ex.getLocalizedMessage(); } */ return person; }
就像上面代码中注释的那样。注释的代码的那一部分是另外一种方法。大家可以参照链接获得更多的信息。
上面,我们使用 Java 的 UUID 类来创建对象的唯一标识符。 这样,我们就可以控制对象标识符的制作方式。我们其实也可以固定一个id去写。如果是这样的话,运行多次,只会更新之前的数据,并且 version 会自动每次运行后增加1。
请求上面存入的文档
完成将数据插入数据库后,我们可以通过向 Elasticsearch 数据库服务器发出 GET 请求来确认操作。 让我们看看如何完成此操作的代码片段:
代码语言:javascript复制 private static Person getPersonById(String id){ GetRequest getPersonRequest = new GetRequest(INDEX, id); GetResponse getResponse = null; try { getResponse = restHighLevelClient.get(getPersonRequest, RequestOptions.DEFAULT); } catch (java.io.IOException e){ e.getLocalizedMessage(); } return getResponse != null ? objectMapper.convertValue(getResponse.getSourceAsMap(), Person.class) : null; }
在这里,我们根据上面返回来得 id 来进行 query,并返回数据。
在这个查询中,我们只提供了可以识别它的对象的主要信息,即索引,和它的唯一标识符id。 此外,我们得到的实际上是一个值的映射。
更新文档
我们可以通过首先使用其索引,类型和唯一标识符来标识资源,从而轻松地向 Elasticsearch 发出更新请求。 然后我们可以使用新的 HashMap 对象来更新 Object 中的任意数量的值。 这是一个示例代码段:
代码语言:javascript复制 private static Person updatePersonById(String id, Person person){ UpdateRequest updateRequest = new UpdateRequest(INDEX, id) .fetchSource(true); // Fetch Object after its update try { String personJson = objectMapper.writeValueAsString(person); updateRequest.doc(personJson, XContentType.JSON); UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT); 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; }
删除文档
最后,我们可以通过简单地使用其索引,类型和唯一标识符来标识资源来删除数据。 让我们看一下如何完成此操作的代码片段
代码语言:javascript复制 private static void deletePersonById(String id) { DeleteRequest deleteRequest = new DeleteRequest(INDEX, TYPE, id); try { DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT); } catch (java.io.IOException e) { e.getLocalizedMessage(); } }
我们根据传入的 id 来删除相应的文档。当然我们也可以做查询删除。
运行我们的应用
让我们通过执行上面提到的所有操作来尝试我们的应用程序。 由于这是一个普通的 Java 应用程序,我们将调用这些方法中的每一个并打印操作结果:
代码语言:javascript复制 public static void main(String[] args) throws IOException { makeConnection(); Person person = new Person(); person.setName("张三"); System.out.println("Inserting a new Person with name " person.getName()); person.setNumber("111111"); person = insertPerson(person); System.out.println("Person inserted --> " person); person = new Person(); person.setName("姚明"); System.out.println("Inserting a new Person with name " person.getName()); person.setNumber("222222"); person = insertPerson(person); System.out.println("Person inserted --> " person); person.setName("李四"); System.out.println("Changing name to " person.getName()); updatePersonById(person.getPersonId(), person); System.out.println("Person updated --> " person); System.out.println("Searching for all documents"); SearchResponse response = searchAll(); System.out.println(response); System.out.println("Searching for a term"); response = searchTerm(); System.out.println(response); System.out.println("Match a query"); response = matchQuery(); System.out.println(response); System.out.println("Getting 李四"); Person personFromDB = getPersonById(person.getPersonId()); System.out.println("Person from DB --> " personFromDB); System.out.println("Deleting " person.getName()); deletePersonById(personFromDB.getPersonId()); System.out.println("Person " person.getName() " deleted!"); closeConnection(); }
运行的结果如下:
代码语言:javascript复制Inserting a new Person with name 张三Person inserted --> Person{personId='33f4162e-0a68-4e66-8717-851516272185', name='张三', number='111111}Inserting a new Person with name 姚明Person inserted --> Person{personId='9b477529-6e79-42e8-a50a-21b2d8bc4c13', name='姚明', number='222222}Changing name to 李四Person updated --> Person{personId='9b477529-6e79-42e8-a50a-21b2d8bc4c13', name='李四', number='222222}Searching for all documents{"took":0,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":4,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"persondata","_type":"_doc","_id":"52425a44-dc06-49ca-b3df-26a8b341391c","_score":1.0,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"c76b8670-ed00-4212-b47b-46bc85d588b6","_score":1.0,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"b8bf0466-0ea5-43e0-8188-c0712812fb9a","_score":1.0,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"468dabe4-8f50-4667-a165-9ce6e015cb76","_score":1.0,"_source":{"number":"222222","name":"李四","personId":"468dabe4-8f50-4667-a165-9ce6e015cb76"}}]}}Searching for a term{"took":0,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":3,"relation":"eq"},"max_score":0.9444616,"hits":[{"_index":"persondata","_type":"_doc","_id":"52425a44-dc06-49ca-b3df-26a8b341391c","_score":0.9444616,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"c76b8670-ed00-4212-b47b-46bc85d588b6","_score":0.9444616,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"b8bf0466-0ea5-43e0-8188-c0712812fb9a","_score":0.9444616,"_source":{"number":"111111","name":"张三"}}]}}Match a query{"took":0,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":3,"relation":"eq"},"max_score":1.8889232,"hits":[{"_index":"persondata","_type":"_doc","_id":"52425a44-dc06-49ca-b3df-26a8b341391c","_score":1.8889232,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"c76b8670-ed00-4212-b47b-46bc85d588b6","_score":1.8889232,"_source":{"number":"111111","name":"张三"}},{"_index":"persondata","_type":"_doc","_id":"b8bf0466-0ea5-43e0-8188-c0712812fb9a","_score":1.8889232,"_source":{"number":"111111","name":"张三"}}]}}Getting 李四Person from DB --> Person{personId='9b477529-6e79-42e8-a50a-21b2d8bc4c13', name='李四', number='222222}
整个项目的源码可以在地址找到:https://github.com/liu-xiao-guo/elastic-java
更多资料:
【1】使用RestHighLevelClient时单个索引速度很慢
最新活动
包含文章发布时段最新活动,前往ES产品介绍页,可查找ES当前活动统一入口
Elasticsearch Service自建迁移特惠政策>>
Elasticsearch Service 新用户特惠狂欢,最低4折首购优惠 >>
Elasticsearch Service 企业首购特惠,助力企业复工复产>>
关注“腾讯云大数据”公众号,技术交流、最新活动、服务专享一站Get~