Elasticsearch(一)入門

第一章、Elasticsearch概述

1.1 開篇

結構化數據
img

結構化數據
img
半結構化數據

img

1.2 技術選型

Elasticsearch 是什麼

The Elastic Stack, 包括 Elasticsearch、 Kibana、 Beats 和 Logstash(也稱爲 ELK Stack)。能夠安全可靠地獲取任何來源、任何格式的數據,然後實時地對數據進行搜索、分析和可視化。

Elaticsearch,簡稱爲 ES, ES 是一個開源的高擴展的分佈式全文搜索引擎, 是整個 ElasticStack 技術棧的核心。

它可以近乎實時的存儲、檢索數據;本身擴展性很好,可以擴展到上百臺服務器,處理 PB 級別的數據。

elastic
英 [ɪˈlæstɪk] 美 [ɪˈlæstɪk]
n. 橡皮圈(或帶);鬆緊帶
adj. 橡皮圈(或帶)的;有彈性的;有彈力的;靈活的;可改變的;可伸縮的

全文搜索引擎

Google,百度類的網站搜索,它們都是根據網頁中的關鍵字生成索引,我們在搜索的時候輸入關鍵字,它們會將該關鍵字即索引匹配到的所有網頁返回;還有常見的項目中應用日誌的搜索等等。對於這些非結構化的數據文本,關係型數據庫搜索不是能很好的支持。

一般傳統數據庫,全文檢索都實現的很雞肋,因爲一般也沒人用數據庫存文本字段。進行全文檢索需要掃描整個表,如果數據量大的話即使對 SQL 的語法優化,也收效甚微。建立了索引,但是維護起來也很麻煩,對於 insert 和 update 操作都會重新構建索引。

基於以上原因可以分析得出,在一些生產環境中,使用常規的搜索方式,性能是非常差的:

  • 搜索的數據對象是大量的非結構化的文本數據。
  • 文件記錄量達到數十萬或數百萬個甚至更多。
  • 支持大量基於交互式文本的查詢。
  • 需求非常靈活的全文搜索查詢。
  • 對高度相關的搜索結果的有特殊需求,但是沒有可用的關係數據庫可以滿足。
  • 對不同記錄類型、非文本數據操作或安全事務處理的需求相對較少的情況。爲了解決結構化數據搜索和非結構化數據搜索性能問題,我們就需要專業,健壯,強大的全文搜索引擎 。

裏說到的全文搜索引擎指的是目前廣泛應用的主流搜索引擎。它的工作原理是計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立的索引進行查找,並將查找的結果反饋給用戶的檢索方式。這個過程類似於通過字典中的檢索字表查字的過程。

Elasticsearch 應用案例

  • GitHub: 2013 年初,拋棄了 Solr,採取 Elasticsearch 來做 PB 級的搜索。 “GitHub 使用Elasticsearch 搜索 20TB 的數據,包括 13 億文件和 1300 億行代碼”。
  • 維基百科:啓動以 Elasticsearch 爲基礎的核心搜索架構
  • 百度:目前廣泛使用 Elasticsearch 作爲文本數據分析,採集百度所有服務器上的各類指標數據及用戶自定義數據,通過對各種數據進行多維分析展示,輔助定位分析實例異常或業務層面異常。目前覆蓋百度內部 20 多個業務線(包括雲分析、網盟、預測、文庫、直達號、錢包、 風控等),單集羣最大 100 臺機器, 200 個 ES 節點,每天導入 30TB+數據。
  • 新浪:使用 Elas ticsearch 分析處理 32 億條實時日誌。
  • 阿里:使用 Elasticsearch 構建日誌採集和分析體系。
  • Stack Overflow:解決 Bug 問題的網站,全英文,編程人員交流的網站。

第二章、Elasticsearch入門

2.1 環境準備

官方網址:https://www.elastic.co/cn/
官方文檔:https://www.elastic.co/guide/index.html
Elasticsearch 7.8.0下載頁面:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-8-0

Windows 版的 Elasticsearch 壓縮包,解壓即安裝完畢,解壓後的 Elasticsearch 的目錄結構如下 :

目錄 含義
bin 可執行腳本目錄
config 配置目錄
jdk 內置 JDK 目錄
lib 類庫
logs 日誌目錄
modules 模塊目錄
plugins 插件目錄

解壓後,進入 bin 文件目錄,點擊 elasticsearch.bat 文件啓動 ES 服務 。
注意: 9300 端口爲 Elasticsearch 集羣間組件的通信端口, 9200 端口爲瀏覽器訪問的 http協議 RESTful 端口。
打開瀏覽器,輸入地址: http://localhost:9200,測試返回結果,返回結果如下:

