主頁 > 後端開發 > 全文檢索Elasticsearch研究

全文檢索Elasticsearch研究

2020-11-05 02:12:26 後端開發

全文檢索Elasticsearch研究

基于虛擬機服務器自主部署ELK服務

學習目標

  1. 了解Elasticsearch的應用場景
  2. 學習基于服務器部署ELK服務
  3. 掌握索引維護的方法
  4. 掌握索引維護的方法
  5. 掌味訓本的搜索API的使用方法

約束

需要提前掌握Lucene的索引方法、搜索方法

ELK的介紹和安裝

1.簡介

? Elasticsearch是一個基于Lucene的搜索服務器,它提供了一個分布式多用戶能力的全文搜索引擎,基于Restful web介面,Elasticsearch是用java開發的,是當前流行的企業級搜索引擎,能到達到實時搜索,穩定、可靠、快速、安裝使用方便,

? 我們建立一個網站或應用程式,并要添加搜索功能,如果搜索的數量非常多,而且分類繁雜,如果使用傳統的資料庫想要完成搜索作業的創建失非常困難的,我們希望搜索解決方案要運行速度快,我們希望有一個零配置和完全免費的搜索模式,能夠簡單的使用JSON通過HTTP來索引資料,而搜索服務器始終可用,并且服務器可以自如擴展,我們一般都會使用全文檢索技術,如solr、Elasticsearch等,

2.突出優點

  1. 擴展性好,可部署上百臺服務器集群,處理PB級資料
  2. 近實時的去索引資料、搜索資料

3.原理與應用

3.1 索引結構

下圖是ElasticSearch的索引結構,下邊==黑色部分是物理結構==,上邊==黃色部分是邏輯結構==,邏輯結構可以更好的描述ElasticSearch的作業原理及去使用物理結構中的索引檔案,

BQJJij.md.png
BQJJij.md.png

邏輯結構部分是一個倒排索引表:

  1. 將要搜索的檔案內容分詞,所有不重復的詞做成分詞串列,
  2. 將搜索的檔案最終以Document方式存盤起來,
  3. 每個詞和document都有關聯,

3.2 RESTFUL應用方法

ElasticSearch提供RESTFUL Api介面進行索引、搜索、并且支持多種客戶端,

下圖是ElasticSearch在專案中的應用方式:

BQcykR.md.png
BQcykR.md.png
  1. 用戶在前端搜索關鍵字
  2. 專案前端通過http方式請求專案服務端
  3. 專案服務端通過http RESTful方式請求ES集群進行搜索
  4. ES集群從索引庫檢索資料

4.ElasticaSearc安裝

4.1 安裝配置

  1. 安裝ElasticaSearc7.9.0
  2. 該版本要求至少jdk1.8以上
  3. 解壓elasticsearch-7.9.0-linux-x86_64.tar.gz
    • bin:腳本目錄,包括:啟動、停止等可執行腳本
    • config:組態檔目錄
    • data:索引目錄,存放索引檔案的地方
    • modules:模板目錄,包括了es的功能模塊
    • plugins:插件目錄,es支持插件機制

4.2 組態檔

ES組態檔的地址根據安裝形式的不同而不同:

  1. 使用zip、tar安裝,組態檔的地址在安裝目錄的config下
  2. 使用RPM安裝,組態檔在/etc/elasticsearch下
  3. 使用MSI安裝,組態檔的地址在安裝目錄的config下,并且會自動將config目錄地址寫入環境變數ES_PATH_CONF

4.3 安裝

為了模擬真實場景,我們將在linux系統下安裝Elasticsearch

4.3.1 新建一個用戶gavin

處于安全考慮,Elasticsearch默認不允許以root賬號運行

創建用戶:

useradd gavin

設定密碼:

passwd gavin

切換用戶:

su gavin

洗掉用戶:

userdel -r gavin

普通用戶增加sudo命令的權限:

vim /etc/sudoers

gavin   ALL=(ALL)       ALL

改變目錄及其目錄下所有檔案的所有者為當前普通用戶:

chown -R yourname dirname

4.3.3 解壓縮

tar -zxvf elasticsearch-7.9.0-linux-x86_64.tar.gz

4.3.4 目錄重命名

mv elasticsearch-7.9.0 elasticsearch

4.3.5 修改組態檔

進入config目錄,修改elasticsearch.ymljvm.options

  1. jvm.options

默認配置:

-Xms1g
-Xmx1g

記憶體占用太多,設定為不超過物理記憶體的一半:

-Xms512m
-Xmx512m
  1. elasticsearch.yml

修改資料和日志目錄

# 資料目錄位置
path.data: /home/gavin/elasticsearch/data
# 日志目錄位置
path.logs: /home/gavin/elasticsearch/logs

elasticsearch的安裝目錄默認只有logs目錄,沒有data目錄,需要手動創建:

mkdir data

修改系結的ip:

# 系結0.0.0.0 允許任何ip來訪問,默認只允許本機訪問
network.host: 0.0.0.0

