Elastic Stack 筆記(八)Elasticsearch5.6 Java API


博客地址:http://www.moonxy.com

一、前言

Elasticsearch 底層依賴於 Lucene 庫,而 Lucene 庫完全是 Java 編寫的,前面的文章都是發送的 RESTful API 請求,其實這些請求最后還是通過 Java 執行的。RESTful API 能做的 Java API 都能做,Java API 比 RESTful API 功能更強大。

1.1 Elasticsearch API 的簡單使用方式

1)非客戶端方式:通過 HTTP 方式的 JSON 格式進行調用,關於 HTTP 的相關參數可以在 elasticsearch.yml 中設置(出於安全考慮,也可禁用 HTTP 接口,只需在配置文件中將 http.enabled 設置為 false 即可)。

2)客戶端方式:對於 Java 來說,Elasticsearch 內置了傳輸客戶端 TransportClient,它是一種輕量型的傳輸客戶端,可被用來向遠程集群發送請求。它不加入集群本身,而是把請求轉發到集群中的節點上。客戶端都使用 Elasticsearch 的傳輸協議,通過 9300 端口與 Java 客戶端進行通信,集群中的各個節點也是通過 9300 端口進行通信。

注意:Elasticsearch 的 9200 端口是 HTTP 端口,9300 端口是 Transport 端口。

Elastic Stack 產品官方參考文檔:https://www.elastic.co/guide/index.html

Elasticsearch Clients 客戶端參考文檔:https://www.elastic.co/guide/en/elasticsearch/client/index.html,可以看出,Elasticsearch 不僅提供了 Java 客戶端方式,還提供了其他常見語言客戶端的方式,如:Python,Perl,Ruby,JavaScript,Groovy,PHP 等。

Java 客戶端目前主要提供兩種方式,分別是 Java API 和 Java REST Client:

Java APIhttps://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html,其使用的核心傳輸對象是 TransportClient。但是 Elastic 官方已經計划在 Elasticsearch 7.0 中廢棄 TransportClient,並在 8.0 中完全移除它,並由 Java High Level REST Client 代替。官網聲明如下:https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/client.html

Java REST Clienthttps://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html,Java REST Client 又分為兩種,Java Low Level REST Client 和 Java High Level REST Client目前官方推薦使用 Java High Level REST Client

二、搜索過程詳解

此處我們依然使用上面提到的 Java API,這也是目前使用最廣泛的 Java 客戶端。

2.1 添加 Java 客戶端 Maven 依賴

根據自己的 Elasticsearch 版本,選擇 TransportClient 的版本,此處我們使用的是 5.6.0。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com</groupId>
  <artifactId>esjavaapi</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>esjavaapi</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>transport</artifactId>
      <version>5.6.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.8.2</version>
    </dependency>
  </dependencies>
</project>

並添加相應的 log4j 的依賴,用於日志輸出。

2.2 Java 客戶端實現代碼

創建客戶端連接Elasticsearch集群,如下:

package tup.es.client;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

public class EsUtils {
    
    public static String CLUSTER_NAME = "Banon";//Elasticsearch集群名稱
    public static String HOST_IP = "192.168.56.110";//Elasticsearch集群節點
    public static int TCP_PORT = 9300;//Elasticsearch節點TCP通訊端口
    private volatile static TransportClient client;//客戶端對象,用於連接到Elasticsearch集群
    
    /**
     * Elasticsearch Java API 的相關操作都是通過TransportClient對象與Elasticsearch集群進行交互的。
     * 為了避免每次請求都創建一個新的TransportClient對象,可以封裝一個雙重加鎖單例模式返回TransportClient對象。
     * 即同時使用volatile和synchronized。volatile是Java提供的一種輕量級的同步機制,synchronized通常稱為重量級同步鎖。
     * @author moonxy
     */
    public static TransportClient getSingleTransportClient() {
        Settings settings  = Settings.builder().put("cluster.name", CLUSTER_NAME).build();
        try {
            if(client == null) {
                synchronized(TransportClient.class) {
                    client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(HOST_IP), TCP_PORT));
                }
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;
    }
    
    //測試入口
    public static void main(String[] args) throws UnknownHostException {
        TransportClient client = EsUtils.getSingleTransportClient();
        GetResponse getResponse = client.prepareGet("books", "IT", "1").get();
        System.out.println(getResponse.getSourceAsString());
    }
}

檢索文檔並返回:

package tup.es.search;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;

import tup.es.client.EsUtils;

public class EsMatchQueryTest {
    