{
  "name" : "DESKTOP-LNJQ0VF",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "nCZqBhfdT1-pw8Yas4QU9w",
  "version" : {
    "number" : "7.8.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
    "build_date" : "2020-06-14T19:35:50.234439Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

2.2 RESTful & JSON

REST 指的是一組架構約束條件和原則。滿足這些約束條件和原則的應用程序或設計就是 RESTful。 Web 應用程序最重要的 REST 原則是,客戶端和服務器之間的交互在請求之間是無狀態的。從客戶端到服務器的每個請求都必須包含理解請求所必需的信息。如果服務器在請求之間的任何時間點重啓,客戶端不會得到通知。此外,無狀態請求可以由任何可用服務器回答,這十分適合雲計算之類的環境。客戶端可以緩存數據以改進性能。

在服務器端,應用程序狀態和功能可以分爲各種資源。資源是一個有趣的概念實體,它向客戶端公開。資源的例子有:應用程序對象、數據庫記錄、算法等等。每個資源都使用 URI(Universal Resource Identifier) 得到一個唯一的地址。所有資源都共享統一的接口,以便在客戶端和服務器之間傳輸狀態。使用的是標準的 HTTP 方法,比如 GET、 PUT、 POST 和DELETE。

在 REST 樣式的 Web 服務中,每個資源都有一個地址。資源本身都是方法調用的目標,方法列表對所有資源都是一樣的。這些方法都是標準方法,包括 HTTP GET、 POST、PUT、 DELETE,還可能包括 HEAD 和 OPTIONS。簡單的理解就是,如果想要訪問互聯網上的資源,就必須向資源所在的服務器發出請求,請求體中必須包含資源的網絡路徑, 以及對資源進行的操作(增刪改查)

REST 樣式的 Web 服務若有返回結果,大多數以JSON字符串形式返回。

2.3 Postman客戶端工具

如果直接通過瀏覽器向 Elasticsearch 服務器發請求,那麼需要在發送的請求中包含
HTTP 標準的方法,而 HTTP 的大部分特性且僅支持 GET 和 POST 方法。所以爲了能方便地進行客戶端的訪問,可以使用 Postman 軟件Postman 是一款強大的網頁調試工具,提供功能強大的 Web API 和 HTTP 請求調試。

軟件功能強大,界面簡潔明晰、操作方便快捷,設計得很人性化。 Postman 中文版能夠發送任何類型的 HTTP 請求 (GET, HEAD, POST, PUT…),不僅能夠表單提交,且可以附帶任意類型請求體。

Postman下載頁面 :https://www.postman.com/downloads/

2.4 倒排索引

正排索引(傳統)

id content
1001 my name is zhang san
1002 my name is li si

倒排索引

keyword id
name 1001, 1002
zhang 1001

Elasticsearch 是面向文檔型數據庫,一條數據在這裏就是一個文檔。 爲了方便大家理解,我們將 Elasticsearch 裏存儲文檔數據和關係型數據庫 MySQL 存儲數據的概念進行一個類比
img

ES 裏的 Index 可以看做一個庫,而 Types 相當於表, Documents 則相當於表的行。這裏 Types 的概念已經被逐漸弱化, Elasticsearch 6.X 中,一個 index 下已經只能包含一個type, Elasticsearch 7.X 中, Type 的概念已經被刪除了。

2.5 索引

創建索引

對比關係型數據庫,創建索引就等同於創建數據庫。
在 Postman 中,向 ES 服務器發 PUT 請求 : http://127.0.0.1:9200/shopping
請求後,服務器返回響應:

{
    "acknowledged": true,//響應結果
    "shards_acknowledged": true,//分片結果
    "index": "shopping"//索引名稱
}

後臺日誌:

[2022-02-10T17:07:45,921][INFO ][o.e.c.m.MetadataCreateIndexService] [LAPTOP-7SVUMV4L] [shopping] creating index, cause [api], templates [], shards [1]/[1], mappings []

如果重複發 PUT 請求 : http://127.0.0.1:9200/shopping 添加索引,會返回錯誤信息 :

{
    "error": {
        "root_cause": [
            {
                "type": "resource_already_exists_exception",
                "reason": "index [shopping/J0WlEhh4R7aDrfIc3AkwWQ] already exists",
                "index_uuid": "J0WlEhh4R7aDrfIc3AkwWQ",
                "index": "shopping"
            }
        ],
        "type": "resource_already_exists_exception",
        "reason": "index [shopping/J0WlEhh4R7aDrfIc3AkwWQ] already exists",
        "index_uuid": "J0WlEhh4R7aDrfIc3AkwWQ",
        "index": "shopping"
    },
    "status": 400
}

查看所有索引

在 Postman 中,向 ES 服務器發 GET 請求 : http://127.0.0.1:9200/_cat/indices?v

這裏請求路徑中的_cat 表示查看的意思, indices 表示索引,所以整體含義就是查看當前 ES服務器中的所有索引,就好像 MySQL 中的 show tables 的感覺,服務器響應結果如下 :

health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   shopping abXoyvUySAKaNWNuKKy12g   1   1          2            0     11.7kb         11.7kb
表頭 含義
health 當前服務器健康狀態: green(集羣完整) yellow(單點正常、集羣不完整) red(單點不正常)
status 索引打開、關閉狀態
index 索引名
uuid 索引統一編號
pri 主分片數量
rep 副本數量
docs.count 可用文檔數量
docs.deleted 文檔刪除狀態(邏輯刪除)
store.size 主分片和副分片整體佔空間大小
pri.store.size 主分片佔空間大小

查看單個索引

在 Postman 中,向 ES 服務器發 GET 請求 : http://127.0.0.1:9200/shopping

返回結果如下:

{
    "shopping": {//索引名
        "aliases": {},//別名
        "mappings": {},//映射
        "settings": {//設置
            "index": {//設置 - 索引
                "creation_date": "1617861426847",//設置 - 索引 - 創建時間
                "number_of_shards": "1",//設置 - 索引 - 主分片數量
                "number_of_replicas": "1",//設置 - 索引 - 主分片數量
                "uuid": "J0WlEhh4R7aDrfIc3AkwWQ",//設置 - 索引 - 主分片數量
                "version": {//設置 - 索引 - 主分片數量
                    "created": "7080099"
                },
                "provided_name": "shopping"//設置 - 索引 - 主分片數量
            }
        }
    }
}

刪除索引

在 Postman 中,向 ES 服務器發 DELETE 請求 : http://127.0.0.1:9200/shopping

返回結果如下:

{
    "acknowledged": true
}

再次查看所有索引,GET http://127.0.0.1:9200/_cat/indices?v,返回結果如下:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size

成功刪除。

2.6 文檔

創建文檔

假設索引已經創建好了,接下來我們來創建文檔,並添加數據。這裏的文檔可以類比爲關係型數據庫中的表數據,添加的數據格式爲 JSON 格式
在 Postman 中,向 ES 服務器發 POST 請求 : http://127.0.0.1:9200/shopping/_doc,請求體JSON內容爲:

{
    "title":"小米手機",
    "category":"小米",
    "images":"http://www.gulixueyuan.com/xm.jpg",
    "price":3999.00
}

img

注意,此處發送請求的方式必須爲 POST,不能是 PUT,否則會發生錯誤 。

返回結果:

{
    "_index": "shopping",//索引
    "_type": "_doc",//類型-文檔
    "_id": "ANQqsHgBaKNfVnMbhZYU",//唯一標識,可以類比爲 MySQL 中的主鍵,隨機生成
    "_version": 1,//版本
    "result": "created",//結果,這裏的 create 表示創建成功
    "_shards": {//
        "total": 2,//分片 - 總數
        "successful": 1,//分片 - 總數
        "failed": 0//分片 - 總數
    },
    "_seq_no": 0,
    "_primary_term": 1
}

上面的數據創建後,由於沒有指定數據唯一性標識(ID),默認情況下, ES 服務器會隨機生成一個。

如果想要自定義唯一性標識,需要在創建時指定: http://127.0.0.1:9200/shopping/_doc/1001,請求體JSON內容爲:

{
    "title":"小米手機",
    "category":"小米",
    "images":"http://www.gulixueyuan.com/xm.jpg",
    "price":3999.00
}

返回結果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",//<------------------自定義唯一性標識
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

此處需要注意:如果增加數據時明確數據主鍵,那麼請求方式也可以爲 PUT。

主鍵查詢

查看文檔時,需要指明文檔的唯一性標識,類似於 MySQL 中數據的主鍵查詢
在 Postman 中,向 ES 服務器發 GET 請求 : http://127.0.0.1:9200/shopping/_doc/1001
返回結果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 1,
    "_seq_no": 1,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "title": "小米手機",
        "category": "小米",
        "images": "http://www.gulixueyuan.com/xm.jpg",
        "price": 3999
    }
}

查找不存在的內容,向 ES 服務器發 GET 請求 : http://127.0.0.1:9200/shopping/_doc/1002。
返回結果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1002",
    "found": false
}

全查詢

查看索引下所有數據,向 ES 服務器發 GET 請求 : http://127.0.0.1:9200/shopping/_search。
返回結果如下:

{
    "took": 61,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "HWQO434B7UmemU84rLH4",
                "_score": 1.0,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999.00
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "1001",
                "_score": 1.0,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999.00
                }
            }
        ]
    }
}

