在今天的文章中,我們來介紹如何使用Java來訪問Elasticsearch。
首先,我們必須在我們的系統中安裝Elasticsearch。
Maven 配置
針對Java的開發,我們必須在pom.xml中配置相應的Elasticsearch的信息。Mavev dependency定義如下:
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.3.1</version>
</dependency>
這也是目前截止最新的Elasticsearch的版本。您可以隨時使用之前提供的鏈接查看Maven Central托管的最新版本。
完成數據庫的查詢
建立一個簡單的model
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
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服務器,我們將能夠執行數據庫操作。 以下是建立連接的代碼。
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 (https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-high.html)。
請注意,我們在此處實現了Singleton Design模式,因此不會為ES創建多個連接,從而節省大量內存。
由於存在RestHighLevelClient,與Elasticsearch的連接是線程安全的。 初始化此連接的最佳時間是應用程序請求或向客戶端發出第一個請求時。 初始化此連接客戶端后,可以使用它來執行任何支持的API。
關掉一個連接
就像在早期版本的Elasticsearch中一樣,我們使用TransportClient,一旦完成查詢就關閉它,一旦數據庫交互完成RestHighLevelClient,也需要關閉連接。 以下是如何做到這一點:
private static synchronized void closeConnection() throws IOException {
restHighLevelClient.close();
restHighLevelClient = null;
}
我們還為RestHighLevelClient對象分配了null,以便Singleton模式可以保持一致。
插入一個文檔
我們可以通過將鍵和值轉換為HashMap將數據插入數據庫。 ES數據庫僅接受HashMap形式的值。 讓我們看看如何實現這一目標的代碼片段:
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;
}
就像上面代碼中注釋的那樣。注釋的代碼的那一部分是另外一種方法。大家可以參照鏈接(https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-high-document-index.html)獲得更多的信息。
上面,我們使用Java的UUID類來創建對象的唯一標識符。 這樣,我們就可以控制對象標識符的制作方式。我們其實也可以固定一個id去寫。如果是這樣的話,運行多次,只會更新之前的數據,並且version會自動每次運行后增加1。
請求上面存入的文檔
完成將數據插入數據庫后,我們可以通過向Elasticsearch數據庫服務器發出GET請求來確認操作。 讓我們看看如何完成此操作的代碼片段:
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中的任意數量的值。 這是一個示例代碼段:
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;
}
刪除文檔
最后,我們可以通過簡單地使用其索引,類型和唯一標識符來標識資源來刪除數據。 讓我們看一下如何完成此操作的代碼片段
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應用程序,我們將調用這些方法中的每一個並打印操作結果:
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();
}
運行的結果如下:
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時單個索引速度很慢(https://discuss.elastic.co/t/resthighlevelclient/170293)