目前我們是學習單機安裝,如果要做集群,只需要在這個組態檔中添加節點資訊即可,

屬性名 說明
cluster.name 配置elasticsearch的集群名稱,默認是elasticsearch,建議修改成一個有意義的名稱,
node.name 節點名,es會默認隨機指定一個名字,建議指定一個有意義的名稱,方便管理
path.conf 設定組態檔的存盤路徑,tar或zip包安裝默認在es根目錄下的config檔案夾,rpm安裝默認在/etc/ elasticsearch
path.data 設定索引資料的存盤路徑,默認是es根目錄下的data檔案夾,可以設定多個存盤路徑,用逗號隔開
path.logs 設定日志檔案的存盤路徑,默認是es根目錄下的logs檔案夾
path.plugins 設定插件的存放路徑,默認是es根目錄下的plugins檔案夾
bootstrap.memory_lock 設定為true可以鎖住ES使用的記憶體,避免記憶體進行swap
network.host 設定bind_host和publish_host,設定為0.0.0.0允許外網訪問
http.port 設定對外服務的http埠,默認為9200,
transport.tcp.port 集群結點之間通信埠
discovery.zen.ping.timeout 設定ES自動發現節點連接超時的時間,默認為3秒,如果網路延遲高可設定大些
discovery.zen.minimum_master_nodes 主結點數量的最少值 ,此值的公式為:(master_eligible_nodes / 2) + 1 ,比如:有3個符合要求的主結點,那么這里要設定為2

4.4 運行

進入elasticsearch/bin*目錄下,可以看到如下的可執行檔案:

然后輸入運行命令:

./elasticsearch

4.5 啟動報錯

4.5.1 JDK版本過低 并且 不支持 root用戶啟動

解決方案:

1.因為elasticsearch7.9.X內置了jdk,默認是jdk11,但是向下兼容,所以可以不用處理

2.切換到普通用戶進行啟動,此時需要修改檔案目錄下所有檔案的所有者為當前用戶,

chown -R gavin /home/gavin/elasticsearch

4.5.2 集群節點導致啟動報錯

解決:

vim elasticsearch.yml

ip替換host1等,多節點請添加多個ip地址,單節點可寫按默認來
#配置以下三者,最少其一
#[discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes]
cluster.initial_master_nodes: ["node-1"] #這里的node-1為node-name配置的值

啟動成功:

可以看到系結了兩個埠:

  • 9200:客戶端訪問埠
  • 9300:集群節點之間通訊埠

我們在瀏覽器中訪問:http://192.168.15.100:9200/

5.安裝kibana

kibana是一個基于Node.js的Elasticsearch索引庫資料統計工具,可以利用Elasticsearch的聚合功能,生成各種圖表,如柱狀圖、線狀圖、餅圖等,

而且還提供了操作Elasticsearch索引資料的控制臺,并且提供了一定的API提示,非常有利于學習Elasticsearch的語法,

5.1 安裝

因為Kibana是依賴于node

查看是否服務器是否安裝nodejs

[root@centos logs]# node -v
v9.3.0

如果沒有安裝,則安裝步驟如下:

1.可以在下載頁面https://nodejs.org/en/download/中找到下載地址,然后執行指令

wget https://nodejs.org/dist/v9.3.0/node-v9.3.0-linux-x64.tar.xz

2.解壓縮

xz -d node-v9.3.0-linux-x64.tar.xz
tar -xf node-v9.3.0-linux-x64.tar

3.部署bin檔案

根據自己nodejs的實際路徑,依次執行下面命令,建立軟連接:

ln -s /usr/local/software/node/bin/node /usr/bin/node
ln -s /usr/local/software/node/bin/npm /usr/bin/npm
ln -s /usr/local/software/node/bin/npx /usr/bin/npx

4.測驗

node -v
npm -v
npx -v

5.1.1 解壓縮kibana

tar -zxvf kibana-7.9.0-linux-x86_64.tar.gz

5.1.2 重命名安裝包

mv kibana-7.9.0-linux-x86_64 kibana

5.1.3 修改配置

vim /home/gavin/kibana/config/kibana.yml

elasticsearch.hosts: ["http://192.168.15.100:9200"]

5.2 啟動

cd /home/gavin/kibana/bin

./kibana

6.安裝ik分析器

Lucene的IK分詞器早在2012年就已經沒有維護了,現在我們要使用的是在其基礎上維護升級的版本,并且開發為ElasticSearch的繼承插件了,與ElasticSearch一起維護升級了,版本也保持一致,

6.1 解壓縮

unzip elasticsearch-analysis-ik-7.9.0.zip -d /home/gavin/kibana/plugins/ik-analyzer

6.2 重啟elasticsearch

加載IK分詞器插件、

6.3 測驗

在Dev Tools --> console 中輸入下面請求:

POST _analyze
{
  "analyzer""ik_max_word",
  "text":     "我是中國人"
}

API