全量修改

和新增文檔一樣,輸入相同的 URL 地址請求,如果請求體變化,會將原有的數據內容覆蓋
在 Postman 中,向 ES 服務器發 POST 請求 : http://127.0.0.1:9200/shopping/_doc/1001
請求體JSON內容爲:

{
    "title":"華爲手機",
    "category":"華爲",
    "images":"http://www.gulixueyuan.com/hw.jpg",
    "price":1999.00
}

修改成功後,服務器響應結果:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    "_version": 2,
    "result": "updated",//<-----------updated 表示數據被更新
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}

局部修改

修改數據時,也可以只修改某一給條數據的局部信息
在 Postman 中,向 ES 服務器發 POST 請求 : http://127.0.0.1:9200/shopping/_update/1001。
請求體JSON內容爲:

{
    "doc": {
        "title":"小米手機",
        "category":"小米"
    }
}

返回結果如下:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 3,
    "result": "updated",//<-----------updated 表示數據被更新
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 3,
    "_primary_term": 1
}

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_doc/1,查看修改內容:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 4,
    "_seq_no": 4,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "doc": {
            "title": "小米手機",
            "category": "小米"
        }
    }
}

刪除文檔

刪除一個文檔不會立即從磁盤上移除,它只是被標記成已刪除(邏輯刪除)。
在 Postman 中,向 ES 服務器發 DELETE 請求 : http://127.0.0.1:9200/shopping/_doc/1001
返回結果:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 4,
    "result": "deleted",//<---刪除成功
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 4,
    "_primary_term": 1
}

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_doc/1001,查看是否刪除成功:

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "found": false
}

URL帶參查詢

查找category爲小米的文檔,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search?q=category:小米,返回結果如下:

{
    "took": 94,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.3862942,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

上述爲URL帶參數形式查詢,這很容易讓不善者心懷惡意,或者參數值出現中文會出現亂碼情況。爲了避免這些情況,我們可用使用帶JSON請求體請求進行查詢。

請求體帶參查詢

接下帶JSON請求體,還是查找category爲小米的文檔,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match":{
            "category":"小米"
        }
    }
}

返回結果如下:

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1.3862942,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

帶請求體方式的查找所有內容

查找所有文檔內容,也可以這樣,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_all":{}
    }
}

則返回所有文檔內容:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

查詢指定字段

如果你想查詢指定字段,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_all":{}
    },
    "_source":["title"]
}

返回結果如下:

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1,
                "_source": {
                    "title": "小米手機"
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1,
                "_source": {
                    "title": "小米手機"
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1,
                "_source": {
                    "title": "小米手機"
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 1,
                "_source": {
                    "title": "華爲手機"
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 1,
                "_source": {
                    "title": "華爲手機"
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 1,
                "_source": {
                    "title": "華爲手機"
                }
            }
        ]
    }
}

分頁查詢

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_all":{}
    },
    "from":0,
    "size":2
}

返回結果如下:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

查詢排序

如果你想通過排序查出價格最高的手機,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_all":{}
    },
    "sort":{
        "price":{
            "order":"desc"
        }
    }
}

返回結果如下:

{
    "took": 96,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": null,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                },
                "sort": [
                    3999
                ]
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": null,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "sort": [
                    1999
                ]
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": null,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "sort": [
                    1999
                ]
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": null,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "sort": [
                    1999
                ]
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": null,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "sort": [
                    1999
                ]
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": null,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "sort": [
                    1999
                ]
            }
        ]
    }
}

多條件查詢

假設想找出小米牌子,價格爲3999元的。(must相當於數據庫的&&

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下

{
    "query":{
        "bool":{
            "must":[{
                "match":{
                    "category":"小米"
                }
            },{
                "match":{
                    "price":3999.00
                }
            }]
        }
    }
}

返回結果如下:

{
    "took": 134,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 2.3862944,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 2.3862944,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            }
        ]
    }
}

範圍查詢

假設想找出小米或華爲價格高於2000的手機。(should相當於數據庫的||

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "bool":{
            "should":[{
                "match":{
                    "category":"小米"
                }
            },{
                "match":{
                    "category":"華爲"
                }
            }]
        },
        "filter":{
            "range":{
                "price":{
                    "gt":2000
                }
            }
        }
    }
}

返回結果如下:

{
    "took": 8,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1.3862942,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1.3862942,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 1.3862942,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 1.3862942,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 1.3862942,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

全文檢索

這功能像搜索引擎那樣,如品牌輸入“小華”,返回結果帶回品牌有“小米”和華爲的。

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match":{
            "category" : "小華"
        }
    }
}

返回結果如下:

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 0.6931471,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 0.6931471,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 0.6931471,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 0.6931471,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

完全匹配

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_phrase":{
            "category" : "爲"
        }
    }
}

返回結果如下:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 0.6931471,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    }
}

高亮查詢

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "query":{
        "match_phrase":{
            "category" : "爲"
        }
    },
    "highlight":{
        "fields":{
            "category":{}//<----高亮這字段
        }
    }
}

返回結果如下:

{
    "took": 100,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 0.6931471,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "highlight": {
                    "category": [
                        "華<em>爲</em>"//<------高亮一個爲字。
                    ]
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "highlight": {
                    "category": [
                        "華<em>爲</em>"
                    ]
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 0.6931471,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                },
                "highlight": {
                    "category": [
                        "華<em>爲</em>"
                    ]
                }
            }
        ]
    }
}

聚合查詢

聚合允許使用者對 es 文檔進行統計分析,類似與關係型數據庫中的 group by,當然還有很多其他的聚合,例如取最大值max、平均值avg等等。
接下來按price字段進行分組:
在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "aggs":{//聚合操作
        "price_group":{//名稱,隨意起名
            "terms":{//分組
                "field":"price"//分組字段
            }
        }
    }
}

返回結果如下:

{
    "took": 63,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "ANQqsHgBaKNfVnMbhZYU",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 3999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "A9R5sHgBaKNfVnMb25Ya",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BNR5sHgBaKNfVnMb7pal",
                "_score": 1,
                "_source": {
                    "title": "小米手機",
                    "category": "小米",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "BtR6sHgBaKNfVnMbX5Y5",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "B9R6sHgBaKNfVnMbZpZ6",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            },
            {
                "_index": "shopping",
                "_type": "_doc",
                "_id": "CdR7sHgBaKNfVnMbsJb9",
                "_score": 1,
                "_source": {
                    "title": "華爲手機",
                    "category": "華爲",
                    "images": "http://www.gulixueyuan.com/xm.jpg",
                    "price": 1999
                }
            }
        ]
    },
    "aggregations": {
        "price_group": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": 1999,
                    "doc_count": 5
                },
                {
                    "key": 3999,
                    "doc_count": 1
                }
            ]
        }
    }
}

上面返回結果會附帶原始數據的。若不想要不附帶原始數據的結果,在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "aggs":{
        "price_group":{
            "terms":{
                "field":"price"
            }
        }
    },
    "size":0
}

