主頁 > 後端開發 > ElasticSearch

ElasticSearch

2022-08-03 15:44:06 後端開發

1、ElasticSearch簡介

1.1 Lucene

  • Doug Cutting開發
  • apache軟體基金會4 jakarta專案組的一個子專案
  • 是一個開放源代碼全文檢索引擎工具包
  • 不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)
  • 當前以及最近幾年最受歡迎的免費Java資訊檢索程式庫

Lucene和ElasticSearch的關系:

  • ElasticSearch是基于Lucene 做了一下封裝和增強

1.2 ElasticSearch 概述

官網:https://www.elastic.co/cn/downloads/elasticsearch

Elaticsearch,簡稱為es,es是一個開源的高擴展分布式全文檢索引擎,它可以近乎實時的存盤檢索資料;本身擴展性很好,可以擴展到上百臺服務器,處理PB級別(大資料時代)的資料,es也使用java開發并使用Lucene作為其核心來實作所有索引和搜索的功能,但是它的目的通過簡單的RESTful API來隱藏Lucene的復雜性,從而讓全文搜索變得簡單

據國際權威的資料庫產品評測機構DB Engines的統計,在2016年1月,ElasticSearch已超過Solr等,成為排名第一的搜索引擎類應用

ELK技術: elasticsearch+logstash+kibana

1.3 ES和Solr

1.3.1 ElasticSearch簡介

  • Elasticsearch是一個實時分布式搜索和分析引擎, 它讓你以前所未有的速度處理大資料成為可能,
  • 它用于全文搜索、結構化搜索、分析以及將這三者混合使用:
  • 維基百科使用Elasticsearch提供全文搜索高亮關鍵字,以及輸入實時搜索(search-asyou-type)和搜索糾錯(did-you-mean)等搜索建議功能,
  • 英國衛報使用Elasticsearch結合用戶日志和社交網路資料提供給他們的編輯以實時的反饋,以便及時了解公眾對新發表的文章的回應,
  • StackOverflow結合全文搜索與地理位置查詢,以及more-like-this功能來找到相關的問題和答案,
  • Github使用Elasticsearch檢索1300億行的代碼,
  • 但是Elasticsearch不僅用于大型企業,它還讓像DataDog以及Klout這樣的創業公司將最初的想法變成可擴展的解決方案,
  • Elasticsearch可以在你的筆記本上運行,也可以在數以百計的服務器上處理PB級別的資料,
  • Elasticsearch是一個基于Apache Lucene(TM)的開源搜索引擎,無論在開源還是專有領域, Lucene可被認為是迄今為止最先進、性能最好的、功能最全的搜索引擎庫,
  • 但是, Lucene只是一個庫, 想要使用它,你必須使用Java來作為開發語言并將其直接集成到你的應用中,更糟糕的是, Lucene非常復雜,你需要深入了解檢索的相關知識來理解它是如何作業的,
  • Elasticsearch也使用Java開發并使用Lucene作為其核心來實作所有索引和搜索的功能,但是它的目的通過簡單的RESTful API來隱藏Lucene的復雜性,從而讓全文搜索變得簡單,

1.3.2 Solr簡介

  • Solr是Apache下的一個頂級開源專案,采用Java開發,它是基于Lucene的全文搜索服務器,Solr提供了比Lucene更為豐富的查詢語言,同時實作了可配置可擴展,并對索引、搜索性能進行了優化
  • Solr可以獨立運行,運行在letty. Tomcat等這些Selrvlet容器中 , Solr 索引的實作方法很簡單,用POST方法向Solr服務器發送一個描述Field及其內容的XML檔案, Solr根據xml檔案添加、洗掉、更新索引,Solr 搜索只需要發送HTTP GET請求,然后對Solr回傳xml、json等格式的查詢結果進行決議,組織頁面布局,
  • Solr不提供構建UI的功能, Solr提供了一個管理界面,通過管理界面可以查詢Solr的配置和運行情況,
  • Solr是基于lucene開發企業級搜索服務器,實際上就是封裝了lucene.
  • Solr是一個獨立的企業級搜索應用服務器,它對外提供類似于Web-service的API介面,用戶可以通過http請求,向搜索引擎服務器提交-定格式的檔案,生成索引;也可以通過提出查找請求,并得到回傳結果,

1.3.3 ElasticSearch與Solr比較

  1. 當單純的對已有資料進行搜索時,Solr更快

2.當實時建立索引時,Solr會產生io阻塞,查詢性能較差,ElasticSearch具有明顯的優勢

3.隨著資料量的增加,Solr的搜索效率會變得更低,而ElasticSearch卻沒有明顯的變化

4.轉變我們的搜索基礎設施后從Solr ElasticSearch,我們看見一個即時~ 50x提高搜索性能!

1.3.4 總結

1、es基本是開箱即用(解壓就可以用!) ,非常簡單,Solr安裝略微復雜一丟丟!
2、Solr 利用Zookeeper進行分布式管理,而Elasticsearch自身帶有分布式協調管理功能
3、Solr 支持更多格式的資料,比如JSON、XML、 CSV ,而Elasticsearch僅支持json檔案格式
4、Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高級功能多有第三方插件提供,例如圖形化界面需要kibana友好支撐
5、Solr 查詢快,但更新索引時慢(即插入洗掉慢) ,用于電商等查詢多的應用;

  • ES建立索引快(即查詢慢) ,即實時性查詢快,用于facebook新浪等搜索,
  • Solr是傳統搜索應用的有力解決方案,但Elasticsearch更適用于新興的實時搜索應用,

6、Solr比較成熟,有一個更大,更成熟的用戶、開發和貢獻者社區,而Elasticsearch相對開發維護者較少,更新太快,學習使用成本較高,