Elasticsearch提供了Rest風格的API,即http請求介面,而且也提供了各種語言的客戶端API

檔案地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

1.客戶端API

Elasticsearch支持的客戶端非常多,如:https://www.elastic.co/guide/en/elasticsearch/client/index.html

點開Java Rest Client后,會有兩個:

  • Java Low Level REST Client:是低級別封裝,提供一些基礎功能,但更靈活
  • Java High Level REST Client:是在Low Level Rest Client基礎上進行的高級別封裝,功能更豐富和完善,而且API會變得簡單

2.如何學習

2.1 操作索引

Elasticsearch是基于Lucene的全文檢索庫,本質也是存盤資料,很多概念與mysql類似

對比關系:

索引(indices) --------------------------------------------- Databases 資料庫

? 型別(type) ------------------------------------------ Table 資料表

? 檔案(Document) ---------------------------Row 行

? 欄位(field) -----------------------------Columns 列

說明:

概念 說明
索引庫(indices) indices是index的復數,代表許多的索引
型別(type) 型別是模擬mysql中的table概念,一個索引庫下可以有不同型別的索引,比如商品索引、訂單索引,其資料格式不同,不過這會導致索引庫混亂,因此未來版本中會移除這個概念
檔案(document) 存入索引庫原始的資料,比如每一條商品資訊,就是一個檔案
欄位(field) 檔案中的屬性
映射配置(mappings) 欄位的資料型別,屬性、是否索引、是否存盤等特性

特別說明:

Elasticsearch本身就是分布式的,因此即便你只有一個節點,Elasticsearch默認也會對你的資料進行分片和副本操作,當你向集群添加新資料時,資料也會在新加入的節點中進行平衡

2.2語法

Elasticsearch采用Rest風格API,因此其API就是一次http請求,可以使用任何工具進行發起http請求

2.1.1 索引庫設定
  1. 創建索引索引庫設定:
  • 請求方式:PUT

  • 請求路徑:/索引名

  • 請求引數:json格式:

    {
        "settings": {
            "number_of_shards"3,
            "number_of_replicas"2
          }
    }

    settings:索引庫的設定

    number_of_shards:分片數量

    number_of_replicas:副本數量

    測驗

    1. 使用postman進行創建索引并對索引庫進行設定測驗

? 2. 使用kibana進行創建索引并對索引庫進行設定測驗

  1. 查看索引庫設定

語法:

GET ceshi

或者,使用*來查詢所有索引配置:

  1. 洗掉索引庫設定

語法:

DELETE /索引庫名

再次查看

2.1.2 映射配置

索引庫創建好之后就是添加資料,再添加資料之前必須定義映射

映射:

定義檔案的程序,檔案包含哪些欄位,這些欄位是否保存、是否索引、是否分詞等

  1. 創建映射欄位

    語法:

    請求方式是PUT,型別名稱和_mapping可以互換位置

    PUT /索引庫名稱/_doc/型別名稱
    {
      "properties": {
        "type""型別",
        "index"true,
        "store"true,
        "analyzer""分詞器"
      }
    }

    型別:就是前面提過的type的概念,類似于資料庫中的不同表

    欄位名:任意填寫,可以指定很多屬性,如:

    • type:型別,可以是text、long、short、date、integer、object等
    • index:是否索引,默認為true
    • store:是否存盤,默認是false
    • analyzer:分詞器,這里的ik_max_word即表示使用ik分詞器

示例:

? 請求:

PUT ceshi/_doc/goods
{
  "properties": {
    "title": {
      "type""text",
      "analyzer""ik_max_word"
    },
    "images": {
      "type""keyword",
      "index"false
    },
    "price": {
      "type""float"
    }
  }
}

特別說明:

傳統ES6創建映射的時候是把上面的**_doc換成_mapping**

ES7這個**_mapping已經移除了,使用_doc**代替

否則會報如下錯誤:

? 回應結果:

{
  "_index" : "ceshi",
  "_type" : "_doc",
  "_id" : "goods",
  "_version" : 3,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
  1. 查看映射關系

    語法:

    GET 索引庫名/_mapping

    示例:

    GET ceshi/_mapping

    結果:

    1. 欄位屬性詳解

3.1 type

Elasticsearch中支持的資料型別非常豐富

常用的說明下:

  • String型別,分為兩種:
    • text:可分詞,不可參與聚合
    • keyword:不可分詞,資料會作為完整欄位進行匹配,可以參與聚合
  • Numerical:數值型別,分兩類
    • 基本資料型別:long、integer、short、byte、double、float、half_float
    • 浮點數的高精度型別:scaled_float
      • 需要指定一個精度因子,比如10或100,Elasticsearch會把真實值乘以這個因子以后存盤,取出時再還原,
  • Date:日期型別
    • Elasticsearch可以對日期格式化為字串存盤,但是建議我們存盤為毫秒值,存盤為long,節省空間,

3.2 index

index影響欄位的索引情況

  • true:欄位會被索引,則可以用來進行搜索,默認值是true
  • false:欄位不會被索引,不能用來搜索

特別說明:

index的默認值就是true,也就是說你不進行任何配置,所有欄位都會被索引,但是有些欄位我們不希望索引的,就需要手動設定index為false

3.3 store

是否將資料額外存盤

在lucene和solr中,我們設定store欄位為false,那么這個欄位在檔案串列中就不會有這個欄位的值,用戶搜索結果中就不會顯示出來,

但在Elasticsearch中,即便設定為false,也可以搜索結果,

原因是Elasticsearch在創建檔案索引庫時,會將檔案中的原始資料備份,保存到一個叫_source的屬性中,而且我們可以通過過濾_source來選擇哪些要顯示,哪些不顯示,

而如果設定storetrue,就會在_source以外額外存盤一份資料,多余,因此我們一般都會講store設定為false,事實上,store的默認是就是false

2.1.3 新增資料

1.隨機生成id

通過POST請求,可以向一個已經存在的索引庫中添加資料

語法:

POST /索引庫名/_doc/型別名
{
  "key""value"
}

示例:

POST /ceshi/_doc/goods
{
  "title""小米手機",
  "images""http://image.leyou.com/12479122.jpg",
  "price"2688.01
}

運行結果:

查看新增資料結果

GET /ceshi/_search
{
    "query":{
        "match_all":{}
    }
}
  • _source:源檔案資訊,所有資料都在里面
  • _id:這條檔案的唯一標識,與檔案自己的id沒有關聯

2.自定義id

如果我們在新增資料的時候指定id,可以按如下操作:

語法:

POST /索引庫名/_doc/id
{
  "key""value"
}

示例:

POST /ceshi/_doc/2
{
  "title""小米手機",
  "images""http://image.leyou.com/12479122.jpg",
  "price"2988.02
}
2.1.4 智能判斷

在學習solr時,我們在新增資料時,智能使用提前批配置好映射屬性的欄位,否則就會報錯

不過在Elasticsearch中,可以不需要給索引庫設定任何的映射屬性的欄位,它也可以根據輸入的資料來判斷型別,動態添加資料映射

示例:

POST /ceshi/_doc/3
{
    "title":"超米手機",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":2899.00,
    "stock"200,
    "saleable":true
}

我們發現,ceshi索引庫額外增加了stock庫存和saleable是否上架兩個欄位,

查看此時的索引庫映射關系

GET ceshi/_mapping
2.1.5 修改資料

把剛才新增請求的方式改為PUT,就是修改了,不過修改必須指定id

  • id對應檔案存在,則修改
  • id對應檔案不存在,則新增

比如,我們把id為3的資料進行修改:

PUT /ceshi/_doc/3
{
    "title":"超大米手機",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":3899.00,
    "stock"100,
    "saleable":true
}
2.1.6 洗掉資料

洗掉資料使用DELETE請求方式,同樣,根據id進行洗掉

語法:

DELETE /索引庫名/_doc/id

示例:

DELETE /ceshi/_doc/1

結果:洗掉id為1的索引庫資料

2.1.7 查詢資料
  • 基本查詢
  • _source過濾
  • 結果過濾
  • 高級查詢
  • 排序

1.基本查詢

基本語法

GET /索引庫名/_search
{
  "query": {
    "查詢型別": {
      "查詢條件""查詢條件值"
    }
  }
}
  • query:表示一個查詢物件,里面可以有不同的查詢屬性

  • 查詢型別:如,mastch_allmatchtermrange

  • 查詢條件:

  • 查詢條件會根據型別的不同,寫法也有差異,后面詳細講解

2.查詢所有(match_all)

GET /ceshi/_search
{
  "query": {
    "match_all": {}
  }
}
  • query:代表查詢物件
  • match_all:代表查詢所有
  • took:查詢花費時間,單位是毫秒
  • time_out:是否超時
  • _shards:分片資訊
  • hits:搜索結果總覽物件
    • total:搜索到的總條數
    • max_score:所有結果中檔案得分的最高分
    • hits:搜索結果的檔案物件陣列,每個元素是一條搜索到的檔案資訊
      • _index:索引庫
      • _type:檔案型別
      • _id:檔案id
      • _score:檔案得分
      • _source:檔案的源資料

3.匹配查詢(match)

先增加一條資料,便于測驗

PUT /ceshi/_doc/1
{
    "title":"小米電視4A",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":1899.00
}

特別說明:增加資料使用POSTPUT的區別

  • PUT:需要指定id,否則會報錯,冪等操作
  • POST:指定的id存在,則更新資料,不存在要么自定義id,要么隨機生成,非冪等操作

從結果中看到,索引庫中有2部手機,1臺電視

  • or關系

match型別查詢,會把查詢條件進行分詞,然后進行查詢,多個詞條之間是or的關系

GET /ceshi/_search
{
  "query": {
    "match": {
      "title""小米電視"
    }
  }
}

默認情況下,是會通過分詞,使多個詞之間是or的關系,

  • and關系

某些時候需要精確查找,需要將多個詞關系設定為and

GET /ceshi/_search
{
  "query": {
    "match": {
      "title": {
        "query""小米電視",
        "operator""and"
      }
    }
  }
}

本例中,只有同時包含小米電視的詞條才會被搜索到

  • or and and 之間

場景:如果用戶給定的條件分詞后有5個查詢詞項,想查找只包含其中4個詞的檔案,該如何處理?將operator運算子設定成and只會將此檔案排除,

有時候這正是我們期望的,但在全文搜索的大多數應用場景下,我們既想包含那些可能相關的檔案,同時又排除那些不太相關的,換句話說,我們想要處于中間某種結果,

match查詢支持minimum_should_match最小匹配引數,這讓我們可以指定匹配的詞項數用來表示一個檔案是否相關,我們可以將其設定為某個具體數字,更常用的做法是將其設定為一個百分數,因為我們無法控制用戶搜索時輸入的單詞數量,

示例:

GET /ceshi/_search
{
  "query": {
    "match": {
      "title": {
        "query""小米曲面電視",
        "minimum_should_match""75%"
      }
    }
  }
}
  • 多欄位查詢(multi_match)

matchmulti_match類似,不同的是multi_match可以在多個欄位中查詢

示例:

GET /ceshi/_search
{
    "query":{
        "multi_match": {
            "query":    "小",
            "fields":   [ "title""subTitle" ]
        }
 }
}
  • 詞條匹配(term)

term查詢被用于精確值匹配,這些精確值可能是數字、時間、布爾或者那些未分詞的字串

示例:

GET /ceshi/_search
{
  "query": {
    "term": {
     "price""1899"
    }
  }
}
  • 多詞條精確匹配(terms)

terms查詢和term查詢一樣,但它允許你指定多值進行匹配,如果這個欄位中包含了指定值中的任何一個值,那么這個檔案滿足條件:

  • 結果過濾

默認情況下,elasticsearch在搜索結果中,會把檔案中保存在_source的所有欄位回傳,但是,如果我們只想要獲取其中的部分欄位,我們可以添加_source的過濾,

  1. 直接指定欄位

    示例:

    GET /ceshi/_search
    {
      "_source": ["title","price"],
      "query": {
        "term": {
          "price"1899
        }
      }
    }

    2. 指定includes和excludes

    我們也可以通過“

    • includes:來指定想要顯示的欄位
    • excludes:來指定不想要顯示的欄位

    示例:

    GET /ceshi/_search
    {
      "_source": {
        "includes": ["title","images"]
      },
      "query": {
        "term": {
          "price"1899
        }
      }
    }

    GET /ceshi/_search
    {
      "_source": {
        "excludes": ["title","images"]
      },
      "query": {
        "term": {
          "price"1899
        }
      }
    }
2.18 高級查詢

1.布爾組合(bool)

bool把各種其他查詢通過must(與)、must_not(非)、shoud(或)的當時組合

示例

# 查詢title可能包含“大米”,但一定包含“手機”的資料
GET /ceshi/_search
{
  "query": {
    "bool": {
      "must": {"match":{"title":"大米"}},
      "must_not":{"match":{"title":"電視"}},
      "should":{"match":{"title":"手機"}}
    }
  }
}

2.范圍查詢(range)

range查詢找出那些落在指定區間內的數字或時間

示例

# 查詢price在1000-2900范圍內的資料
GET /ceshi/_search
{
  "query": {
    "range": {
      "price": {
        "gte"1000,
        "lte"2900
      }
    }
  }
}

range查詢允許以下字符:

運算子 說明
gt 大于
gte 大于等于
lt 小于
lte 小于等于

3.模糊查詢(fuzzy)

新增一條資料

POST /ceshi/_doc/4
{
    "title":"apple手機",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":6899.00
}

fuzzy查詢時term查詢的模糊等價,它允許用戶搜索詞條與實際詞條的拼寫出現偏差,但是偏差的編輯距離不得超過2

POST /ceshi/_doc/4
{
    "title":"apple手機",
    "images":"http://image.leyou.com/12479122.jpg",
    "price":6899.00
}

上面查詢也是可以查到apple手機資料的

我們可以通過fuzziness屬性來指定允許的偏差距離:

GET /ceshi/_search
{
  "query": {
    "fuzzy": {
      "title": {
        "value""appaa",
        "fuzziness"3
      }
    }
  }
}

也是可以查到資料

注意:fuzzinexx值越大,偏差距離也越大,模糊查詢的范圍也越大,反之,

4.過濾(filter)

條件查詢找那個進行過濾

所有的查詢都會影響到檔案的評分及排名,如果我們需要在查詢結果中進行過濾,并且不希望過濾條件影響評分,那么就不要吧過濾條件作為查詢條件來用,而是使用filter方式:

GET /ceshi/_search
{
  "query": {
    "bool": {
      "must":{"match":{"title":"手機"}},
      "filter": {
        "range": {
          "price": {
            "gte"1000,
            "lte"5000
          }
        }
      }
    }
  }
}

注意:filet中還可以再次進行bool組合條件過濾

無查詢條件,直接過濾

如果一個查詢只有過濾,沒有查詢條件,不希望進行評分,我們可以使用constant_score取代只有filter陳述句的bool查詢,在性能上時完全相同的,但對于提高查詢簡潔性和清晰度有很大幫助,

示例:

GET /ceshi/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "price": {
            "gte"1000,
            "lte"4000
          }
        }
      }
    }
  }
}