返回結果如下:

{
    "took": 60,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "price_group": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": 1999,
                    "doc_count": 5
                },
                {
                    "key": 3999,
                    "doc_count": 1
                }
            ]
        }
    }
}

若想對所有手機價格求平均值。

在 Postman 中,向 ES 服務器發 GET請求 : http://127.0.0.1:9200/shopping/_search,附帶JSON體如下:

{
    "aggs":{
        "price_avg":{//名稱,隨意起名
            "avg":{//求平均
                "field":"price"
            }
        }
    },
    "size":0
}

返回結果如下:

{
    "took": 14,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 6,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "price_avg": {
            "value": 2332.3333333333335
        }
    }
}

映射關係

有了索引庫,等於有了數據庫中的 database。
接下來就需要建索引庫(index)中的映射了,類似於數據庫(database)中的表結構(table)。
創建數據庫表需要設置字段名稱,類型,長度,約束等;索引庫也一樣,需要知道這個類型下有哪些字段,每個字段有哪些約束信息,這就叫做映射(mapping)
先創建一個索引:

# PUT http://127.0.0.1:9200/user

返回結果:

{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "user"
}   

創建映射
# PUT http://127.0.0.1:9200/user/_mapping

{
    "properties": {
        "name":{
            "type": "text",
            "index": true
        },
        "sex":{
            "type": "keyword",
            "index": true
        },
        "tel":{
            "type": "keyword",
            "index": false
        }
    }
}

返回結果如下:

{
    "acknowledged": true
}

查詢映射
#GET http://127.0.0.1:9200/user/_mapping
返回結果如下:

{
    "user": {
        "mappings": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "sex": {
                    "type": "keyword"
                },
                "tel": {
                    "type": "keyword",
                    "index": false
                }
            }
        }
    }
}

增加數據

#PUT http://127.0.0.1:9200/user/_create/1001

{
    "name":"小米",
    "sex":"男的",
    "tel":"1111"
}

返回結果如下:

{
    "_index": "user",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

查找name含有”小“數據:json

#GET http://127.0.0.1:9200/user/_search

{
    "query":{
        "match":{
            "name":"小"
        }
    }
}

返回結果如下:

{
    "took": 495,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "user",
                "_type": "_doc",
                "_id": "1001",
                "_score": 0.2876821,
                "_source": {
                    "name": "小米",
                    "sex": "男的",
                    "tel": "1111"
                }
            }
        ]
    }
}

查找sex含有”男“數據:

#GET http://127.0.0.1:9200/user/_search

{
    "query":{
        "match":{
            "sex":"男"
        }
    }
}

返回結果如下:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }
}

找不想要的結果,只因創建映射時"sex"的類型爲"keyword"。

"sex"只能完全爲”男的“,才能得出原數據。

#GET http://127.0.0.1:9200/user/_search

{
    "query":{
        "match":{
            "sex":"男的"
        }
    }
}

返回結果如下:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "user",
                "_type": "_doc",
                "_id": "1001",
                "_score": 0.2876821,
                "_source": {
                    "name": "小米",
                    "sex": "男的",
                    "tel": "1111"
                }
            }
        ]
    }
}

查詢電話

# GET http://127.0.0.1:9200/user/_search

{
    "query":{
        "match":{
            "tel":"11"
        }
    }
}

返回結果如下:

{
    "error": {
        "root_cause": [
            {
                "type": "query_shard_exception",
                "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                "index_uuid": "ivLnMfQKROS7Skb2MTFOew",
                "index": "user"
            }
        ],
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "phase": "query",
        "grouped": true,
        "failed_shards": [
            {
                "shard": 0,
                "index": "user",
                "node": "4P7dIRfXSbezE5JTiuylew",
                "reason": {
                    "type": "query_shard_exception",
                    "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                    "index_uuid": "ivLnMfQKROS7Skb2MTFOew",
                    "index": "user",
                    "caused_by": {
                        "type": "illegal_argument_exception",
                        "reason": "Cannot search on field [tel] since it is not indexed."
                    }
                }
            }
        ]
    },
    "status": 400
}

報錯只因創建映射時"tel"的"index"爲false。

第三章 JavaAPI

新建Maven工程。

添加依賴:

<dependencies>
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>7.8.0</version>
    </dependency>
    <!-- elasticsearch 的客戶端 -->
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.8.0</version>
    </dependency>
    <!-- elasticsearch 依賴 2.x 的 log4j -->
    <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>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.9</version>
    </dependency>
    <!-- junit 單元測試 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

HelloElasticsearch

import java.io.IOException;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class HelloElasticsearch {

    public static void main(String[] args) throws IOException {
        // 創建客戶端對象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));
//        ...
        System.out.println(client);

        // 關閉客戶端連接
        client.close();
    }
}

3.1 索引操作

創建索引

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import java.io.IOException;

public class CreateIndex {

    public static void main(String[] args) throws IOException {
        // 創建客戶端對象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));

        // 創建索引 - 請求對象
        CreateIndexRequest request = new CreateIndexRequest("user2");
        // 發送請求,獲取響應
        CreateIndexResponse response = client.indices().create(request,
                RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        // 響應狀態
        System.out.println("操作狀態 = " + acknowledged);

        // 關閉客戶端連接
        client.close();
    }

}

後臺打印

二月 10, 2022 8:32:24 下午 org.elasticsearch.client.RestClient logResponse
警告: request [PUT http://localhost:9200/user2?master_timeout=30s&include_type_name=true&timeout=30s] returned 1 warnings: [299 Elasticsearch-7.8.0-757314695644ea9a1dc2fecd26d1a43856725e65 "[types removal] Using include_type_name in create index requests is deprecated. The parameter will be removed in the next major version."]
操作狀態 = true

查詢索引

import org.apache.http.HttpHost;

import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;

import java.io.IOException;

public class SearchIndex {
    public static void main(String[] args) throws IOException {
        // 創建客戶端對象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));

        // 查詢索引 - 請求對象
        GetIndexRequest request = new GetIndexRequest("user2");
        // 發送請求,獲取響應
        GetIndexResponse response = client.indices().get(request,
                RequestOptions.DEFAULT);
        
        System.out.println("aliases:"+response.getAliases());
        System.out.println("mappings:"+response.getMappings());
        System.out.println("settings:"+response.getSettings());

        client.close();
    }
}

後臺打印:

aliases:{user2=[]}
mappings:{user2=org.elasticsearch.cluster.metadata.MappingMetadata@ceb7e09a}
settings:{user2={"index.creation_date":"1644496343959","index.number_of_replicas":"1","index.number_of_shards":"1","index.provided_name":"user2","index.uuid":"dnRxs3_iSm6SM39LSe8_vA","index.version.created":"7080099"}}

刪除索引

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import java.io.IOException;

public class DeleteIndex {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));
        // 刪除索引 - 請求對象
        DeleteIndexRequest request = new DeleteIndexRequest("user2");
        // 發送請求,獲取響應
        AcknowledgedResponse response = client.indices().delete(request,RequestOptions.DEFAULT);
        // 操作結果
        System.out.println("操作結果 : " + response.isAcknowledged());
        client.close();
    }
}