2、ElasticSearch安裝

JDK8,最低要求;

使用Java開發,必須保證ElasticSearch的版本與Java的核心jar包版本對應!(Java環境保證沒錯)

2.1 ElasticSearch

2.1.1 下載

ElasticSearch下載地址:https://mirrors.huaweicloud.com/elasticsearch/7.6.1

下載完解壓即可;

2.1.2 熟悉目錄

bin 啟動檔案目錄
config 組態檔目錄
    1og4j2 日志組態檔
    jvm.options java 虛擬機相關的配置(默認啟動占1g記憶體,內容不夠需要自己調整)
    elasticsearch.ym1 elasticsearch 的組態檔! 默認9200埠!跨域!
1ib 
    相關jar包
modules 功能模塊目錄
plugins 插件目錄
    ik分詞器

2.1.3 啟動

1.點擊:elasticsearch.bat

2.訪問地址:127.0.0.1:9200

3.訪問測驗成功

2.2 安裝elasticsearch-head可視化界面

elasticsearch-head

使用前提:需要安裝node.js

2.2.1 下載

elasticsearch-head下載地址:https://github.com/mobz/elasticsearch-head/archive/master.zip

下載完解壓即可;

2.2.2安裝依賴與啟動

cd elasticsearch-head
# 安裝依賴
npm install
# 啟動
npm run start
# 訪問
http://localhost:9100/

訪問地址:http://loacalhost:9100

使用羅技滑鼠插件的可能會占用9100埠

關閉LGHUBUpdateService服務即可;或者修改elasticsearch-head的埠,反正兩個不用埠沖突就好;

2.2.3訪問

存在跨域問題

存在跨域問題(只有當兩個頁面同源,才能互動);

同源(埠,主機,協議三者都相同);

開啟跨域(在elasticsearch解壓目錄config下elasticsearch.yml中添加)

# 開啟跨域
http.cors.enabled: true
# 所有人訪問
http.cors.allow-origin: "*"

再次連接連接成功

如何理解上圖:

  • 如果你是初學者

    • 索引 可以看做 “資料庫”
    • 型別 可以看做 “表”
    • 檔案 可以看做 “庫中的資料(表中的行)”
  • 這個head,我們只是把它當做可視化資料展示工具,之后所有的查詢都在kibana中進行

    • 因為不支持json格式化,不方便

2.3安裝 kibana

Kibana是一個針對ElasticSearch的開源分析及可視化平臺,用來搜索、查看互動存盤在Elasticsearch索引中的資料,使用Kibana ,可以通過各種圖表進行高級資料分析及展示,Kibana讓海量資料更容易理解,它操作簡單,基于瀏覽器的用戶界面可以快速創建儀表板( dashboard )實時顯示Elasticsearch查詢動態,設置Kibana非常簡單,無需編碼或者額外的基礎架構,幾分鐘內就可以完成Kibana安裝并啟動Elasticsearch索引監測,

2.3.1 下載

kibana下載地址:https://mirrors.huaweicloud.com/kibana/7.6.1/

下載后解壓即可;

2.3.2 啟動

點擊kibana.bat;

訪問:http://localhost:5601

訪問成功

2.3.3 控制臺

2.3.4kibana 漢化

編輯器打開kibana解壓目錄/config/kibana.yml,添加 i18n.locale: "zh-CN"

zh-CH.json檔案

2.3.5 了解ELK

  • ELK是

    • Elasticsearch、Logstash、 Kibana三大開源框架首字母大寫簡稱

    • 市面上也被成為Elastic Stack,

    • 其中Elasticsearch是一個基于Lucene、分布式、通過Restful方式進行互動的近實時搜索平臺框架,
    • 像類似百度、谷歌這種大資料全文搜索引擎的場景都可以使用Elasticsearch作為底層支持框架,可見Elasticsearch提供的搜索能力確實強大,市面上很多時候我們簡稱Elasticsearch為es,
    • Logstash是ELK的中央資料流引擎,用于從不同目標(檔案/資料存盤/MQ )收集的不同格式資料,經過過濾后支持輸出到不同目的地(檔案/MQ/redis/elasticsearch/kafka等),
    • Kibana可以將elasticsearch的資料通過友好的頁面展示出來 ,提供實時分析的功能,
  • 市面上很多開發只要提到ELK能夠一致說出它是一個日志分析架構技術堆疊總稱 ,但實際上ELK不僅僅適用于日志分析,它還可以支持其它任何資料分析和收集的場景,日志分析和收集只是更具有代表性,并非唯一性,

收集清洗資料(Logstash) ==> 搜索、存盤(ElasticSearch) ==> 展示(Kibana)

img

3、ElasticSearch核心概念

3.1 概述

  1. 索引
    • 包含多個分片
  2. 欄位型別(mapping)
  3. 檔案(documents)
  4. 分片(Lucence索引,倒排索引)

集群,節點,索引,型別,檔案,分片,映射是什么?

3.2 關系行資料庫和ElasticSearch客觀對比

ElasticSearch是面向檔案,關系行資料庫和ElasticSearch客觀對比!一切都是JSON!

Relational DB ElasticSearch
資料庫(database) 索引(indices)
表(tables) types <慢慢會被棄用!>
行(rows) documents
欄位(columns) fields

elasticsearch(集群)中可以包含多個索引(資料庫) ,每個索引中可以包含多個型別(表) ,每個型別下又包含多個檔案(行) ,每個檔案中又包含多個欄位(列)

3.3 物理設計:

elasticsearch在后臺把每個索引劃分成多個分片,每分分片可以在集群中的不同服務器間遷移

一個人就是一個集群! ,即啟動的ElasticSearch服務,默認就是一個集群,且默認集群名為elasticsearch

3.4 邏輯設計:

一個索引型別中,包含多個檔案,比如說檔案1,檔案2,當我們索引一篇檔案時,可以通過這樣的順序找到它:索引 => 型別 => 檔案ID ,通過這個組合我們就能索引到某個具體的檔案, 注意:ID不必是整數,實際上它是個字串,

3.4.1 檔案(”行“)

之前說elasticsearch是面向檔案的,那么就意味著索引和搜索資料的最小單位是檔案,elasticsearch中,檔案有幾個重要屬性:

  • 自我包含,一篇檔案同時包含欄位和對應的值,也就是同時包含key:value !
  • 可以是層次型的,一個檔案中包含自檔案,復雜的邏輯物體就是這么來的! {就是一個json物件 ! fastjson進行自動轉換 !}
  • 靈活的結構,檔案不依賴預先定義的模式,我們知道關系型資料庫中,要提前定義欄位才能使用,在elasticsearch中,對于欄位是非常靈活的,有時候,我們可以忽略該欄位,或者動態的添加一個新的欄位,

盡管我們可以隨意的新增或者忽略某個欄位,但是,每個欄位的型別非常重要,比如一個年齡欄位型別,可以是字串也可以是整形,因為elasticsearch會保存欄位和型別之間的映射及其他的設定,這種映射具體到每個映射的每種型別,這也是為什么在elasticsearch中,型別有時候也稱為映射型別,

3.4.2 型別(“表”)

型別是檔案的邏輯容器就像關系型資料庫一樣,表格是行的容器,型別中對于欄位的定義稱為映射,比如name映射為字串型別,我們說檔案是無模式的,它們不需要擁有映射中所定義的所有欄位,比如新增一個欄位,那么elasticsearch是怎么做的呢?

  • elasticsearch會自動的將新欄位加入映射,但是這個欄位的不確定它是什么型別,elasticsearch就開始猜,如果這個值是18,那么elasticsearch會認為它是整形,但是elasticsearch也可能猜不對,所以最安全的方式就是提前定義好所需要的映射,這點跟關系型資料庫殊途同歸了,先定義好欄位,然后再使用,別整什么幺蛾子,

3.4.3 索引(“庫”)

引是映射型別的容器, elasticsearch中的索引是一個非常大的檔案集合, 索引存盤了映射型別的欄位和其他設定,然后它們被存盤到了各個分片上了,我們來研究下分片是如何作業的,

物理設計:節點和分片 如何作業

創建新索引

一個集群至少有一個節點,而一個節點就是一個elasricsearch行程,節點可以有多個索引默認的,如果你創建索引,那么索引將會有個5個分片(primary shard ,又稱主分片)構成的,每一個主分片會有一個副本(replica shard,又稱復制分片);

上圖是一個有3個節點的集群,可以看到主分片和對應的復制分片都不會在同一個節點內,這樣有利于某個節點掛掉了,資料也不至于丟失,實際上,一個分片是一個Lucene索引(一個ElasticSearch索引包含多個Lucene索引一個包含倒排索引的檔案目錄,倒排索引的結構使得elasticsearch在不掃描全部檔案的情況下,就能告訴你哪些檔案包含特定的關鍵字,不過,等等,倒排索引是什么鬼?

3.4.3.1 倒排索引(Lucene索引底層)

簡單說就是 按(文章關鍵字,對應的檔案<0個或多個>)形式建立索引,根據關鍵字就可直接查詢對應的檔案(含關鍵字的),無需查詢每一個檔案,如下圖

如果要搜索含有python標簽的文章,那相對于查找所有原資料而言,查找倒排索引后的資料將會快的多,只需要查看標簽這一欄,然后獲取相關的文章ID即可,完全過濾無關的所有資料,提高效率!

3.4.3.2 elasticsearch的索引和Lucene的索引對比

在elasticsearch中,索引(庫)這個詞被頻繁使用,這就是術語的使用,在elasticsearch中,索引被分為多個分片,每份 分片是 一個Lucence的索引,所以一個elasticsearch索引是 由多個Lucence索引組成的,別問問什么,因為elasticsearch的Lucence作為底層呢!,如無特指,說起索引都是指elasticsearch的索引,

4、IK分詞器(elasticsearch插件)

4.1 IK分詞器:中文分詞器

分詞:即把一段中文或者別的文字劃分成一個個的關鍵字,我們在搜索時候會把自己的資訊進行分詞,會把資料庫中或者索引庫中的資料進行分詞,然后進行一一個匹配操作,默認的中文分詞是將每個字看成一個詞不使用用IK分詞器的情況下),比如“我愛狂神”會被分為”我”,”愛”,”狂”,”神” ,這顯然是不符合要求的,所以我們需要安裝中文分詞器ik來解決這個問題,

IK提供了兩個分詞演算法: ik_smartik_max_word ,其中ik_smart最少切分, ik_max_word最細粒度劃分!

4.2 下載

elasticsearch-analysis-ik下載地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.1

下載后解壓到ElasticSearch的plugins目錄ik(自己添加一個名為ik的檔案夾)檔案夾下:

4.3 重啟ElasticSearch

加載了IK分詞器

4.4 elasticsearch-plugin list 命令 查看插件

4.5 使用kibana測驗

4.5.1 查看不同的分詞效果

ik_smart:最少切分

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "全世界"
}

ik_max_word:最細粒度劃分(窮盡詞庫的可能)

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "全世界"
}

4.5.2 添加自定義的詞添加到擴展字典中

從上面看,感覺分詞都比較正常,但是大多數,分詞都滿足不了我們的想法,如下例;

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "超喜歡狂神說Java"
}

發現問題:狂神說被拆開了;

這種自己需要的詞,需要自己加到我們的分詞器的字典中!