5.排序

需求:想要將查詢條件title和price范圍過濾出來結果,進行首先按照價格排序,然后按照得分排序:

GET /ceshi/_search
{
  "query": {
    "bool": {
      "must":{"match":{"title":"手機"}},
      "filter":{
        "range": {
          "price": {
            "gte"1000,
            "lte"7000
          }
        }
      }
    }
  },
  "sort": [
    {"price": {"order""desc"}},
    {"_score": {"order""desc"}}
  ]
}

2.3聚合aggregations

聚合可以讓我們及其方便的實作對資料的統計、分析,例如:

  • 什么品牌的手機最受歡迎
  • 這些手機的平均價格、最高價格、最低價格
  • 這些手機每月的銷售情況如何

實作這些統計功能要比資料庫的sql方便的多,而且查詢速度非常快,可以實作實時搜索效果,

2.3.1 基本概念

Elasticsearch中的聚合,包含多種型別,最常用的兩種:

  • 度量
1.桶(bucket)

桶的作用,是按照某種方式對資料進行分組,每一組資料在ES中稱為一個桶,例如,我們根據國籍對人劃分,可以得到中國桶、英國桶、美國桶,,,

Elasticsearch中提供的劃分桶的方式很多:

  • Date Histogram Aggregation:根據日期階梯分組,例如給定階梯為周,會自動每周分一組
  • Histogram Aggregation:根據數值階梯分組,與日期類似
  • Terms Aggregation:根據詞條內容分組,詞條內容完全匹配分為一組
  • Range Aggregation:數值和日期的范圍分組,指定開始和結束,然后分段分組
  • ,,,