後臺打印:

操作結果 : true

Process finished with exit code 0

3.2 重構優化

上文由於頻繁使用以下連接Elasticsearch和關閉它的代碼,於是個人對它進行重構。

public class SomeClass {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));
        
        ...
        
        client.close();
    }
}

重構後的代碼:

import org.elasticsearch.client.RestHighLevelClient;

public interface ElasticsearchTask {

    void doSomething(RestHighLevelClient client) throws Exception;

}

使用

public class ConnectElasticsearch{

    public static void connect(ElasticsearchTask task){
        // 創建客戶端對象
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));
        try {
            task.doSomething(client);
            // 關閉客戶端連接
            client.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接下來,如果想讓Elasticsearch完成一些操作,就編寫一個lambda式即可。

public class SomeClass {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //do something
        });
    }
}

封裝好User類

public class User {
    private String name;
    private String sex;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3.3 文檔操作

新增文檔

import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

public class InsertDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            // 新增文檔 - 請求對象
            IndexRequest request = new IndexRequest();
            // 設置索引及唯一性標識
            request.index("user").id("1001");

            // 創建數據對象
            User user = new User();
            user.setName("zhangsan");
            user.setAge(30);
            user.setSex("男");

            ObjectMapper objectMapper = new ObjectMapper();
            String productJson = objectMapper.writeValueAsString(user);
            // 添加文檔數據,數據格式爲 JSON 格式
            request.source(productJson, XContentType.JSON);
            // 客戶端發送請求,獲取響應對象
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            // 打印結果信息
            System.out.println("_index:" + response.getIndex());
            System.out.println("_id:" + response.getId());
            System.out.println("_result:" + response.getResult());
        });
    }
}

後臺打印:

_index:user
_id:1001
_result:UPDATED

Process finished with exit code 0

修改文檔

import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

public class UpdateDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            // 修改文檔 - 請求對象
            UpdateRequest request = new UpdateRequest();
            // 配置修改參數
            request.index("user").id("1001");
            // 設置請求體,對數據進行修改
            request.doc(XContentType.JSON, "sex", "女");
            // 客戶端發送請求,獲取響應對象
            UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
            System.out.println("_index:" + response.getIndex());
            System.out.println("_id:" + response.getId());
            System.out.println("_result:" + response.getResult());
        });
    }

}

後臺打印:

_index:user
_id:1001
_result:UPDATED

Process finished with exit code 0

查詢文檔

import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.RequestOptions;

public class GetDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //1.創建請求對象
            GetRequest request = new GetRequest().index("user").id("1001");
            //2.客戶端發送請求,獲取響應對象
            GetResponse response = client.get(request, RequestOptions.DEFAULT);
            //3.打印結果信息
            System.out.println("_index:" + response.getIndex());
            System.out.println("_type:" + response.getType());
            System.out.println("_id:" + response.getId());
            System.out.println("source:" + response.getSourceAsString());
        });
    }
}

後臺打印:

_index:user
_type:_doc
_id:1001
source:{"name":"zhangsan","sex":"女","age":30}
Process finished with exit code 0

刪除文檔

import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.client.RequestOptions;

public class DeleteDoc {
    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //創建請求對象
            DeleteRequest request = new DeleteRequest().index("user").id("1001");
            //客戶端發送請求,獲取響應對象
            DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
            //打印信息
            System.out.println(response.toString());
        });
    }
}

後臺打印:

DeleteResponse[index=user,type=_doc,id=1001,version=3,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]

Process finished with exit code 0

3.4 批量操作

批量新增

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

public class BatchInsertDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //創建批量新增請求對象
            BulkRequest request = new BulkRequest();
            request.add(new
                    IndexRequest().index("user").id("1001").source(XContentType.JSON, "name",
                    "zhangsan"));
            request.add(new
                    IndexRequest().index("user").id("1002").source(XContentType.JSON, "name",
                    "lisi"));
            request.add(new
                    IndexRequest().index("user").id("1003").source(XContentType.JSON, "name",
                    "wangwu"));
            //客戶端發送請求,獲取響應對象
            BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
            //打印結果信息
            System.out.println("took:" + responses.getTook());
            System.out.println("items:" + responses.getItems());
        });
    }
}

後臺打印

took:4ms
items:[Lorg.elasticsearch.action.bulk.BulkItemResponse;@5552768b

Process finished with exit code 0

批量刪除

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.client.RequestOptions;

public class BatchDeleteDoc {
    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //創建批量刪除請求對象
            BulkRequest request = new BulkRequest();
            request.add(new DeleteRequest().index("user").id("1001"));
            request.add(new DeleteRequest().index("user").id("1002"));
            request.add(new DeleteRequest().index("user").id("1003"));
            //客戶端發送請求,獲取響應對象
            BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
            //打印結果信息
            System.out.println("took:" + responses.getTook());
            System.out.println("items:" + responses.getItems());
        });
    }
}

後臺打印

took:4ms
items:[Lorg.elasticsearch.action.bulk.BulkItemResponse;@5c671d7f

3.5 高級查詢

全量查詢

先批量增加數據

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

public class BatchInsertDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            //創建批量新增請求對象
            BulkRequest request = new BulkRequest();
            request.add(new IndexRequest().index("user").id("1001").source(XContentType.JSON, "name", "zhangsan", "age", "10", "sex","女"));
            request.add(new IndexRequest().index("user").id("1002").source(XContentType.JSON, "name", "lisi", "age", "30", "sex","女"));
            request.add(new IndexRequest().index("user").id("1003").source(XContentType.JSON, "name", "wangwu1", "age", "40", "sex","男"));
            request.add(new IndexRequest().index("user").id("1004").source(XContentType.JSON, "name", "wangwu2", "age", "20", "sex","女"));
            request.add(new IndexRequest().index("user").id("1005").source(XContentType.JSON, "name", "wangwu3", "age", "50", "sex","男"));
            request.add(new IndexRequest().index("user").id("1006").source(XContentType.JSON, "name", "wangwu4", "age", "20", "sex","男"));
            //客戶端發送請求,獲取響應對象
            BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
            //打印結果信息
            System.out.println("took:" + responses.getTook());
            System.out.println("items:" + responses.getItems());
        });
    }
}

查詢所有索引數據

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;

public class QueryDoc {

    public static void main(String[] args) {
        ConnectElasticsearch.connect(client -> {
            // 創建搜索請求對象
            SearchRequest request = new SearchRequest();
            request.indices("user");
            // 構建查詢的請求體
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            // 查詢所有數據
            sourceBuilder.query(QueryBuilders.matchAllQuery());
            request.source(sourceBuilder);
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 查詢匹配
            SearchHits hits = response.getHits();
            System.out.println("took:" + response.getTook());
            System.out.println("timeout:" + response.isTimedOut());
            System.out.println("total:" + hits.getTotalHits());
            System.out.println("MaxScore:" + hits.getMaxScore());
            System.out.println("hits========>>");
            for (SearchHit hit : hits) {
                //輸出每條查詢的結果信息
                System.out.println(hit.getSourceAsString());
            }
            System.out.println("<<========");
        });
    }

}