1.創建字典檔案

2.添加字典內容:kuang.dic

3.將自己的字典檔案配置到ik分詞器的組態檔中:

配置自己的擴展字典:

4.重啟,再次測驗

再次測驗一下狂神說,看下效果

以后的話,我們需要將自己配置 分詞就在自己定義的dic檔案中進行配置即可;

5、關于索引的基本操作

5.1Rest風格說明

一種軟體架構風格,而不是標準,只是提供了一組設計原則和約束條件,它主要用于客戶端和服務器互動類的軟體,基于這個風格設計的軟體可以更簡潔更有層次更易于實作快取等機制,

基本Rest命令說明:

method url地址 描述
PUT(創建,修改) localhost:9200/索引名稱/型別名稱/檔案id 創建檔案(指定檔案id)
POST(創建) localhost:9200/索引名稱/型別名稱 創建檔案(隨機檔案id)
POST(修改) localhost:9200/索引名稱/型別名稱/檔案id/_update 修改檔案
DELETE(洗掉) localhost:9200/索引名稱/型別名稱/檔案id 洗掉檔案
GET(查詢) localhost:9200/索引名稱/型別名稱/檔案id 查詢檔案通過檔案ID
POST(查詢) localhost:9200/索引名稱/型別名稱/檔案id/_search 查詢所有資料

5.2 增刪改查

5.2.1 創建索引

PUT /索引名/~型別名~/檔案id        #型別名后面棄用了
{
    請求體
}
PUT /test1/type1/1
{
  "name": "狂神說",
  "age": 3
}

執行命令

完成了自動增加索引,資料也添加了, 添加了檔案 (“行”)![image-20220725111741302]

5.2.2 欄位資料型別

  • 字串型別
    • text、keyword
      • text:支持分詞,全文檢索,支持模糊、精確查詢,不支持聚合,排序操作;text型別的最大支持的字符長度無限制,適合大欄位存盤;
      • keyword:不進行分詞,直接索引、支持模糊、支持精確匹配,支持聚合、排序操作,keyword型別的最大支持的長度為——32766個UTF-8型別的字符,可以通過設定ignore_above指定自持字符長度,超過給定長度后的資料將不被索引,無法通過term精確匹配檢索回傳結果,
  • 數值型
    • long、Integer、short、byte、double、float、half floatscaled float
  • 日期型別
    • date
  • te布爾型別
    • boolean
  • 二進制型別
    • binary
  • 等等…

5.2.3 指定欄位的型別

創建規則

# 創建規則
PUT /test2
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "birthday": {
        "type": "date"
      }
    }
  }
}

GET test2 查看規則資訊

5.3.4 查看默認資訊

直接插入資料,不創建規則,自動匹配資料型別

查看test索引的默認匹配資料型別

如果自己的檔案欄位沒有指定,那么es就會給我們默認匹配欄位型別;

5.3.5 擴展:get _cat/

通過get _cat/可以獲取ElasticSearch的當前的很多資訊!

GET _cat/indices
GET _cat/aliases
GET _cat/allocation
GET _cat/count
GET _cat/fielddata
GET _cat/health
GET _cat/indices
GET _cat/master
GET _cat/nodeattrs
GET _cat/nodes
GET _cat/pending_tasks
GET _cat/plugins
GET _cat/recovery
GET _cat/repositories
GET _cat/segments
GET _cat/shards
GET _cat/snapshots
GET _cat/tasks
GET _cat/templates
GET _cat/thread_pool

5.3.6 修改

5.3.6.1 使用put覆寫原來的值 (舊方法)

注意:覆寫修改,會資料丟失,如果原來的資料有多個欄位,但是新資料只有一個欄位,則其他沒有新資料的欄位會沒有資料;(原理先洗掉后增加)

# 覆寫更新 修改后版本會增加
PUT /test3/_doc/1
{
  "name": "狂神說123",
  "age": 13,
  "birthday": "1997-01-05"
}

5.3.6.2 使用post的update跟新
  • 需要注意doc
  • 不會丟失欄位
# post 更新  不會資料丟失
POST /test3/_doc/1/_update
{
  "doc":{
    "name": "法外狂徒張三"
  }
}

5.3.7 洗掉索引

# 洗掉test1
DELETE test1

通過DELETE命令實作洗掉,根據你的請求判斷是洗掉索引還是洗掉檔案記錄;

使用RESTFUL風格是我們ES推薦大家使用的;

6、關于檔案的基本操作

添加資料

# 關于檔案的操作
# 基本操作
# 添加資料
PUT /kuangshen/user/1
{
  "name": "狂神說",
  "age": 23,
  "desc": "一頓操作猛如虎再看工資2500",
  "tags": ["技術宅","溫暖","指男"]
}

PUT /kuangshen/user/2
{
  "name": "張三",
  "age": 3,
  "desc": "法外狂徒",
  "tags": ["交友","旅游","渣男"]
}

PUT /kuangshen/user/3
{
  "name": "李四",
  "age": 30,
  "desc": "mpm,不知道怎么形容",
  "tags": ["靚女","旅游","唱歌"]
}

PUT /kuangshen/user/4
{
  "name": "狂神說前端",
  "age": 3,
  "desc": "一頓操作猛如虎再看工資2500",
  "tags": ["技術宅","溫暖","指男"]
}

6.1 條件查詢(簡單查詢)

簡單的條件查詢,可以根據默認的映射規則,產生的查詢!

# 簡單的搜索
GET kuangshen/user/1

# 簡單的條件查詢
GET kuangshen/user/_search?q=name:狂神說

GET kuangshen/user/_search?q=name:狂神說Java

6.2 復雜查詢

GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  }
}

6.2.1 _score 欄位過濾

GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  },
  "_source": ["name","desc"]
}