綜上所述,我們發現bucket aggregations只負責對資料進行分組,并不進行計算,因此bucket中往往會嵌套另一種聚合:metrics aggregations 即度量

2.度量(metrics)

分組完成以后,我們一般會對組中的資料進行聚合運算,例如求平均值、最大、最小、求和等操作,這些在ES中稱為度量

比較常用的一些度量聚合方式:

  • Avg Aggregation:求平均值
  • Max Aggregation:求最大值
  • MIn Aggregation:求最小值
  • Percentiles Aggregation:求百分比
  • Stats Aggregation:同時回傳avg、max、min、sum、count等
  • Sum Aggregation:求和
  • Top hits Aggregation:求前幾
  • Value Count Aggregation:求總數
  • ,,,

開始測驗

為了方便測驗,我們首先批量匯入測驗資料

2.3.2 創建索引庫
PUT /cars
{
  "settings": {
    "number_of_shards"1,
    "number_of_replicas"0
  },
  "mappings": {
      "properties":{
        "color":{"type":"keyword"},
        "make":{"type":"keyword"}
    }
  }
}

查看索引庫映射關系:GET /cars/_mapping

注意:在ES中,需要進行聚合、排序、過濾的欄位其處理方式比較特殊,因此不能被分詞,這里我們將color和make這兩個欄位型別設定為keyword型別,這個型別不會被分詞,將來就可以參與聚合

匯入資料:

POST /cars/_bulk
"index": {}}
"price" : 10000"color" : "red""make" : "honda""sold" : "2014-10-28" }
"index": {}}
"price" : 20000"color" : "red""make" : "honda""sold" : "2014-11-05" }
"index": {}}
"price" : 30000"color" : "green""make" : "ford""sold" : "2014-05-18" }
"index": {}}
"price" : 15000"color" : "blue""make" : "toyota""sold" : "2014-07-02" }
"index": {}}
"price" : 12000"color" : "green""make" : "toyota""sold" : "2014-08-19" }
"index": {}}
"price" : 20000"color" : "red""make" : "honda""sold" : "2014-11-05" }
"index": {}}
"price" : 80000"color" : "red""make" : "bmw""sold" : "2014-01-01" }
"index": {}}
"price" : 25000"color" : "blue""make" : "ford""sold" : "2014-02-12" }

查看cars索引庫中的資料:

GET /cars/_search
{
  "query": {
    "match_all": {}
  }
}
2.3.3 聚合為桶
1.按照cars中的color欄位來劃分桶
GET /cars/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color"
            }
        }
    }
}
  • size:查詢條數,這里設定為0,因為我們不關心搜索到的資料,只關心聚合結果,提高效率
  • aggs:宣告這是一個聚合查詢,是aggregations的縮寫
    • popular_color:給這次聚合起一個名字,任意,
      • terms:劃分桶的方式,這里是根據詞潭訓分
        • field:劃分桶的欄位
  • hits:查詢結果為空,因為我們設定了size為0
  • aggregations:聚合的結果
    • popular_clor:我們定義的聚合名稱
      • buckets:查找到的桶,每個不同的color欄位值都會形成一個桶
        • key:這個桶對應的color欄位的值
        • doc_count:這個桶中的檔案數量