後臺打印

took:743ms
timeout:false
total:6 hits
MaxScore:1.0
hits========>>
{"name":"zhangsan","age":"10","sex":"女"}
{"name":"lisi","age":"30","sex":"女"}
{"name":"wangwu1","age":"40","sex":"男"}
{"name":"wangwu2","age":"20","sex":"女"}
{"name":"wangwu3","age":"50","sex":"男"}
{"name":"wangwu4","age":"20","sex":"男"}
<<========

條件查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_BY_CONDITION = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");
        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.termQuery("age", "30"));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_BY_CONDITION);
    }
}

後臺打印

took:29ms
timeout:false
total:1 hits
MaxScore:1.0
hits========>>
{"name":"lisi","age":"30","sex":"女"}
<<========

分頁查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_BY_PAGING = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");
        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        // 分頁查詢
        // 當前頁其實索引(第一條數據的順序號), from
        sourceBuilder.from(0);

        // 每頁顯示多少條 size
        sourceBuilder.size(2);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_BY_PAGING);
    }

}

後臺打印

took:2ms
timeout:false
total:6 hits
MaxScore:1.0
hits========>>
{"name":"zhangsan","age":"10","sex":"女"}
{"name":"lisi","age":"30","sex":"女"}
<<========

Process finished with exit code 0

查詢排序

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_WITH_ORDER = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");

        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        // 排序
        sourceBuilder.sort("age", SortOrder.ASC);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_WITH_ORDER);
    }

}

後臺打印

took:81ms
timeout:false
total:6 hits
MaxScore:NaN
hits========>>
{"name":"zhangsan","age":"10","sex":"女"}
{"name":"wangwu2","age":"20","sex":"女"}
{"name":"wangwu4","age":"20","sex":"男"}
{"name":"lisi","age":"30","sex":"女"}
{"name":"wangwu1","age":"40","sex":"男"}
{"name":"wangwu3","age":"50","sex":"男"}
<<========

Process finished with exit code 0

組合查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_BY_BOOL_CONDITION = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");
        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 必須包含
        boolQueryBuilder.must(QueryBuilders.matchQuery("age", "30"));
        // 一定不含
        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("name", "zhangsan"));
        // 可能包含
        boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "男"));
        sourceBuilder.query(boolQueryBuilder);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");

    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_BY_BOOL_CONDITION);
    }
}

後臺打印

took:42ms
timeout:false
total:1 hits
MaxScore:1.0
hits========>>
{"name":"lisi","age":"30","sex":"女"}
<<========

Process finished with exit code 0

範圍查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;


public class QueryDoc {

    public static final ElasticsearchTask SEARCH_BY_RANGE = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");
        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
        // 大於等於
        //rangeQuery.gte("30");
        // 小於等於
        rangeQuery.lte("40");
        sourceBuilder.query(rangeQuery);
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_BY_RANGE);
    }

}

後臺打印

took:8ms
timeout:false
total:5 hits
MaxScore:1.0
hits========>>
{"name":"zhangsan","age":"10","sex":"女"}
{"name":"lisi","age":"30","sex":"女"}
{"name":"wangwu1","age":"40","sex":"男"}
{"name":"wangwu2","age":"20","sex":"女"}
{"name":"wangwu4","age":"20","sex":"男"}
<<========

Process finished with exit code 0

模糊查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_BY_FUZZY_CONDITION = client -> {
        // 創建搜索請求對象
        SearchRequest request = new SearchRequest();
        request.indices("user");
        // 構建查詢的請求體
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.fuzzyQuery("name","wangwu").fuzziness(Fuzziness.ONE));
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 查詢匹配
        SearchHits hits = response.getHits();
        System.out.println("took:" + response.getTook());
        System.out.println("timeout:" + response.isTimedOut());
        System.out.println("total:" + hits.getTotalHits());
        System.out.println("MaxScore:" + hits.getMaxScore());
        System.out.println("hits========>>");
        for (SearchHit hit : hits) {
            //輸出每條查詢的結果信息
            System.out.println(hit.getSourceAsString());
        }
        System.out.println("<<========");
    };


    public static void main(String[] args) {
        
        ConnectElasticsearch.connect(SEARCH_BY_FUZZY_CONDITION);
    }

}

後臺打印

took:41ms
timeout:false
total:4 hits
MaxScore:1.2837042
hits========>>
{"name":"wangwu1","age":"40","sex":"男"}
{"name":"wangwu2","age":"20","sex":"女"}
{"name":"wangwu3","age":"50","sex":"男"}
{"name":"wangwu4","age":"20","sex":"男"}
<<========

Process finished with exit code 0

高亮查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;

import java.util.Map;

public class QueryDoc {

    public static final ElasticsearchTask SEARCH_WITH_HIGHLIGHT = client -> {
        // 高亮查詢
        SearchRequest request = new SearchRequest().indices("user");
        //2.創建查詢請求體構建器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //構建查詢方式:高亮查詢
        TermsQueryBuilder termsQueryBuilder =
                QueryBuilders.termsQuery("name","zhangsan");
        //設置查詢方式
        sourceBuilder.query(termsQueryBuilder);
        //構建高亮字段
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'>");//設置標籤前綴
        highlightBuilder.postTags("</font>");//設置標籤後綴
        highlightBuilder.field("name");//設置高亮字段
        //設置高亮構建對象
        sourceBuilder.highlighter(highlightBuilder);
        //設置請求體
        request.source(sourceBuilder);
        //3.客戶端發送請求,獲取響應對象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.打印響應結果
        SearchHits hits = response.getHits();
        System.out.println("took::"+response.getTook());
        System.out.println("time_out::"+response.isTimedOut());
        System.out.println("total::"+hits.getTotalHits());
        System.out.println("max_score::"+hits.getMaxScore());
        System.out.println("hits::::>>");
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            System.out.println(sourceAsString);
            //打印高亮結果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            System.out.println(highlightFields);
        }
        System.out.println("<<::::");
    };


    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_WITH_HIGHLIGHT);
    }

}

後臺打印

took::85ms
time_out::false
total::1 hits
max_score::1.0
hits::::>>
{"name":"zhangsan","age":"10","sex":"女"}
{name=[name], fragments[[<font color='red'>zhangsan</font>]]}
<<::::

Process finished with exit code 0

最大值查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;


public class QueryDoc {

    public static final ElasticsearchTask SEARCH_WITH_MAX = client -> {
        // 高亮查詢
        SearchRequest request = new SearchRequest().indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));
        //設置請求體
        request.source(sourceBuilder);
        //3.客戶端發送請求,獲取響應對象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.打印響應結果
        SearchHits hits = response.getHits();
        System.out.println(response);
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_WITH_MAX);
    }

}