6.2.2 sort排序

desc 降序 asc 升序

# sort排序 desc 降序 asc 升序
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  },
 "sort": [
   {
     "age": {
       "order": "desc"
     }
   }
 ]
}

6.2.3 form size 分頁查詢

# 分頁查詢 from:從第一個資料開始 size:回傳多少條資料
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  },
 "sort": [
   {
     "age": {
       "order": "desc"
     }
   }
 ],
 "from": 0,
 "size": 2
}
# 資料下標從0開始

6.2.4 布林值查詢

6.2.4.1 match 匹配查詢
# 多個條件使用空格隔開 (類似 in)
# 只要滿足其中一個結果舊可以被查出來
# 這個時候可以通過分值基本的判斷
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "tags": "男 技術"
    }
  }
}

6.2.4.2 must 等價 and
# 多條件查詢
# must 表示and,所有的條件都要符合 類似where id  = 1 and name = xxx
GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
          "name": "狂神說"
          }
          
        },
        {
          "match": {
            "age": "23"
          }
        }
      ]
    }
  }
}

6.2.4.3 should 等價or
# should 表示or
GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
          "name": "狂神說"
          }
          
        },
        {
          "match": {
            "age": "23"
          }
        }
      ]
    }
  }
}

6.2.4.4 must_not 等價 not
# must_not   等價于not 
GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
          "name": "狂神說"
          }
          
        },
        {
          "match": {
            "age": "23"
          }
        }
      ]
    }
  }
}

6.2.4.5 filter 對資料進行過濾
# 過濾器 filter 對資料進行過濾
GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
          "name": "狂神說"
          }
          
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 10
          }
        }
      }
    }
  }
}
# gt 大于
# gte 大于等于
# lt 小于
# lte 小于等于

6.2.4.6 匹配多個條件
# 匹配多個條件
# 多個條件使用空格隔開
# 只要滿足其中一個結果就可以被查出
# 這個時候可以通過分值基本的判斷
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "tags": "女 技術"
    }
  }
}

6.2.4.7 term 精確查詢

term查詢是直接通過倒排索引指定的詞條進行精確的查找;

關于分詞

  • trem:直接查詢精確的
  • match:會使用分詞器決議!(先分析檔案,然后通過分析的檔案進行查詢;)
  • 兩個型別 text keywork
# 測驗
# 創建索引
PUT testdb
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "desc": {
        "type": "keyword"
      }
    }
  }
}

# 插入資料
PUT testdb/_doc/1
{
  "name": "狂神說 Java name",
  "desc": "狂神說 Java desc"
}

PUT testdb/_doc/2
{
  "name": "狂神說 Java name2",
  "desc": "狂神說 Java desc2"
}
6.2.4.6.1 "analyzer": "keyword" 模式
# keywork 模式 一個整體 不分詞 沒有被分析
GET _analyze
{
  "analyzer": "keyword",
  "text": "狂神說 Java name"
}

6.2.4.6.2 "analyzer": "standard"模式
# standard 模式 會進行分詞
GET _analyze
{
  "analyzer": "standard",
  "text": "狂神說 Java name"
}

6.2.4.6.3 text 和 keywork 型別區別
# name的型別是text會被分詞決議器決議,所以可以通過部分詞搜索到
GET testdb/_search
{
  "query": {
    "term": {
      "name": {
        "value": "狂"
      }
    }
  }
}

# keyword 型別的欄位不會被分詞決議,必須完全一樣才可以搜索到
GET testdb/_search
{
  "query": {
    "term": {
      "desc": {
        "value": "狂神說 Java desc"
      }
    }
  }
}

6.2.4.6.4 多個值匹配精確查詢
# 精確查詢多個值
# 插入資料
PUT testdb/_doc/3
{
  "t1": "22",
  "t2": "2020-4-6"
}

PUT testdb/_doc/4
{
  "t1": "33",
  "t2": "2020-4-7"
}


GET testdb/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "t1": 22
          }
        },
        {
          "term": {
            "t1": 3
          }
        }
      ]
    }
  }
}

6.2.5 高亮查詢 highlight

6.2.5.1 高亮查詢
# 高亮查詢
# highlight 只當的欄位會被加上標簽
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神說"
    }
  },
  "highlight": {
    "fields": {
      "name":{}
    }
  }
}

6.2.5.2 自定義高亮標簽
# 自定義搜索高亮條件 ,自定義標簽
GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name": "狂神說"
    }
  },
  "highlight": {
    "pre_tags": "<p class='key' style='color:red'>", 
    "post_tags": "</p>", 
    "fields": {
      "name":{}
    }
  }
}

7、SpringBoot整合

7.1 官方API

Java REST Client->https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.6/index.html

7.2 基本配置

7.2.1 找到原生的依賴

API中原生依賴的位置

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.2</version>
</dependency>

7.2.2 找物件

AIP中物件位置

RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));

client.close();

7.2.3 分析這個類中的方法

配置基本的專案

注意幾個配置

JDK版本:

Javac編譯版本

JavaScript版本

7.3 搭建環境

7.3.1 依賴

7.3.1.1 確保elasticsearch的版本和我們本地版本一致
1 查看版本是否一致:

2 自定義我們需要的版本7.6.1:
<properties>
    <java.version>1.8</java.version>
    <!--   自定義 es依賴,保證和本地版本一致    -->
    <elasticsearch.version>7.6.1</elasticsearch.version>
</properties>

7.3.2注入物件

自定義配置類

@Configuration
public class ElasticSearchClientConfig {

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));
        return  client;
    }

}

原始碼中提供的物件(可以直接拿來用)

雖然這里匯入了3個類,靜態內部類,但是核心類就只有一個:

class RestClientConfigurations {
    RestClientConfigurations() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    static class RestClientFallbackConfiguration {
        RestClientFallbackConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean
        RestClient elasticsearchRestClient(RestClientBuilder builder) {
            return builder.build();
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({RestHighLevelClient.class})
    static class RestHighLevelClientConfiguration {
        RestHighLevelClientConfiguration() {
        }
		
        //RestHighLevelClient  高級客戶端,也是我們這里要講,后面專案會用到的客戶端
        @Bean
        @ConditionalOnMissingBean
        RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
            return new RestHighLevelClient(restClientBuilder);
        }

        //RestClient 普通客戶端
        @Bean
        @ConditionalOnMissingBean
        RestClient elasticsearchRestClient(RestClientBuilder builder, ObjectProvider<RestHighLevelClient> restHighLevelClient) {
            RestHighLevelClient client = (RestHighLevelClient)restHighLevelClient.getIfUnique();
            return client != null ? client.getLowLevelClient() : builder.build();
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    static class RestClientBuilderConfiguration {
        RestClientBuilderConfiguration() {
        }

        //RestClientBuilder
        @Bean
        @ConditionalOnMissingBean
        RestClientBuilder elasticsearchRestClientBuilder(RestClientProperties properties, ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
            HttpHost[] hosts = (HttpHost[])properties.getUris().stream().map(HttpHost::create).toArray((x$0) -> {
                return new HttpHost[x$0];
            });
            RestClientBuilder builder = RestClient.builder(hosts);
            PropertyMapper map = PropertyMapper.get();
            map.from(properties::getUsername).whenHasText().to((username) -> {
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(), properties.getPassword());
                credentialsProvider.setCredentials(AuthScope.ANY, credentials);
                builder.setHttpClientConfigCallback((httpClientBuilder) -> {
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
            });
            builder.setRequestConfigCallback((requestConfigBuilder) -> {
                properties.getClass();
                map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis).to(requestConfigBuilder::setConnectTimeout);
                properties.getClass();
                map.from(properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis).to(requestConfigBuilder::setSocketTimeout);
                return requestConfigBuilder;
            });
            builderCustomizers.orderedStream().forEach((customizer) -> {
                customizer.customize(builder);
            });
            return builder;
        }
    }
}

7.4 測驗API

7.4.1 創建索引

//測驗索引的創建 Request PUT kuang_index
@Test
void testCreateIndex() throws IOException {
    // 1.創建索引物件
    CreateIndexRequest request = new CreateIndexRequest("kuang_index");

    // 2.客戶端執行請求 IndicesClient
    CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);

    System.out.println(createIndexResponse);
}

7.4.2 判斷索引是否存在

//測驗獲取索引,只能判斷其是否存在
@Test
void testExistIndex() throws IOException {
    GetIndexRequest request = new GetIndexRequest("kuang_index");
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    System.out.println("kuang_index是否存在:"+exists);
}

7.4.3 洗掉索引

//測驗洗掉索引
@Test
void testDeleteIndex() throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest("kuang_index");
    //洗掉
    AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
    System.out.println("洗掉 kuang_index 是否成功:"+delete.isAcknowledged());
}

7.4.4 添加檔案

7.4.4.1 匯入依賴
<!--   fastjson     -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
</dependency>
7.4.4.2 創建物體類
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
    private String name;
    private int age;
}
7.4.4.3 添加檔案
//測驗添加檔案
@Test
void testAddDocument() throws IOException {
    //創建物件
    User user = new User("狂神", 3);
    //創建請求
    IndexRequest request = new IndexRequest("kuang_index");

    //規則 put /kuang_index/_doc/1
    request.id("1");
    request.timeout(TimeValue.timeValueSeconds(1));
    request.timeout("1s");

    //將我們的資料放入請求  json
    IndexRequest source = request.source(JSON.toJSON(user), XContentType.JSON);

    //客戶端發請求,獲取相應的結果
    IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

    System.out.println(indexResponse.toString());
    //IndexResponse[index=kuang_index,type=_doc,id=1,version=1,result=created,seqNo=0,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
    System.out.println(indexResponse.status()); //對應回傳的狀態 CREATED

}

7.4.5 判斷檔案是否存在

//判斷檔案是否存在
@Test
void testIsExists() throws IOException {
    GetRequest getRequest = new GetRequest("kuang_index", "1");
    //不獲取回傳值,_source 的背景關系
    getRequest.fetchSourceContext(new FetchSourceContext(false));
    getRequest.storedFields("_none_");

    boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);

    System.out.println("判斷1號檔案是否存在:" + exists);
    //判斷1號檔案是否存在:true
}

7.4.6 獲得檔案的資訊

//獲取檔案資訊
@Test
void testGetDocument() throws IOException {
    GetRequest getRequest = new GetRequest("kuang_index", "1");
    GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);

    System.out.println(getResponse.getSourceAsString()); //列印檔案內容
    //
}

7.4.7 更新檔案資訊

//更新檔案的資訊
@Test
void testUpdateDocument() throws IOException{
    UpdateRequest updateRequest = new UpdateRequest("kuang_index", "1");
    updateRequest.timeout("1s");

    User user = new User("狂神說Java", 18);
    updateRequest.doc(JSON.toJSONString(user),XContentType.JSON); //XContentType.JSON 傳入的資料型別

    UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);

    System.out.println("檔案是否跟新成功:"+updateResponse.status());
}

7.4.8 洗掉檔案

//洗掉檔案記錄
@Test
void testDeleteDocument() throws IOException{
    DeleteRequest deleteRequest = new DeleteRequest("kuang_index", "1");
    deleteRequest.timeout("1s");
    DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
    System.out.println("檔案是否洗掉成功:" + deleteResponse.status());
}

7.4.9 批量處理請求

//特殊的,真的專案一般都會批量插入資料
@Test
void testBulkRequest() throws IOException{
    BulkRequest bulkRequest = new BulkRequest();
    bulkRequest.timeout("1s");

    ArrayList<User> userList = new ArrayList<>();
    userList.add(new User("kuangshen1",3));
    userList.add(new User("kuangshen2",3));
    userList.add(new User("kuangshen3",3));
    userList.add(new User("kuangshen4",3));
    userList.add(new User("kuangshen5",3));
    userList.add(new User("kuangshen6",3));

    //批處理請求
    for (int i = 0; i < userList.size(); i++) {
         //批量更新和批量洗掉,就在這里修改對應的請求就可以了
        bulkRequest.add(
            new IndexRequest("kuang_index")
            .id(""+(i+1)) //不設定id就會默認生成隨機id
            .source(JSON.toJSONString(userList.get(i)),XContentType.JSON)
        );
    }

    BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);

    System.out.println("添加處理請求是否失敗:"+bulkResponse.hasFailures()); //false表示成功,true表示失敗
}

7.4.10 查詢

// 查詢
//SearchRequest 搜索請i去
//searchSourceBuilder 條件構造器
//HighlightBuilder 構建高亮
//TermQueryBuilder 精確查詢
//MatchAllQueryBuilder
// xxxQueryBuilder 對應我們之前的所有命令
@Test
void testSearch() throws IOException {
    SearchRequest searchRequest = new SearchRequest("kuang_index");
    //構建搜索條件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

    //查詢條件,我們可以使用QueryBuilders工具來實作
    //QueryBuilders.termQuery 精確查詢
    //QuertBuilders.matchAllQuery() 匹配所有
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "kuangshen1");
    sourceBuilder.query(termQueryBuilder);
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    searchRequest.source(sourceBuilder);

    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    System.out.println("查詢結果:"+JSON.toJSONString(searchResponse.getHits()));
    //查詢結果:{"fragment":true,"hits":[{"fields":{},"fragment":false,"highlightFields":{},"id":"1","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":1.540445,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"kuangshen1","age":3},"sourceAsString":"{\"age\":3,\"name\":\"kuangshen1\"}","sourceRef":{"fragment":true},"type":"_doc","version":-1}],"maxScore":1.540445,"totalHits":{"relation":"EQUAL_TO","value":1}}
    System.out.println("===================");
    for (SearchHit documentFields : searchResponse.getHits().getHits()) {
        System.out.println(documentFields.getSourceAsMap());
    }
    //{name=kuangshen1, age=3}
}

8、京東搜索

8.1 專案搭建

素材連接:https://pan.baidu.com/s/1M5uWdYsCZyzIAOcgcRkA_A#list/path=%2F

提取碼:qk8p

感謝這位老鐵->殺神TH

匯入素材啟動專案,訪問頁面

indexController.java

@Controller
public class IndexController {
    @GetMapping({"/","/index"})
    public String index(){
        return "index";
    }
}

8.2 爬取資料

資料問題?資料庫獲取,訊息佇列,訊息佇列中獲取,都可以成為資料源,爬蟲!

爬蟲資料:(獲取請求回傳的頁面資訊,篩選出我們想要的資料就可以了!)

pojo:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Content {
    private String title;
    private String img;
    private String price;

}

工具類:

@Component
public class HtmlParseUtil {
    public static void main(String[] args) throws Exception {
        new HtmlParseUtil().parseJD("java").forEach(System.out::println);

    }

    public ArrayList<Content> parseJD(String keyword) throws Exception{
        //獲取請求https://search.jd.com/Search?keyword=java
        //前提,需要聯網,ajax 不能獲取到
        String url = "https://search.jd.com/Search?keyword="+keyword;
        //決議網頁(Jsoup回傳Document就是瀏覽器Document物件)
        Document document = Jsoup.parse(new URL(url), 30000);
        //所有在js中可以使用的方法,這里都能使用
        Element element = document.getElementById("J_goodsList");
//        System.out.println(element.html());
        //獲取所有的li元素
        Elements elements = element.getElementsByTag("li");

        ArrayList<Content> goodsList = new ArrayList<>();

        //獲取元素中的內容,這里el,就是每個li標簽
        for(Element el : elements){
            //關于這種圖片特別多的網站,多有的圖片都是延遲加載的!
            //source-data-lazy-img
//            String img = el.getElementsByTag("img").eq(0).attr("src");
            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
            String price = el.getElementsByClass("p-price").eq(0).text();
            String title = el.getElementsByClass("p-name").eq(0).text();

//            System.out.println("=====================================");
//            System.out.println(img);
//            System.out.println(price);
//            System.out.println(title);
            Content content = new Content();
            content.setTitle(title);
            content.setImg(img);
            content.setPrice(price);
            goodsList.add(content);

        }

        return goodsList;
    }

}

8.3 業務撰寫

8.3.1 業務撰寫

ContentService

//業務撰寫
@Service
public class ContentService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //1.決議資料放入es索引
    public Boolean parseContent(String keywords) throws Exception {
        ArrayList<Content> contents = new HtmlParseUtil().parseJD(keywords);
        //把查詢到的資料放入es中
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");

        for (int i = 0; i < contents.size(); i++) {
            System.out.println(JSON.toJSONString(contents.get(i)));
            bulkRequest.add(
                    new IndexRequest("jd_goods")
//                    .id(""+(i+1))
                    .source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
        }

        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

        return  !bulkResponse.hasFailures();
    }

    //2.獲取這些資料實作搜索功能
    public List<Map<String,Object>> highlightSearch(String keyword,int pageNo,int pageSize) throws IOException {
        if(pageNo <= 1){
            pageNo = 1;
        }

        //條件搜索
        SearchRequest searchRequest = new SearchRequest("jd_goods");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //分頁
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);

        //精準匹配
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title"); //高亮的欄位
        highlightBuilder.requireFieldMatch(false); //多個高亮顯示
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);

        //執行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //決議結果
        ArrayList<Map<String,Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {

            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
             Map<String,Object> sourceAsMap = hit.getSourceAsMap(); //原來的結果

            //決議高亮的欄位
            if(title != null){
                Text[] fragments = title.fragments();
                String new_title = "";
                for (Text text : fragments) {
                    new_title += text;
                }
                sourceAsMap.put("title",new_title);//高亮欄位替換原來的內容即可
            }

            list.add(sourceAsMap);
        }

        return list;
    }

}

8.3.2 控制層

ContentController

//請求撰寫
@Controller
public class ContentController {

    @Autowired
    private ContentService contentService;

    @ResponseBody
    @GetMapping("/parse/{keyword}")
   public Boolean parse(@PathVariable("keyword") String keyword) throws Exception {
       return contentService.parseContent(keyword);
   }

    @ResponseBody
    @GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
    public List<Map<String,Object>> search(@PathVariable("keyword") String keyword,
                                           @PathVariable("pageNo") int pageNo,
                                           @PathVariable("pageSize") int pageSize) throws Exception {
        return contentService.highlightSearch(keyword,pageNo,pageSize);
    }

}

8.4 Index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="utf-8"/>
    <title>狂神說Java-ES仿京東實戰</title>
    <link rel="stylesheet" th:href="https://www.cnblogs.com/xiaoqigui/p/@{/css/style.css}"/>
    <script th:src="https://www.cnblogs.com/xiaoqigui/p/@{/js/jquery.min.js}"></script>
<!--    <script src="https://www.cnblogs.com/xiaoqigui/static/js/jquery.min.js"></script>-->
    <script th:src="https://www.cnblogs.com/xiaoqigui/p/@{/js/jquery.min.js}"></script>
</head>

<body >
<div  id="app">
    <div id="mallPage" >

        <!-- 頭部搜索 -->
        <div id="header" >
            <div >
                <div >
                    <!-- Logo-->
                    <h1 id="mallLogo">
                        <img th:src="https://www.cnblogs.com/xiaoqigui/p/@{/images/jdlogo.png}" alt="">
                    </h1>

                    <div >

                        <!--搜索-->
                        <div id="mallSearch" >
                            <form name="searchTop" >
                                <fieldset>
                                    <legend>天貓搜索</legend>
                                    <div >
                                        <div  id="s-combobox-685">
                                            <div >
                                                <input v-model="keyword" type="text" autocomplete="off" value="https://www.cnblogs.com/xiaoqigui/p/dd" id="mq"
                                                        aria-haspopup="true">
                                            </div>
                                        </div>
                                        <button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button>
                                    </div>
                                </fieldset>
                            </form>
                            <ul >
                                <li><a>狂神說Java</a></li>
                                <li><a>狂神說前端</a></li>
                                <li><a>狂神說Linux</a></li>
                                <li><a>狂神說大資料</a></li>
                                <li><a>狂神聊理財</a></li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 商品詳情頁面 -->
        <div id="content">
            <div >
                <!-- 品牌分類 -->
                <form >
                    <div  style="display:block">
                        <div >
                            <div >
                                <div >
                                    品牌
                                </div>
                                <div >
                                    <ul >
                                        <li><a href="https://www.cnblogs.com/xiaoqigui/p/#"> 狂神說 </a></li>
                                        <li><a href="https://www.cnblogs.com/xiaoqigui/p/#"> Java </a></li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                </form>

                <!-- 排序規則 -->
                <div >
                    <a >綜合<i ></i></a>
                    <a >人氣<i ></i></a>
                    <a >新品<i ></i></a>
                    <a >銷量<i ></i></a>
                    <a >價格<i ></i><i ></i></a>
                </div>

                <!-- 商品詳情 -->
                <div >

                    <div  v-for="result in results">
                        <div >
                            <!--商品封面-->
                            <div >
                                <a >
                                    <img :src="https://www.cnblogs.com/xiaoqigui/p/result.img">
                                </a>
                            </div>
                            <!--價格-->
                            <p >
                                <em>{{result.price}}</em>
                            </p>
                            <!--標題-->
                            <p >
                                <a v-html="result.title"> </a>
                            </p>
                            <!-- 店鋪名 -->
                            <div >
                                <span>店鋪: 狂神說Java </span>
                            </div>
                            <!-- 成交資訊 -->
                            <p >
                                <span>月成交<em>999筆</em></span>
                                <span>評價 <a>3</a></span>
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script th:src="https://www.cnblogs.com/xiaoqigui/p/@{/js/vue.min.js}"></script>
<script th:src="https://www.cnblogs.com/xiaoqigui/p/@{/js/axios.min.js}"></script>
<script>
    new Vue({
        el:"#app",
        data:{
            "keyword": '', // 搜索的關鍵字
            "results":[] // 后端回傳的結果
        },
        methods:{
            searchKey(){
                var keyword = this.keyword;
                console.log(keyword);
                //對接
                axios.get('search/'+keyword+"/1/5").then(response=>{
                    console.log(response);
                    this.results = response.data; //
                })
            }
        }
    });
</script>

</body>
</html>

8.5 測驗結果

筆記視頻來源->B站狂神說Java的ElasticSearch課程

筆記部分參考-> ElasticSearch7.6入門學習筆記

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

標籤:Java

上一篇:kafka~消費群組如何重新消費

下一篇:【面經】被虐了之后,我翻爛了equals原始碼,總結如下

標籤雲
其他(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