    /**
     * 詳細檢索過程
     * @author moonxy
     */
    public void esMatchQuery() {
        //構造查詢對象的工廠類 QueryBuilders,matchQuery全文查詢,Operator.AND指定分詞項之間采用AND方式連接,默認是OR
        MatchQueryBuilder matchQuery = QueryBuilders
                .matchQuery("title", "python")
                .operator(Operator.AND);
        
        //構造HighlightBuilder對象,設置需要高亮的字段並自定義高亮標簽
        HighlightBuilder highlighter = new HighlightBuilder()
                .field("title")
                .preTags("<span stype=\"color:red\">")
                .postTags("</span>");
        
        //獲取傳輸客戶端TransportClient對象,指定要搜索的索引名,設置查詢字段和高亮,並設置一次查詢返回文檔的數量
        SearchResponse response = EsUtils
                .getSingleTransportClient()
                .prepareSearch("books")
                .setQuery(matchQuery)
                .highlighter(highlighter)
                .setSize(100)
                .get();
        
        //通過上面獲得的SearchResponse對象,取得返回結果
        SearchHits hits = response.getHits();
        //搜索到的結果數
        System.out.println("共搜索到:" + hits.getTotalHits());
        
        //遍歷SearchHits數組
        for (SearchHit hit : hits) {
            System.out.println("Source:" + hit.getSourceAsString());//返回String類型的文檔內容
            System.out.println("Source As Map:" + hit.getSource());//返回Map格式的文檔內容
            System.out.println("Index:" + hit.getIndex());//返回文檔所在的索引
            System.out.println("Type:" + hit.getType());//返回文檔所在的類型
            System.out.println("ID:" + hit.getId());//返回文檔的id
            System.out.println("Source:" + hit.getSource().get("price"));//從返回的map中通過key取到value
            System.out.println("Score:" + hit.getScore());//返回文檔的評分
            //getHighlightFields()會返回文檔中所有高亮字段的內容,再通過get()方法獲取某一個字段的高亮片段,最后調用getFragments()方法,返回Text類型的數組
            Text[] texts = hit.getHighlightFields().get("title").getFragments();
            if(texts != null) {
                //遍歷高亮結果數組,取出高亮內容
                for (Text text : texts) {
                    System.out.println(text.string());
                }
            }
        }
    }
    
    //測試入口
    public static void main(String[] args) {
        new EsMatchQueryTest().esMatchQuery();
    }
}

Console 控制台輸出如下:

共搜索到:2
Source:{"id":"4","title":"Python基礎教程","language":"python","author":"Helant","price":54.50,"publish_time":"2014-03-01","description":"經典的Python入門教程,層次鮮明,結構嚴謹,內容翔實"}
Source As Map:{author=Helant, price=54.5, publish_time=2014-03-01, description=經典的Python入門教程,層次鮮明,結構嚴謹,內容翔實, language=python, id=4, title=Python基礎教程}
Index:books
Type:IT
ID:4
Source:54.5
Score:0.9130229
<span stype="color:red">Python</span>基礎教程
Source:{"id":"3","title":"Python科學計算","language":"python","author":"張若愚","price":81.40,"publish_time":"2016-05-01","description":"零基礎學python,光盤中作者獨家整合開發winPython運行環境,涵蓋了Python各個擴展庫"}
Source As Map:{author=張若愚, price=81.4, publish_time=2016-05-01, description=零基礎學python,光盤中作者獨家整合開發winPython運行環境,涵蓋了Python各個擴展庫, language=python, id=3, title=Python科學計算}
Index:books
Type:IT
ID:3
Source:81.4
Score:0.6931472
<span stype="color:red">Python</span>科學計算

代碼截圖如下:

使用對應的 RESTful 請求:

GET books/_search
{
  "query": {
    "match": {
      "title":  "python"
    }
  },
  "highlight": {
      "fields": {
          "title": {
              "pre_tags": ["<span stype=\"color:red\">"],
              "post_tags": ["</strong>"]
          }
      }
  }
}

返回的響應結果:

{
  "took": 15,
  "timed_out": false,
  "_shards": {
    "total": 3,
    "successful": 3,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.9130229,
    "hits": [
      {
        "_index": "books",
        "_type": "IT",
        "_id": "4",
        "_score": 0.9130229,
        "_source": {
          "id": "4",
          "title": "Python基礎教程",
          "language": "python",
          "author": "Helant",
          "price": 54.5,
          "publish_time": "2014-03-01",
          "description": "經典的Python入門教程,層次鮮明,結構嚴謹,內容翔實"
        },
        "highlight": {
          "title": [
            """<span stype="color:red">Python</strong>基礎教程"""
          ]
        }
      },
      {
        "_index": "books",
        "_type": "IT",
        "_id": "3",
        "_score": 0.6931472,
        "_source": {
          "id": "3",
          "title": "Python科學計算",
          "language": "python",
          "author": "張若愚",
          "price": 81.4,
          "publish_time": "2016-05-01",
          "description": "零基礎學python,光盤中作者獨家整合開發winPython運行環境,涵蓋了Python各個擴展庫"
        },
        "highlight": {
          "title": [
            """<span stype="color:red">Python</strong>科學計算"""
          ]
        }
      }
    ]
  }
}