後臺打印

{"took":16,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":6,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"user","_type":"_doc","_id":"1001","_score":1.0,"_source":{"name":"zhangsan","age":"10","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1002","_score":1.0,"_source":{"name":"lisi","age":"30","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1003","_score":1.0,"_source":{"name":"wangwu1","age":"40","sex":"男"}},{"_index":"user","_type":"_doc","_id":"1004","_score":1.0,"_source":{"name":"wangwu2","age":"20","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1005","_score":1.0,"_source":{"name":"wangwu3","age":"50","sex":"男"}},{"_index":"user","_type":"_doc","_id":"1006","_score":1.0,"_source":{"name":"wangwu4","age":"20","sex":"男"}}]},"aggregations":{"max#maxAge":{"value":50.0}}}

Process finished with exit code 0

分組查詢

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;


public class QueryDoc {

    public static final ElasticsearchTask SEARCH_WITH_GROUP = client -> {
        SearchRequest request = new SearchRequest().indices("user");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(AggregationBuilders.terms("age_groupby").field("age"));
        //設置請求體
        request.source(sourceBuilder);
        //3.客戶端發送請求,獲取響應對象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.打印響應結果
        SearchHits hits = response.getHits();
        System.out.println(response);
    };

    public static void main(String[] args) {
        ConnectElasticsearch.connect(SEARCH_WITH_GROUP);
    }

}

後臺打印

{"took":14,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":6,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"user","_type":"_doc","_id":"1001","_score":1.0,"_source":{"name":"zhangsan","age":"10","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1002","_score":1.0,"_source":{"name":"lisi","age":"30","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1003","_score":1.0,"_source":{"name":"wangwu1","age":"40","sex":"男"}},{"_index":"user","_type":"_doc","_id":"1004","_score":1.0,"_source":{"name":"wangwu2","age":"20","sex":"女"}},{"_index":"user","_type":"_doc","_id":"1005","_score":1.0,"_source":{"name":"wangwu3","age":"50","sex":"男"}},{"_index":"user","_type":"_doc","_id":"1006","_score":1.0,"_source":{"name":"wangwu4","age":"20","sex":"男"}}]},"aggregations":{"lterms#age_groupby":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":20,"doc_count":2},{"key":10,"doc_count":1},{"key":30,"doc_count":1},{"key":40,"doc_count":1},{"key":50,"doc_count":1}]}}}

Process finished with exit code 0

第四章、Elasticsearch環境

4.1 相關概念

單機 & 集羣

單臺 Elasticsearch 服務器提供服務,往往都有最大的負載能力,超過這個閾值,服務器
性能就會大大降低甚至不可用,所以生產環境中,一般都是運行在指定服務器集羣中。
除了負載能力,單點服務器也存在其他問題:

  • 單臺機器存儲容量有限
  • 單服務器容易出現單點故障,無法實現高可用
  • 單服務的併發處理能力有限

配置服務器集羣時,集羣中節點數量沒有限制,大於等於 2 個節點就可以看做是集羣了。一
般出於高性能及高可用方面來考慮集羣中節點數量都是 3 個以上

總之,集羣能提高性能,增加容錯。

集羣 Cluster

一個集羣就是由一個或多個服務器節點組織在一起,共同持有整個的數據,並一起提供索引和搜索功能。一個 Elasticsearch 集羣有一個唯一的名字標識,這個名字默認就是”elasticsearch”。這個名字是重要的,因爲一個節點只能通過指定某個集羣的名字,來加入這個集羣。

節點 Node

集羣中包含很多服務器, 一個節點就是其中的一個服務器。 作爲集羣的一部分,它存儲數據,參與集羣的索引和搜索功能。

一個節點也是由一個名字來標識的,默認情況下,這個名字是一個隨機的漫威漫畫角色的名字,這個名字會在啓動的時候賦予節點。這個名字對於管理工作來說挺重要的,因爲在這個管理過程中,你會去確定網絡中的哪些服務器對應於 Elasticsearch 集羣中的哪些節點。

一個節點可以通過配置集羣名稱的方式來加入一個指定的集羣。默認情況下,每個節點都會被安排加入到一個叫做“elasticsearch”的集羣中,這意味着,如果你在你的網絡中啓動了若干個節點,並假定它們能夠相互發現彼此,它們將會自動地形成並加入到一個叫做“elasticsearch”的集羣中。

在一個集羣裏,只要你想,可以擁有任意多個節點。而且,如果當前你的網絡中沒有運
行任何 Elasticsearch 節點,這時啓動一個節點,會默認創建並加入一個叫做“elasticsearch”的
集羣。

4.2 Windows 集羣部署

部署集羣

一、創建 elasticsearch-cluster 文件夾

創建 elasticsearch-7.8.0-cluster 文件夾,在內部複製三個 elasticsearch 服務。

image-20221206104113809

二、修改集羣文件目錄中每個節點的 config/elasticsearch.yml 配置文件

node-1001 節點

#節點 1 的配置信息:
#集羣名稱,節點之間要保持一致
cluster.name: my-elasticsearch
#節點名稱,集羣內要唯一
node.name: node-1001
node.master: true
node.data: true
#ip 地址
network.host: localhost
#http 端口
http.port: 1001
#tcp 監聽端口
transport.tcp.port: 9301
#discovery.seed_hosts: ["localhost:9301", "localhost:9302","localhost:9303"]
#discovery.zen.fd.ping_timeout: 1m
#discovery.zen.fd.ping_retries: 5
#集羣內的可以被選爲主節點的節點列表
#cluster.initial_master_nodes: ["node-1", "node-2","node-3"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"

node-1002 節點

#節點 2 的配置信息:
#集羣名稱,節點之間要保持一致
cluster.name: my-elasticsearch
#節點名稱,集羣內要唯一
node.name: node-1002
node.master: true
node.data: true
#ip 地址
network.host: localhost
#http 端口
http.port: 1002
#tcp 監聽端口
transport.tcp.port: 9302
discovery.seed_hosts: ["localhost:9301"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5
#集羣內的可以被選爲主節點的節點列表
#cluster.initial_master_nodes: ["node-1", "node-2","node-3"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"

node-1003 節點

#節點 3 的配置信息:
#集羣名稱,節點之間要保持一致
cluster.name: my-elasticsearch
#節點名稱,集羣內要唯一
node.name: node-1003
node.master: true
node.data: true
#ip 地址
network.host: localhost
#http 端口
http.port: 1003
#tcp 監聽端口
transport.tcp.port: 9303
#候選主節點的地址,在開啓服務後可以被選爲主節點
discovery.seed_hosts: ["localhost:9301", "localhost:9302"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5
#集羣內的可以被選爲主節點的節點列表
#cluster.initial_master_nodes: ["node-1", "node-2","node-3"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"

啓動集羣

刪除每個節點中的 data 目錄中所有內容 。

分別依次雙擊執行節點的bin/elasticsearch.bat, 啓動節點服務器(可以編寫一個腳本啓動),啓動後,會自動加入指定名稱的集羣。

測試集羣

一、用Postman,查看集羣狀態

GET http://127.0.0.1:1001/_cluster/health
GET http://127.0.0.1:1002/_cluster/health
GET http://127.0.0.1:1003/_cluster/health

返回結果皆爲如下:

{
    "cluster_name": "my-application",
    "status": "green",
    "timed_out": false,
    "number_of_nodes": 3,
    "number_of_data_nodes": 3,
    "active_primary_shards": 0,
    "active_shards": 0,
    "relocating_shards": 0,
    "initializing_shards": 0,
    "unassigned_shards": 0,
    "delayed_unassigned_shards": 0,
    "number_of_pending_tasks": 0,
    "number_of_in_flight_fetch": 0,
    "task_max_waiting_in_queue_millis": 0,
    "active_shards_percent_as_number": 100.0
}

status字段指示着當前集羣在總體上是否工作正常。它的三種顏色含義如下:

  • green:所有的主分片和副本分片都正常運行。
  • yellow:所有的主分片都正常運行,但不是所有的副本分片都正常運行。
  • red:有主分片沒能正常運行。

二、用Postman,在一節點增加索引,另一節點獲取索引

向集羣中的node-1001節點增加索引:

#PUT http://127.0.0.1:1001/user

返回結果:

{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "user"
}

向集羣中的node-1003節點獲取索引:

#GET http://127.0.0.1:1003/user

返回結果:

{
    "user": {
        "aliases": {},
        "mappings": {},
        "settings": {
            "index": {
                "creation_date": "1617993035885",
                "number_of_shards": "1",
                "number_of_replicas": "1",
                "uuid": "XJKERwQlSJ6aUxZEN2EV0w",
                "version": {
                    "created": "7080099"
                },
                "provided_name": "user"
            }
        }
    }
}

如果在1003創建索引,同樣在1001也能獲取索引信息,這就是集羣能力。

4.3 Linux 單機部署

軟件安裝

一、下載軟件

下載Linux版的Elasticsearch

二、解壓軟件

# 解壓縮
tar -zxvf elasticsearch-7.8.0-linux-x86_64.tar.gz -C /opt/module
# 改名
mv elasticsearch-7.8.0 es

三、創建用戶

因爲安全問題, Elasticsearch 不允許 root 用戶直接運行,所以要創建新用戶,在 root 用戶中創建新用戶。

useradd es #新增 es 用戶
passwd es #爲 es 用戶設置密碼
userdel -r es #如果錯了,可以刪除再加
chown -R es:es /opt/module/es #文件夾所有者

四、修改配置文件

修改/opt/module/es/config/elasticsearch.yml文件。

# 加入如下配置
cluster.name: elasticsearch
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]

修改/etc/security/limits.conf

# 在文件末尾中增加下面內容
# 每個進程可以打開的文件數的限制
es soft nofile 65536
es hard nofile 65536

修改/etc/security/limits.d/20-nproc.conf

# 在文件末尾中增加下面內容
# 每個進程可以打開的文件數的限制
es soft nofile 65536
es hard nofile 65536
# 操作系統級別對每個用戶創建的進程數的限制
* hard nproc 4096
# 注: * 帶表 Linux 所有用戶名稱

修改/etc/sysctl.conf

# 在文件中增加下面內容
# 一個進程可以擁有的 VMA(虛擬內存區域)的數量,默認值爲 65536
vm.max_map_count=655360

重新加載

sysctl -p

啓動軟件

使用 ES 用戶啓動

cd /opt/module/es/
#啓動
bin/elasticsearch
#後臺啓動
bin/elasticsearch -d  

啓動時,會動態生成文件,如果文件所屬用戶不匹配,會發生錯誤,需要重新進行修改用戶和用戶組

img

關閉防火牆

#暫時關閉防火牆
systemctl stop firewalld
#永久關閉防火牆
systemctl enable firewalld.service #打開防火牆永久性生效,重啓後不會復原
systemctl disable firewalld.service #關閉防火牆,永久性生效,重啓後不會復原

測試軟件

瀏覽器中輸入地址: http://linux1:9200/

img

4.4 Linux 集羣部署

軟件安裝

一、下載軟件

下載Linux版的Elasticsearch

二、解壓軟件

# 解壓縮
tar -zxvf elasticsearch-7.8.0-linux-x86_64.tar.gz -C /opt/module
# 改名
mv elasticsearch-7.8.0 es-cluster

將軟件分發到其他節點: linux2, linux3

三、創建用戶

因爲安全問題, Elasticsearch 不允許 root 用戶直接運行,所以要創建新用戶,在 root 用戶中創建新用戶。

useradd es #新增 es 用戶
passwd es #爲 es 用戶設置密碼
userdel -r es #如果錯了,可以刪除再加
chown -R es:es /opt/module/es #文件夾所有者

四、修改配置文件

修改/opt/module/es/config/elasticsearch.yml 文件,分發文件。

# 加入如下配置
#集羣名稱
cluster.name: cluster-es
#節點名稱, 每個節點的名稱不能重複
node.name: node-1
#ip 地址, 每個節點的地址不能重複
network.host: linux1
#是不是有資格主節點
node.master: true
node.data: true
http.port: 9200
# head 插件需要這打開這兩個配置
http.cors.allow-origin: "*"
http.cors.enabled: true
http.max_content_length: 200mb
#es7.x 之後新增的配置,初始化一個新的集羣時需要此配置來選舉 master
cluster.initial_master_nodes: ["node-1"]
#es7.x 之後新增的配置,節點發現
discovery.seed_hosts: ["linux1:9300","linux2:9300","linux3:9300"]
gateway.recover_after_nodes: 2
network.tcp.keep_alive: true
network.tcp.no_delay: true
transport.tcp.compress: true
#集羣內同時啓動的數據任務個數,默認是 2 個
cluster.routing.allocation.cluster_concurrent_rebalance: 16
#添加或刪除節點及負載均衡時併發恢復的線程個數,默認 4 個
cluster.routing.allocation.node_concurrent_recoveries: 16
#初始化數據恢復時,併發恢復線程的個數,默認 4 個
cluster.routing.allocation.node_initial_primaries_recoveries: 16

修改/etc/security/limits.conf ,分發文件

# 在文件末尾中增加下面內容
es soft nofile 65536
es hard nofile 65536

修改/etc/security/limits.d/20-nproc.conf,分發文件

# 在文件末尾中增加下面內容
es soft nofile 65536
es hard nofile 65536
\* hard nproc 4096
\# 注: * 帶表 Linux 所有用戶名稱

修改/etc/sysctl.conf

# 在文件中增加下面內容
vm.max_map_count=655360

重新加載

sysctl -p

啓動軟件

分別在不同節點上啓動 ES 軟件

cd /opt/module/es-cluster
#啓動
bin/elasticsearch
#後臺啓動
bin/elasticsearch -d

測試集羣

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章