總結:通過聚合的結果我們發現,目前紅色的小車比較暢銷

2.3.4 桶內度量

前面的例子告訴我們每個桶里面的檔案數量,但通常,我們的應用需要提供更為復雜的檔案度量,例如,每種顏色騎車的平均價格是多少?

因此,我們需要告訴Elasticsearch使用哪個欄位,使用何種度量方式進行運算,這些資訊要嵌套在==桶內==,度量的運算會基于==桶內==的檔案進行,

示例:

需求:按照cars中的color欄位劃分桶,并求相應每個桶中的平均價格

GET /cars/_search
{
  "size"0,
  "aggs": {
    "popular_color": {
      "terms": {
        "field""color"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field""price"
          }
        }
      }
    }
  }
}
  • arrgs:我們在aggs(popular_color)中添加新的aggs,可見==度量也是一個聚合,度量是在桶中的聚合==,
  • avg_price:度量聚合的名稱,任意
  • avg:度量的型別,這里是求平均值
  • field:度量運算的欄位

結果:

我們可以看到每個桶中都有自己的avg_price欄位,這就是度量聚合的結果

2.3.5 桶內嵌套桶

上面示例是桶內嵌套度量運算,事實上桶內不僅可以嵌套運算,還可以嵌套其他桶,也就是說在每個分組中,可以再分更多桶,

示例:

需求:我們想要統計每種顏色的汽車中,分別屬于哪個制造商,按照make欄位在進行分桶

GET /cars/_search
{
  "size"0,
  "aggs": {
    "popular_color": {
      "terms": {
        "field""color"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field""price"
          }
        },
        "maker":{
          "terms": {
            "field""make"
          }
        }
      }
    }
  }
}

結果:

{
  "took" : 14,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 8,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_color" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "red",
          "doc_count" : 4,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "honda",
                "doc_count" : 3
              },
              {
                "key" : "bmw",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 32500.0
          }
        },
        {
          "key" : "blue",
          "doc_count" : 2,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ford",
                "doc_count" : 1
              },
              {
                "key" : "toyota",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 20000.0
          }
        },
        {
          "key" : "green",
          "doc_count" : 2,
          "maker" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ford",
                "doc_count" : 1
              },
              {
                "key" : "toyota",
                "doc_count" : 1
              }
            ]
          },
          "avg_price" : {
            "value" : 21000.0
          }
        }
      ]
    }
  }
}
  • 我們可以看到,新的聚合maker被嵌套在原來每一個color的桶中,
  • 每個顏色下面都根據make欄位進行了分組
  • 我們從結果中讀到的資訊:
    • 紅色車共有4輛
    • 紅色車的平均售價32500
    • 其中3輛是Honda本田制造,1輛是BMW寶馬制造
2.3.6 階梯分桶(Histogram)

histogram是把數值型別的欄位,按照一定的階梯大小進行分組,需要指定一個階梯值(interval)來劃分階梯大小

示例

需求:比如你有價格欄位,如果你設定interval的值為200.那么階梯就會是這樣的:

0,200,400,600,,,,

上面列出的是每個階梯的key,也是區間的起點

如果一件商品的價格是450,會落在哪個階梯區間呢?計算公式如下:

bucket_key = Math.floor((value-offset)/interval)*interval+offset

  • value:就是當前資料的值,本例中是450
  • offset:起始偏移值,默認為0
  • interval:階梯間隔,比如200
  • 因此得到的key=Math.floor((450-0)/200)*200+0=400

我們對汽車的價格進行分組,指定間隔interval為5000:

# 階梯分桶,對汽車的價格分組,指定間隔interval為5000,并約束桶內的檔案最小值為1
GET /cars/_search
{
  "size"0,
  "aggs": {
    "price": {
      "histogram": {
        "field""price",
        "interval"5000,
        "min_doc_count"1
      }
    }
  }
}
2.3.7 范圍分桶(range)

范圍分桶和階梯分桶類似,也是把數字按照階段進行分組,只不過range方式需要你自己指定每一組的起始和結束大小

2.4 Spring Data Elaticsearch

Elasticsearch提供的java客戶端有一些不太方便的地方:

  • 很多地方需要在java中拼接json字串
  • 需要自己把物件序列化為json存盤
  • 查詢到結果也需要自己反序列化為物件

因此,我們可以學習Spring提供的套件:Spring Data Elaticsearch

2.4.1 簡介

Spring Data Elaticsearch是Spring Data專案下的一個子模塊

Spring Data官網:http://projects.spring.io/spring-data/

Spring Data的使命是給各種資料訪問提供統一的編程介面,不管是關系型資料庫(mysql),還是非關系型資料庫(redis),或者類似Elaticsearch索引資料庫,

Spring Data Elaticsearch的頁面:https://projects.spring.io/spring-data-elasticsearch/

特征:

  • 支持Spring的基于@configuration的java配置方式,或者XML配置方式
  • 提供了用于操作ES的便捷工具類ElaticsearchTemplate,包括實作檔案到POJO之間的自動智能映射
  • 利用Spring的資料轉換服務實作的功能豐富的物件映射
  • 基于注解的元資料映射方式,而且可擴展以支持更多不同的資料格式
  • 根據持久層介面自動生成物件實作方法,無需人工撰寫基本操作代碼(類似mybatis,根據介面自動得到實作,也支持人工定制查詢)
2.4.2 專案實戰

1.創建一個專案,匯入如下pom依賴:

<!-- high client -->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>${es.version}</version>
</dependency>

<!-- rest-high-level-client 依賴如下兩個jar -->
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>${es.version}</version>
</dependency>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>${es.version}</version>
</dependency>

2.application.yml配置

es:
  host: 192.168.15.100
  port: 9200
  scheme: http

3.es配置類

@Configuration
public class ElaticsearchConfig {

    @Value("${es.host}")
    public String host;
    @Value("${es.port}")
    public int port;
    @Value("${es.scheme}")
    public String scheme;

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(RestClient.builder(
                new HttpHost(host, port, scheme)));
    }
}

4.單元測驗

  • 創建索引庫
@Test
public void createIndexTest() throws IOException {
 CreateIndexRequest indexRequest = new CreateIndexRequest(index);
 CreateIndexResponse response = client.indices().create(indexRequest,                    RequestOptions.DEFAULT);
 System.out.println(response.isAcknowledged());
}
  • 判斷索引庫是否存在
@Test
public void indexExistsTest() throws IOException {
 GetIndexRequest request = new GetIndexRequest(index);
 boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
 System.out.println(exists);
}
  • 添加檔案
@Test
public void addDocTest() throws IOException {
    IndexRequest request = new IndexRequest(index);
    String source = JSONObject.toJSONString(new Users(1000"瑟曦"30));
    request.source(source, XContentType.JSON);
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
    System.out.println(response.getResult());  //CREATED
}
  • 批量添加檔案
@Test
public void batchAddDocTest() throws IOException {
    BulkRequest bulkRequest = new BulkRequest();
    List<IndexRequest> indexRequests = generateRequests();
    indexRequests.forEach(x -> {
        bulkRequest.add(x);
    });
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    System.out.println(response.hasFailures());
}

public List<IndexRequest> generateRequests() {
    List<IndexRequest> requests = new ArrayList<>();
    requests.add(generateNewRequests(new Users(1"雪諾"25)));
    requests.add(generateNewRequests(new Users(2"艾麗婭"20)));
    requests.add(generateNewRequests(new Users(3"珊莎"23)));
    return requests;
}

public IndexRequest generateNewRequests(Users users) {
    IndexRequest indexRequest = new IndexRequest(index);
    indexRequest.source(JSONObject.toJSONString(users), XContentType.JSON);
    return indexRequest;
}
  • 根據條件搜索檔案
@Test
public void serachTest() throws IOException {
    SearchRequest request = new SearchRequest(index);
    SearchSourceBuilder builder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    boolQueryBuilder.must(new RangeQueryBuilder("age").from(20).to(30))
        .mustNot(new TermQueryBuilder("id"1000));
    builder.query(boolQueryBuilder);
    request.source(builder);
    System.out.println("搜索陳述句為:" + request.source().toString());
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    System.out.println("搜索結果:" + response);
    SearchHits hits = response.getHits();
    SearchHit[] hitsArr = hits.getHits();
    for (SearchHit documentFields : hitsArr) {
        System.out.println(documentFields.getSourceAsString());
    }
}
  • 修改檔案
@Test
public void modifyDocTest() throws IOException {
    UpdateRequest request = new UpdateRequest(index, "nqHRknUBJcoqc7s-i-Az");
    Map<String, Object> params = new HashMap<>();
    params.put("id"4);
    request.doc(params);
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    System.out.println(response.getResult());
}
  • 洗掉指定ID的檔案
@Test
public void deleteDocTest() throws IOException {
    DeleteRequest request = new DeleteRequest(index, "nqHRknUBJcoqc7s-i-Az");
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    System.out.println(response.getResult());
}
  • 洗掉索引庫
@Test
public void deleteIndex() throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest(index);
    AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
    System.out.println(response.isAcknowledged());

}

完結

與springboot集成的話也可直接使用ElasticsearchRestTemplate,也是基于RestHighLevelClient的模板封裝,后續有需要可以研究下,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/202747.html

標籤:Java

上一篇:java流程控制.1scanner

下一篇:SpringBoot第四集:整合JdbcTemplate和JPA(2020最新最易懂)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more