ElasticSearch,簡稱es,es是一個開源的高拓展的分佈式全文檢索引擎,它可以近乎實時的儲存、檢索數據;本身拓展性很好,它可以拓展到上百台伺服器,處理PB級別的數據。es也使用java開發並使用Lucene的複雜性,從而讓全文檢索變得簡單
據國際權威的數據庫產品評測機構DB Engines的統計,2016年1月,ElasticSearch已超過solr等成爲排名第一的搜尋引擎類應用
ElasticSearch是一個實時分佈式搜尋和分析引擎,它讓你以前所未有的速度處理大數據的可能
它用於全文搜尋,結構化搜尋,分析以及這三者混合使用
ElasticSearch是一個基於Apache Lucene™的開源搜尋引擎。無論是在開源還是專有鄰域,Lucene可以被認爲是迄今爲止最先進、效能最好的,功能最全的搜尋引擎庫。
但是。Lucene只是一個庫。想要使用它,你必須使用java作爲開發語言並將其直接整合到你的應用中,更糟糕的是,Lucene非常複雜,你需要更深入的瞭解檢索的相關知識來理解它是如何工作的。
ElasticSearch也使用java開發並使用Lucene作爲其核心來實現所有索引和功能,但是它的目的是通過簡單的RESTful API來隱藏Lucene的複雜性,從而讓全文檢索變得簡單
Solr是Apache下的頂級開源專案,採用java開發,它是基於Lucene的全文檢索伺服器。solr提供優化比Lucene跟爲豐富的查詢語言,同時實現了可設定、可拓展,並對索引、搜尋效能進行了優化
solr可以獨立執行,執行在jetty、tomcat等這些servlet容器中,Sole索引的實現方法很簡單,用post方法向solr伺服器發送一條可描述Filed及其內容的XML文件,Solr根據xml文件的新增、刪除、更新索引、Solr搜尋只需要發送HTTP GET請求,然後對solr返回xml、json等格式的查詢結果進行解析,組織頁面佈局、solr不提供構建UI的功能,solr提供了一個管理介面,通過管理介面可以對查詢的solr的設定和執行情況
Solr是一個開源搜尋平臺,用於構建搜尋應用程式。
是一個獨立的企業級搜尋應用伺服器,它對外提供類似於Web-service的API介面
它建立在Lucene(全文搜尋引擎)之上。 Solr是企業級的,快速的和高度可延伸的。
Lucene 是一個基於 Java 的全文資訊檢索工具包,它不是一個完整的搜尋應用程式,而是爲你的應用程式提供索引和搜尋功能的一個開源框架。Lucene 目前是 Apache Jakarta 家族中的一個開源專案。也是目前最爲流行的基於 Java 開源全文檢索工具包。
目前已經有很多應用程式的搜尋功能是基於 Lucene 的,比如 Eclipse 的幫助系統的搜尋功能。Lucene 能夠爲文字型別的數據建立索引,所以你只要能把你要索引的數據格式轉化的文字的,Lucene 就能對你的文件進行索引和搜尋。比如你要對一些 HTML 文件,PDF 文件進行索引的話你就首先需要把 HTML 文件和 PDF 文件轉化成文字格式的,然後將轉化後的內容交給 Lucene 進行索引,然後把建立好的索引檔案儲存到磁碟或者記憶體中,最後根據使用者輸入的查詢條件在索引檔案上進行查詢。不指定要索引的文件的格式也使 Lucene 能夠幾乎適用於所有的搜尋應用程式。
ElasticSearch vs solr 總結
宣告:JDK1.8,最低要求,ElasticSearch用戶端,介面工具
java開發,ElasticSearch的版本和我們之後對應的java的核心jar包!版本對應,JDK環境是正常
下載 https://www.elastic.co/cn/
華爲雲的映象去下載
ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
鏈接: https://pan.baidu.com/s/1xhpIqT8Tm-jN_h2ir9xlwA 提取碼: jv4h
bin 啓動檔案
config 組態檔
log4j2 日誌組態檔
jvm.options java虛擬機器相關設定
elasticsearch.yml elasticsearch的組態檔 預設9200埠! 跨域!
lib 相關jar包
modules 功能目錄
plugins 外掛
http.cors.enabled: true
http.cors.allow-origin: "*"
瞭解ELK
ELK是elasticSearch、Logstash、kibana三大開源框架首字母大寫的簡稱。市面上也被稱爲Elastic Stack。其中elasticSearch是一個基於Lucene、分佈式、通過RESTful方式進行互動的近實時搜尋平臺框架。像類似於百度、谷歌這種大數據全文搜尋引擎的場景都可以使用elasticSearch作爲底層支援框架、可見elasticSearch提供的搜尋能力確實強大,elasticSearch也被市面上簡稱爲es。
Logstash是ELK的中央數據引流引擎,用於從不同目標(檔案/數據儲存/MQ)收集的不同格式數據,經過過濾後支援輸出帶不同的目的地(檔案/MQ/redis/elasticSearch/kafka)等。kibana可以將elasticSearch的數據通過友好的頁面展示出來,提供實時分析的功能
下載地址:https://www.elastic.co/cn/downloads/kibana
注意:es版本和kibana版本儘量保持一致
下載完解壓
啓動測試:bin/kibana.bat
埠:5601
存取:http://localhost:5601
英文看不懂咋辦?使用漢化外掛
開啓/config/kibana.yml檔案 設定i18n.locale: 「zh-CN」
重新啓動kibana
1、索引
2、欄位型別(mapping)
3、文件(documents)
概述
elasticsearch是面向文件,關係型數據庫和elasticsearch客觀的對比
叢集,節點所以,型別,文件,分片,對映是什麼?
Relational DB | elasticsearch |
---|---|
數據庫(database) | 索引(indices) |
表(tables) | type |
行(rows) | documents |
欄位(colums) | fieds |
elasticsearch(叢集)中可以包含多個索引(數據庫),每個索引可以包含多個型別(表),每個型別可以包含多個文件(行),每個文件中有保安多個欄位(列)。
物理設計:
elasticsearch在後台吧每個索引劃分成多個分片,每分分片可以在叢集中的不同伺服器間遷移
邏輯設計:
一個索引型別中,包含多個文件,比如說文件1,文件2。當我們索引一篇文件時,可以通過這樣的一個順序找到它:索引>型別>文件id>,通過這個組合我們就能索引帶某個具體的文件。注意:ID不必是整數,實際上是一個字串
文件
之前說 elasticsearch是面向文件的,那麼就意味着索引和搜尋數據的最小單位是文件, elasticsearch中,文件有幾個重要屬性:
型別
型別是文件的邏輯容器,就像關係型數據庫一樣,表格是行的容器。型別中對於欄位的定義稱爲對映,比如name對映爲字串型別。我們說文件是無模式的,它們不需要擁有對映中所定義的所有欄位,比如新增一個欄位,那麼 elasticsearch是怎麼做的呢?
elasticsearch會自動的將新欄位加入對映,但是這個欄位的不確定它是什麼型別, elasticsearch就開始猜,如果這個值是18,那elasticsearch會認爲它是整形。但是 elasticsearch也可能猜不對,所以最安全的方式就是提前定義好所需要的對映,這點跟關係
型數據庫殊途同歸了,先定義好欄位,然後再使用,別整什麼蛾子。
索引
就是數據庫!
索引是對映型別的容器, elasticsearch中的索引是一個非常大的文件集合。索引儲存了對映型別的欄位和其他設定。然後它們被儲存到了各個分片上了。我們來研究下分片是如何工作的
物理設計:節點和分片如何工作
一個叢集至少有一個節點,而一個節點就是一個 elasricsearch進程,節點可以有多個索引預設的,如果你建立索引,那麼索引將會
有個5個分片( primary shard,又稱主分片)構成的,毎一個主分片會有一個副本( replica shard,又稱複製分片)
上圖是一個有3個節點的叢集,可以看到主分片和對應的複製分片都不會在同一個節點內,這樣有利於某個節點掛掉了,數據也不於丟失。
實際上,一個分片是一個 Lucene索引,一個包含倒排索引的檔案目錄,倒排素引的結構使得 elasticsearchi在不掃描全部文件的情況下,就能告訴你哪些文件包含特定的關鍵字。不過,等等,倒排索引是什麼鬼?
倒排索引
elasticsearch使用的是一種稱爲倒排索引的結構,採用 lucene倒排索作爲底層。這種結構適用於快速的全文搜尋,一個索引由文件中所有不重複的列表構成,對於每一個詞,都有一個包含它的文件列表。例如,現在有兩個文件,每個文件包含如下內容
Study every day , good good up to forever # 文件1包含的內容
To forever , Study every day, good good up # 文件2包含的內容
爲了建立倒排索引,我們首先要將每個文件拆分成獨立的詞或稱爲詞條或者 tokens),然後建立一個包含所有不重複的詞條的排序列表,然後列出每個詞條出現在哪個文件
term | doc_1 | doc_2 |
---|---|---|
Study | ✓ | ✕ |
To | ✕ | ✕ |
ever | ✓ | ✓ |
forever | ✓ | ✓ |
day | ✓ | ✓ |
study | ✕ | ✓ |
good | ✓ | ✓ |
every | ✓ | ✓ |
to | ✓ | ✕ |
up | ✓ | ✓ |
來看一個範例,比如我們通過部落格標籤來搜尋部落格文章。那麼倒排索引列表就是這樣的一個結構
如果要搜尋含有 python標籤的文章,那相對於査找所有原始數據而言,查詢倒排索引後的數據將會快的多。只需要檢視標籤這欄,然後獲取相關的文章ID即可。完全過濾掉無關的所有數據,提高效率!
elasticsearche的索引和 Lucene的索引對比
在 elasticsearcht中,索引這個詞被頻繁使用,這就是術語的使用。在 elasticsearch中,索引被分爲多個分片,每份分片是一個
Lucene的索引。所以一個 elasticsearch素引是由多個 Lucene索引組成的。別問爲什麼,誰讓 elasticsearch使用 Lucene作爲底層呢!
如無特指,說起索引都是指 elasticsearchi的索引
接下來的一切操作都在 kibana中 Dev Tools下的 Consale裡完成。
基礎操作
什麼是IK分詞器
分詞:即把一段中文或者別的劃分成一個個的關鍵字,我們在搜尋時候會把自己的資訊進行分詞,會把數據庫中或者索引庫中的數據進行分詞,然後進行一個匹配操作,預設的中文分詞是將每個字看成一個詞,比如「我愛jd」會被分爲我",「愛」,「j」,"d」,這顯
然是不符合要求的,所以我們需要安裝中文分詞器K來解決這個問題。
如果要使用中文,建議使用ik分詞器
IK提供了兩個分詞演算法: ik smart和 ik max word,其中 ik smart爲最少切分, ik max word爲最細粒度劃分!一會我們測試!
下載地址:https://github.com/medcl/elasticsearch-analysis-ik
下載完畢之後,放入到我們的 elasticsearch外掛即可!
通過命令檢視外掛是否載入:elasticsearch-plugin list
使用kibana測試
檢視不同分詞器的效果
其中 ik smart爲最少切分
ik_max_word爲細粒度拆分
自定義詞典
一種軟體架構風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於用戶端和伺服器互動類的軟體。基於這個風格設計的軟體可以更筒潔,更有層次,更易於實現快取等機制 機製。
基本Rest命令說明
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名稱/型別名稱/文件id | 建立文件(指定文件d) |
POST | localhost:9200/索引名稱/型別名稱 | 建立文件(隨機文件id) |
POST | locahost:9200/索引名稱/型別名稱/文件id/_ update | 修改文件 |
DELETE | localhost::9200/索引名稱/型別名稱/文件id | 刪除文件 |
GET | localhost:9200/索引名稱/型別名稱文件id | 查詢文件通過文件d |
POST | localhost::9200/索引名稱/型別名稱/_search | 查詢所有數據 |
執行es,kibana、es-head
PUT /索引名/~型別名~/文件id
{
請求體
}
完成了自動増加了索引!數據也成功的新增了
那麼name這個欄位用不用指定型別呢。畢亮我們關係型數據庫是需要指定型別的啊
預設型別:
指定欄位的型別
PUT /test2
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"birthday":{
"type": "date"
}
}
}
}
GET /索引名/~型別名~/文件id
如果自己的文件欄位沒有指定,那麼es就會給我們預設設定欄位型別
展:通過命令 elasticsearch索引情況!通過get_cat/可以獲得es的當前的很多資訊!
POST /索引名稱/型別名稱/文件名稱/_update
PUT /test3/_doc/1
{
"name":"joker",
"age":18,
"sex":"男"
}
POST /test3/_doc/1/_update
{
"doc":{
"name":"joker_dj"
}
}
通過 DELETE命令實現州除、根據你的請求來判斷是刪除索引還是刪除文件記錄!
DELETE /索引名稱/型別名稱/文件名稱
刪除指定文件
DELETE 索引名稱/型別名稱/文件id
{
方法體
}
例:
DELETE test3/_doc/1
{
"name":"joker_dj"
}
建立索引
PUT /joker/user/1
{
"name":"joker_dj",
"age":18,
"desc":"java小萌新",
"tags":["技術宅","直男"]
}
PUT /joker/user/1
{
"name":"joker_dj",
"age":18,
"desc":"java小萌新",
"tags":["技術宅","直男"]
}
PUT /joker/user/3
{
"name":"李四",
"age":5,
"desc":"合夥人",
"tags":["壞人"]
}
GET /索引名稱/型別名稱/文件id
重新put一邊就是修改 (不推薦)
version就是數據被改動的次數
POST _update,推薦使用這種更新方式
POST /索引名稱/型別名稱/文件id/_update
{
"doc":{
"fieds":"value"
}
}
POST /joker/user/3/_update
{
"doc":{
"name":"張三的哥哥李四"
}
}
簡單的搜尋
GET /索引名稱/[型別名稱]/[文件id]
高階搜尋 條件查詢
GET /索引名稱/[型別名稱]/_search?q=fieds:value
根據_score分數進行排序 檢視誰最匹配
只要文件中包含value的就會被查出來 前提是被分詞器捕獲
GET /索引名稱/型別名稱/_search
{
"query": {
"match": {
"fileds": "value"
}
}
}
GET /joker/user/_search
{
"query": {
"match": {
"name": "張三"
}
}
}
GET /索引名稱/型別名稱/_search
{
"query": {
"match": {
"fileds": "value"
}
},
"_source": ["fileds","fileds"]
}
GET /joker/user/_search
{
"query": {
"match": {
"name": "張三"
}
},
"_source": ["desc","name"]
}
GET /索引名稱/型別名稱/_search
{
"query": {
"match": {
"fileds": "value"
}
},
"_source": ["fileds","fileds"],
"sort": [
{
"fileds": {
"order": "desc"
}
}
]
}
例:
GET /joker/user/_search
{
"query": {
"match": {
"name": "張三"
}
},
"_source": ["desc","name","age"],
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
GET /joker/user/_search
{
"query": {
"match": {
"name": "張三"
}
},
"from": 0, # 起始值 page
"size": 20 # 查詢大小 pageSize
}
數據下標還是從0開始的,和學的所有數據結構是一樣的!
/seach/{current}/{pageSize}
多條件查詢
must 相當於 and
GET /joker/user/_search
{
"query": {
"bool": {
"must ": [
{
"match": {
"name": "張三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
should相當於 or
GET /joker/user/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "張三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
GET /joker/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "張三"
}
},
{
"match": {
"age": 5
}
}
]
}
}
}
GET /joker/user/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "張三"
}
}
],
"filter": {
"range": {
"age": {
"gt": 1,
}
}
}
}
}
}
篩選年齡大於1
多個條件之間使用空格隔開
只滿足其中一個即可查出
通過分值基本判斷
term查詢是直接通過倒排索引指定的詞條進程精確的查詢的!
關於分詞:
兩個型別 text 、 keyword
keyword不會被分詞器解析
text 會被分詞器解析
GET /joker/user/_search
{
"query": {
"match": {
"name":"張三"
}
},
"highlight": {
"fields": {
"name":{
}
}
}
}
自定義高亮標籤
GET /joker/user/_search
{
"query": {
"match": {
"name":"張三"
}
},
"highlight": {
"pre_tags":"<p class='key' style='color:red'>",
"post_tags":"</p>",
"fields": {
"name":{
}
}
}
}
https://www.elastic.co/guide/index.html
1、找到原生的依賴
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
2、初始化
用完後關閉用戶端
3、分析類中的方法
建立空的父工程
建立SpringBoot子模組
勾選相關依賴
下載相關依賴
注意版本問題:要和本地es版本一致,否則可能會出現問題
新建設定類
@Configuration
public class ElasticSearchclientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
return client;
}
}
@SpringBootTest
class ElasticseachApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//索引的建立 Request
@Test
void testCerateIndex() throws IOException {
//1. 建立索引請求
CreateIndexRequest request = new CreateIndexRequest("joker_index");
//2. 執行建立請求 indices.create
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
}
@Test
void testGetindex() throws IOException {
GetIndexRequest request = new GetIndexRequest("joker_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
//刪除索引
@Test
void testDeleteindex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("test2");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
新增fastjson依賴
方便json轉換
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
//新增文件
@Test
void testAddDocument() throws IOException {
User user = new User("joker_dj", 18);
//建立請求
IndexRequest request = new IndexRequest("joker_index");
// 規則 put joker_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
//將我們的數據放入請求 json
request.source(JSON.toJSONString(user), XContentType.JSON);
//用戶端發送請求
IndexResponse indexRespons = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexRespons.toString());
System.out.println(indexRespons.status());
}
//獲取文件 判斷文件是否存在
@Test
void testexitdocument() throws IOException {
GetRequest request = new GetRequest("joker_index", "1");
//不獲取返回的上下文 _source
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields("_none_");
boolean exists = client.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
//獲取文件的內容
@Test
void testGetDocument() throws IOException {
GetRequest request = new GetRequest("joker_index", "1");
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
System.out.println(documentFields.toString());//列印文件的內容
}
//更新文件資訊
@Test
void testUpdateDocument() throws IOException {
UpdateRequest request = new UpdateRequest("joker_index", "1");
request.timeout("1s");
User user = new User("joker_djs", 20);
request.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
System.out.println(update.status());
}
//刪除文件記錄
@Test
void testDeleteRequst() throws IOException {
DeleteRequest request = new DeleteRequest("joker_index", "1");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
// 批次插入數據
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> userList=new ArrayList<>();
userList.add(new User("joker1",18));
userList.add(new User("joker2",18));
userList.add(new User("joker3",18));
userList.add(new User("joker4",18));
userList.add(new User("joker5",18));
userList.add(new User("joker6",18));
userList.add(new User("joker7",18));
userList.add(new User("joker8",18));
for (int i = 0; i < userList.size(); i++) {
bulkRequest.add(new IndexRequest("joker_index")
.id(""+(i+1))
.source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures()); //false 成功
}
//查詢
//搜尋請求 SearchRequest
//條件構造 SearchSourceBuilder
//MatchAllQueryBuilder
//TermQueryBuilder 精確查詢
// xxx QueryBuilder
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("joker_index");
//構建搜尋條件
SearchSourceBuilder SourceBuilder = new SearchSourceBuilder();
//高亮
SourceBuilder.highlighter();
//查詢條件 我們可以使用QueryBuidler
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "joker1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
SourceBuilder.query(matchAllQueryBuilder);
SourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
//構建搜尋
searchRequest.source(SourceBuilder);
/*用戶端執行*/
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(search.getHits()));
System.out.println("==============================");
for (SearchHit hit : search.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
組態檔
引入靜態頁面
啓動存取
數據問題?數據庫獲取,訊息佇列中獲取中,都可以成爲數據源,爬蟲!
爬取數據:(獲取請求返回的頁面資訊,篩選出我們想要的數據就可以了!)
<!--解析網頁jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Content {
private String title;
private String img;
private String price;
}
public class HtmlParseUtil {
public static void main(String[] args) throws IOException {
new HtmlParseUtil().ParseJD(encode).forEach(System.out::println);
}
public List<Content> ParseJD(String keywords) throws IOException {
//獲取請求 :https://search.jd.com/Search?keyword=java
//需要聯網
String encode = URLEncoder.encode(keywords, "UTF-8");//url字元跳脫
String url="https://search.jd.com/Search?keyword="+ keywords;
//解析網頁 (Jsoup返回的Document物件就是JS的Document物件)
Document document = Jsoup.parse(new URL(url), 30000);
//所有js的方法都可以使用
Element element = document.getElementById("J_goodsList");
//獲取所有的li標籤
Elements elements = element.getElementsByTag("li");
ArrayList<Content> goods = new ArrayList<>();
for (Element el : elements) {
//關於圖片特別多的網站 所有圖片都是懶載入
String img = el.getElementsByTag("img").eq(0).attr("src");//圖片
String price = el.getElementsByClass("p-price").eq(0).text();//價格
String title = el.getElementsByClass("p-name").eq(0).text();//標題
Content content = new Content(title, img, price);
goods.add(content);
}
return goods;
}
}
ElasticSearchclientConfig
@Configuration
public class ElasticSearchclientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
return client;
}
}
//業務編寫
@Service
public class ContentService {
@Autowired
private RestHighLevelClient restHighLevelClient;
//解析數據放入es中
public boolean parseContent(String keywords) throws IOException {
List<Content> contents = new HtmlParseUtil().ParseJD(keywords);
//把查詢得到的數據放到es中
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for (int i = 0; i < contents.size(); i++) {
bulkRequest.add(new IndexRequest("jd_goods")
.source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !bulk.hasFailures();
}
}
@RestController
public class ContentController {
@Autowired
ContentService service;
@GetMapping("/parse/{keywords}")
public boolean parse(@PathVariable("keywords") String keywords) throws IOException {
return service.parseContent(keywords);
}
}
執行:localhost:9090/parse/java
Service層
//2. 獲取數據實現搜尋功能
public List<Map<String,Object>> searchPage(String keywords,int page,int pageSize) throws IOException {
if(page<=1){
page=1;
}
//條件搜尋
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分頁
sourceBuilder.from(page);
sourceBuilder.size(pageSize);
//模糊匹配
MatchPhraseQueryBuilder title = QueryBuilders.matchPhraseQuery("title", keywords);
//精確匹配
// TermQueryBuilder title = QueryBuilders.termQuery("title", keywords);
sourceBuilder.query(title);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//執行搜尋
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析結果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit hit : search.getHits().getHits()) {
list.add(hit.getSourceAsMap());
}
return list;
}
Controller
@GetMapping("/search/{keywords}/{page}/{pageSize}")
public List<Map<String,Object>> search(@PathVariable("keywords") String keywords,@PathVariable("page") int page,@PathVariable("pageSize") int pageSize) throws IOException {
return service.searchPage(keywords, page, pageSize);
}
存取測試
下載vue
npm install vue
npm install axios
引入工程中
編寫vue程式碼
<script>
new Vue({
el:"#app",
data:{
keywords:"",//搜尋的關鍵字
result:[]//搜尋的結果
},
methods:{
searchkey(){
var keyword=this.keywords;
axios.get("/search/"+keyword+"/1/10").then(res=>{
this.result=res.data;
}).catch(err=>{
})
}
}
})
</script>
數據渲染
//3. 獲取數據實現高亮搜尋功能
public List<Map<String,Object>> searchHighlightPage(String keywords,int page,int pageSize) throws IOException {
if(page<=1){
page=1;
}
//條件搜尋
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.requireFieldMatch(false);//是否多個欄位高亮;
highlightBuilder.field("title");//設定高亮的欄位
highlightBuilder.preTags("<span style='color:red'>");//設定字首
highlightBuilder.postTags("</span>");//設定後綴
sourceBuilder.highlighter(highlightBuilder);
//分頁
sourceBuilder.from(page);
sourceBuilder.size(pageSize);
//模糊匹配
MatchPhraseQueryBuilder titles = QueryBuilders.matchPhraseQuery("title", keywords);
//精確匹配
//TermQueryBuilder title = QueryBuilders.termQuery("title", keywords);
sourceBuilder.query(titles);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//執行搜尋
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//解析結果
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit hit : search.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 n_title="";
for (Text text : fragments) {
n_title+=text;
}
sourceAsMap.put("title",n_title);
}
list.add(sourceAsMap);
}
return list;
}
contrller參照
存取測試
推薦視訊 elasticsearch up主 遇見狂神說:
bilibili鏈接