可以看到使用 RESTful API 和 Java API 返回的結果一致。

三、Java API 詳解

上面通過一個例子,演示了如何使用 Java API 客戶端連接 Elastisearch 集群和檢索數據,下面展示具體的 Java API。

3.1 傳輸客戶端

傳輸客戶端官方文檔:TransportClient

// on startup

TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
        .addTransportAddress(new TransportAddress(InetAddress.getByName("host1"), 9300))
        .addTransportAddress(new TransportAddress(InetAddress.getByName("host2"), 9300));

// on shutdown

client.close();

client 對象知道一個或多個傳輸地址,通過輪詢調度的方式和服務器交互。

3.2 索引管理

索引管理官方文檔:Indices Administration

其核心是通過 IndicesAdminClient 對象發送各種索引操作。

3.3 文檔管理

文檔管理官方文檔:Document APIs

主要包括單文檔操作 Single document APIs 和多文檔操作 Multi-document APIs。

Single document APIs

Multi-document APIs

創建文檔 Index API

package tup.es.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

public class TestClient {
    
    public static String CLUSTER_NAME = "Banon";
    public static String HOST_IP = "192.168.56.110";
    public static int TCP_PORT = 9300;
    
    public static void main(String[] args) throws IOException {
        Settings settings  = Settings.builder().put("cluster.name", CLUSTER_NAME).build();
        TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(HOST_IP), TCP_PORT));
//        GetResponse getResponse = client.prepareGet("books", "IT", "1").get();
//        System.out.println(getResponse.getSourceAsString());
        
        XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
                .field("id", "11580657")
                .field("title", "使用Linux Shell編程")
                .field("language", "Shell")
                .field("author", "石慶東")
                .field("price", 48.30)
                .field("publish_time", "2014-11-01")
                .field("description", "《信息科學與技術叢書:實用LinuxShell編程》系統地介紹了在Linux系統中廣泛使用的Bash腳本語言。全書內容的安排由淺入深,體系合理。先講解腳本的概念和學習環境的搭建,接下來介紹Linux的常用命令,然后根據概念之間的依賴關系,講解Bash環境設置、變量與數組、條件流程控制、循環、函數、正則表達式、文本處理、進程與作業、高級話題等。本書是一本不可多得的shell編程原創讀物。")
                .endObject();
        System.out.println(builder.string());
        IndexResponse response = client.prepareIndex("books", "IT", "6").setSource(builder).get();
        System.out.println(response.status());
    }

}

控制台返回結果如下:

{"id":"11580657","title":"使用Linux Shell編程","language":"Shell","author":"石慶東","price":48.3,"publish_time":"2014-11-01","description":"《信息科學與技術叢書:實用LinuxShell編程》系統地介紹了在Linux系統中廣泛使用的Bash腳本語言。全書內容的安排由淺入深,體系合理。先講解腳本的概念和學習環境的搭建,接下來介紹Linux的常用命令,然后根據概念之間的依賴關系,講解Bash環境設置、變量與數組、條件流程控制、循環、函數、正則表達式、文本處理、進程與作業、高級話題等。本書是一本不可多得的shell編程原創讀物。"}
CREATED

jsonbuilder 是高度優化的 JSON 生成器。此處使用 Elasticsearch 內置的幫助類 XContentFactory 的 jsonBuilder() 方法,構造出 XContentBuilder 對象,XContentBuilder 對象可以直接寫入 Elasticsearch 中。如果需要查看生成的 JSON 內容,可以調用 string() 方法。

3.4 查詢檢索

查詢檢索官方文檔:Query DSL

主要包括如下類別,這些與 RESTful 中的請求相互對應。

以下分別為全部匹配查詢,全文查詢,詞項查詢,符合查詢,嵌套(連接)查詢,地理位置查詢,特殊查詢,跨度查詢。

上面的 Full text queries 全文查詢中包括 multi_match query,表示檢索多個字段,如下:

QueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(
        "java思想",                              
        "title", "description");

"java思想" 表示 text,"title" 和 "description" 表示 fields。

3.5 聚合分析

聚合分析官方文檔:Aggregations

主要包括如下類別,主要仍然為指標聚合和桶聚合。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM